mirror of
https://github.com/verilator/verilator.git
synced 2026-10-03 08:33:21 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3df9b4230b | ||
|
|
13163aef24 | ||
|
|
5c2b5bd782 | ||
|
|
da1ebcb4e4 | ||
|
|
8dbfc940ba | ||
|
|
e0654dc218 | ||
|
|
74a92e739f | ||
|
|
304a24d03a | ||
|
|
5ae1ce90ad | ||
|
|
d87b9d25ca | ||
|
|
9bc8d77f39 | ||
|
|
b7e4083e70 | ||
|
|
595419b370 | ||
|
|
442e4f35f0 | ||
|
|
e8b8b33ff6 | ||
|
|
e4d638c73d | ||
|
|
bccb9f794c | ||
|
|
fe0dae74c1 | ||
|
|
b59c23d346 | ||
|
|
6047049fc7 | ||
|
|
08e650a2b4 | ||
|
|
11d4d6562f | ||
|
|
5c9da65606 | ||
|
|
97d89cce35 | ||
|
|
cc45a3dd72 | ||
|
|
870918a788 | ||
|
|
41be66af95 | ||
|
|
9ad252be34 | ||
|
|
1f344ac99e | ||
|
|
8b738c0022 | ||
|
|
57fbf38fb2 | ||
|
|
4cd01bc99a | ||
|
|
fc0cf00b1d | ||
|
|
4333c201f1 | ||
|
|
1f5913a83c | ||
|
|
159c653b4b | ||
|
|
47a2e2aeb9 | ||
|
|
98b0d19363 | ||
|
|
24a0873f6d | ||
|
|
c5fee7c456 | ||
|
|
d11592cadd | ||
|
|
8ef9ac7dba | ||
|
|
a5aa0e2b0a | ||
|
|
f8ae08c0c2 | ||
|
|
72f5d39ace | ||
|
|
5c9801d42c | ||
|
|
c9ee425ed5 | ||
|
|
1871e2b541 | ||
|
|
3e39c55a97 | ||
|
|
e4d6fa0e3b | ||
|
|
9f8dbc91f2 | ||
|
|
0e37747d2c | ||
|
|
a8519a7a53 | ||
|
|
d4159811d2 | ||
|
|
b2ca4995ab | ||
|
|
7876fe94ad | ||
|
|
cc9f7d1ac3 | ||
|
|
94c295f26a | ||
|
|
8c51b5a980 | ||
|
|
4f98f84da9 | ||
|
|
63a429dd64 | ||
|
|
9c3331927b | ||
|
|
3a6edae59d | ||
|
|
75794e2eaa | ||
|
|
24efa6c19a | ||
|
|
7a8c5ecfe1 | ||
|
|
4684f1d4cc | ||
|
|
ef5c31b4c9 | ||
|
|
9d21c5441b | ||
|
|
24fe724e0b | ||
|
|
acf4a3fa99 | ||
|
|
3ed8d968ff | ||
|
|
45c09f3fae | ||
|
|
c93d28050e | ||
|
|
15abb4d590 | ||
|
|
ae98e87222 | ||
|
|
77f6b8cf5f | ||
|
|
573e79a329 | ||
|
|
aaac5d4685 | ||
|
|
b02e353ad1 | ||
|
|
80a269f49a | ||
|
|
75f28fd446 | ||
|
|
ec538c02d8 | ||
|
|
f7b93c8718 | ||
|
|
18ae08e14a | ||
|
|
bffe658026 | ||
|
|
789917cd06 | ||
|
|
8f838433df | ||
|
|
0b84222500 | ||
|
|
aad5c3ae75 | ||
|
|
b586e071fb | ||
|
|
c8e437c45c | ||
|
|
4d034d774d | ||
|
|
4627716aa4 | ||
|
|
847dbbbaf0 | ||
|
|
c011842edf | ||
|
|
aabb7394c3 | ||
|
|
d90064eaee | ||
|
|
ec8dbbffed | ||
|
|
0070520edb | ||
|
|
8e646bc55f | ||
|
|
e97dbf9537 | ||
|
|
0ef3baa87d | ||
|
|
3a777c2a37 | ||
|
|
237f4806bf | ||
|
|
8ec8c0ea76 | ||
|
|
dfe66a2b77 | ||
|
|
a18d6c8159 | ||
|
|
a0b09b6450 | ||
|
|
ca24357611 | ||
|
|
78c16081a4 | ||
|
|
e37dce9d85 | ||
|
|
43694ec87c | ||
|
|
22939d7473 | ||
|
|
9e3a88c41d | ||
|
|
cf4bf9b7a5 | ||
|
|
d065662afc | ||
|
|
70e2538436 | ||
|
|
338ebcd6f0 | ||
|
|
ea8b416e91 | ||
|
|
de81593e98 | ||
|
|
84562f98de | ||
|
|
5520759d9e | ||
|
|
9156a0e400 | ||
|
|
736bf75a50 | ||
|
|
81ef9b5dd2 | ||
|
|
2f18a52118 | ||
|
|
f3ed36840e | ||
|
|
129610821f | ||
|
|
4cd0310516 | ||
|
|
8b245138ee | ||
|
|
4f962bddd8 | ||
|
|
02738ec4ea | ||
|
|
e8a23cf8bc | ||
|
|
39ecfd9900 | ||
|
|
d6a7073a2b | ||
|
|
45eddfb335 | ||
|
|
d4b9b5aba6 | ||
|
|
b1bf742544 | ||
|
|
ebab30d864 | ||
|
|
cdd70ff8fc | ||
|
|
ad4c8ee955 | ||
|
|
2edfe7b8a8 | ||
|
|
3444dad9ce | ||
|
|
b8842f7e55 | ||
|
|
86d85412e1 | ||
|
|
2b56acb39a | ||
|
|
35a40b4930 | ||
|
|
c1f2b2cf93 | ||
|
|
011e9f3b0a | ||
|
|
dddc51b75c | ||
|
|
5187096bf9 | ||
|
|
ae33a1ceb1 | ||
|
|
d225e46e4a | ||
|
|
dcf946b437 | ||
|
|
2a5123f318 | ||
|
|
43d07235c9 | ||
|
|
1fad055286 | ||
|
|
e89cb1b9f7 | ||
|
|
4c7a397dc4 | ||
|
|
047a08c999 | ||
|
|
1d74657bbb | ||
|
|
7085fdbc27 | ||
|
|
4c9c39bd08 | ||
|
|
ec82243ee6 | ||
|
|
65bb93a6c5 | ||
|
|
6e7f28785e | ||
|
|
35654b5355 | ||
|
|
efb2801eeb | ||
|
|
9f5a4c9b22 | ||
|
|
7fc565a1bd | ||
|
|
0eb1d0a84e | ||
|
|
5d26bca55c | ||
|
|
ba69297c33 | ||
|
|
8b5dc0b678 | ||
|
|
5988bba9fb | ||
|
|
a15e6f68dd | ||
|
|
adfb903469 | ||
|
|
3e739db7fa | ||
|
|
c124fe6d0c | ||
|
|
95da5f90c7 | ||
|
|
e4a79d643d | ||
|
|
27c7d0c95b | ||
|
|
05a8a9ee28 | ||
|
|
b1ab48b9fc | ||
|
|
e4e3294363 | ||
|
|
9afa1f7fb9 | ||
|
|
c253b7769e | ||
|
|
f3c9b4fb03 | ||
|
|
26c31db75e | ||
|
|
3d49136e27 | ||
|
|
cd4e6b35b3 | ||
|
|
4abf5be9ce | ||
|
|
9ba6fc9279 | ||
|
|
05db8ce6c8 | ||
|
|
489f58011b | ||
|
|
1759b0826e | ||
|
|
5f3c9cca11 | ||
|
|
f02b99c709 | ||
|
|
542cf9b6e1 | ||
|
|
2f7002c5ec | ||
|
|
422c915c1d | ||
|
|
1f04d17e77 | ||
|
|
02f18fc21b | ||
|
|
5652867316 | ||
|
|
d08a91b71e | ||
|
|
2c30aecc5b | ||
|
|
3dccd89c18 | ||
|
|
aed3307214 | ||
|
|
ef3c7bb6a2 | ||
|
|
0a887c29f1 | ||
|
|
f0ba17a19b | ||
|
|
597d28b505 | ||
|
|
94e8cf1de9 | ||
|
|
33d6205e20 |
@@ -0,0 +1,9 @@
|
||||
Checks: 'clang-diagnostic-*,clang-analyzer-*,*,-modernize*,-hicpp*,-android-cloexec-fopen,-cert-dcl50-cpp,-cert-env33-c,-cert-err34-c,-cert-err58-cpp,-clang-analyzer-core.UndefinedBinaryOperatorResult,-clang-analyzer-security*,-cppcoreguidelines-no-malloc,-cppcoreguidelines-owning-memory,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-type-const-cast,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-type-static-cast-downcast,-cppcoreguidelines-pro-type-union-access,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-special-member-functions,-fuchsia-default-arguments,-fuchsia-overloaded-operator,-google-default-arguments,-google-readability-todo,-google-runtime-references,-llvm-header-guard,-llvm-include-order,-misc-string-integer-assignment,-misc-string-literal-with-embedded-nul,-readability-braces-around-statements,-readability-container-size-empty,-readability-delete-null-pointer,-readability-else-after-return,-readability-implicit-bool-conversion,-readability-named-parameter,-readability-static-accessed-through-instance'
|
||||
WarningsAsErrors: ''
|
||||
HeaderFilterRegex: ''
|
||||
FormatStyle: none
|
||||
CheckOptions:
|
||||
- key: google-readability-braces-around-statements.ShortStatementLines
|
||||
value: '2'
|
||||
- key: google-readability-namespace-comments.SpacesBeforeComments
|
||||
value: '2'
|
||||
@@ -1,6 +1,7 @@
|
||||
\#*
|
||||
.#*
|
||||
*~
|
||||
*.tidy
|
||||
*.old
|
||||
*.gz
|
||||
*.gz.uu
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
|
||||
@@ -2,6 +2,94 @@ Revision history for Verilator
|
||||
|
||||
The contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
|
||||
* Verilator 4.006 2018-10-27
|
||||
|
||||
** Add --pp-comments, msg2700. [Robert Henry]
|
||||
|
||||
** Add --dump-defines.
|
||||
|
||||
*** For --trace-fst, save enum decoding information, bug1358. [Sergi Granell]
|
||||
(To visualize enumeration data you must use GTKwave 3.3.95 or newer.)
|
||||
|
||||
*** For --trace-fst, instead of *.fst.hier, put data into *.fst. [Tony Bybell]
|
||||
|
||||
**** Fix --trace-lxt2 compile error on MinGW, msg2667. [HyungKi Jeong]
|
||||
|
||||
**** Fix Windows .exe not found, bug1361. [Patrick Stewart]
|
||||
|
||||
|
||||
* Verilator 4.004 2018-10-6
|
||||
|
||||
** Add GTKWave FST native tracing, bug1356. [Sergi Granell]
|
||||
(Verilator developers need to pull the latest vcddiff.)
|
||||
|
||||
*** Support $past. [Dan Gisselquist]
|
||||
|
||||
*** Support restrict, bug1350. [Clifford Wolf]
|
||||
|
||||
*** Rename include/lxt2 to include/gtkwave.
|
||||
|
||||
**** Fix replication of 64-bit signal change detects.
|
||||
|
||||
**** Fix Mac OSX 10.13.6 / LLVM 9.1 compile issues, bug1348. [Kevin Kiningham]
|
||||
|
||||
**** Fix MinGW compile issues, msg2636. [HyungKi Jeong]
|
||||
|
||||
|
||||
* Verilator 4.002 2018-09-16
|
||||
|
||||
** This is a major release. Any patches may require major rework to apply.
|
||||
[Thanks everyone]
|
||||
|
||||
** Add multithreaded model generation.
|
||||
|
||||
** Add runtime arguments.
|
||||
|
||||
** Add GTKWave LXT2 native tracing, bug1333. [Yu Sheng Lin]
|
||||
|
||||
** Note $random has new algorithm; results may vary vs. previous versions.
|
||||
|
||||
*** Better optimize large always block splitting, bug1244. [John Coiner]
|
||||
|
||||
*** Add new reloop optimization for repetitive assignment compression.
|
||||
|
||||
*** Support string.atoi and similar methods, bug1289. [Joel Holdsworth]
|
||||
|
||||
**** Fix internals to be C++ null-pointer-check clean.
|
||||
|
||||
**** Fix internals to avoid 'using namespace std'.
|
||||
|
||||
**** Fix Verilation performance issues, bug1316. [John Coiner]
|
||||
|
||||
**** Fix clocker attributes to not propagate on concats. [John Coiner]
|
||||
|
||||
**** Fix first clock edge and --x-initial-edge, bug1327. [Rupert Swarbrick]
|
||||
|
||||
**** Fix compile error on tracing of string arrays, bug1338. [Iztok Jeras]
|
||||
|
||||
**** Fix number parsing with newline after radix, bug1340. [George Cuan]
|
||||
|
||||
**** Fix string ?: conditional type resolution, bug1345. [Iztok Jeras]
|
||||
|
||||
**** Fix duplicate symbol error on generate tri, bug1347. [Tomas Dzetkulic]
|
||||
|
||||
|
||||
* Verilator 3.926 2018-08-22
|
||||
|
||||
**** Add OBJCACHE envvar support to examples and generated Makefiles.
|
||||
|
||||
**** Change MODDUP errors to warnings, msg2588. [Marshal Qiao]
|
||||
|
||||
**** Fix define argument stringification (`"), broke since 3.914. [Joe DErrico]
|
||||
|
||||
**** Fix to ignore Unicode UTF-8 BOM sequences, msg2576. [HyungKi Jeong]
|
||||
|
||||
**** Fix std:: build error, bug1322.
|
||||
|
||||
**** Fix function inlining inside certain while loops, bug1330. [Julien Margetts]
|
||||
|
||||
|
||||
* Verilator 3.924 2018-06-12
|
||||
|
||||
*** Renamed --profile-cfuncs to --prof-cfuncs.
|
||||
|
||||
+4
-4
@@ -1,5 +1,4 @@
|
||||
^CVS/
|
||||
/CVS/
|
||||
\.clang-tidy
|
||||
\.git/
|
||||
\.svn/
|
||||
\.(bak|old)/
|
||||
@@ -9,6 +8,7 @@
|
||||
.*\.log
|
||||
\..*\.swp
|
||||
.*\.tmp
|
||||
.*\.tidy
|
||||
.*\.tex
|
||||
.*\.key
|
||||
.*\.vcd
|
||||
@@ -31,8 +31,8 @@ include/verilated.mk$
|
||||
test_regress/.gdbinit$
|
||||
config.cache$
|
||||
config.status$
|
||||
verilator.log
|
||||
verilator.tex
|
||||
verilator\.log
|
||||
verilator\.tex
|
||||
verilator.pc$
|
||||
verilator_bin.*
|
||||
verilator_coverage_bin.*
|
||||
|
||||
+62
-36
@@ -56,6 +56,7 @@ INSTALL_DATA = @INSTALL_DATA@
|
||||
MAKEINFO = makeinfo
|
||||
POD2TEXT = pod2text
|
||||
POD2LATEXFIX = $(srcdir)/src/pod2latexfix
|
||||
MKINSTALLDIRS = $(SHELL) $(srcdir)/src/mkinstalldirs
|
||||
PERL = @PERL@
|
||||
|
||||
# Destination prefix for RPMs
|
||||
@@ -120,18 +121,20 @@ DISTFILES_INC = $(INFOS) .gitignore Artistic COPYING COPYING.LESSER \
|
||||
bin/verilator \
|
||||
bin/verilator_coverage \
|
||||
bin/verilator_difftree \
|
||||
bin/verilator_gantt \
|
||||
bin/verilator_includer \
|
||||
bin/verilator_profcfunc \
|
||||
doxygen-mainpage doxygen.config veripool-logo.png \
|
||||
install-sh configure mkinstalldirs *.pod \
|
||||
doxygen-mainpage doxygen.config verilator_logo.png \
|
||||
install-sh configure *.pod \
|
||||
include/*.[chv]* \
|
||||
include/*.in \
|
||||
include/.*ignore \
|
||||
include/gtkwave/*.[chv]* \
|
||||
include/vltstd/*.[chv]* \
|
||||
.*attributes */.*attributes */*/.*attributes \
|
||||
src/.*ignore src/*.in src/*.cpp src/*.[chly] \
|
||||
src/astgen src/bisonpre src/*fix src/cppcheck_filtered \
|
||||
src/vlcovgen \
|
||||
src/vlcovgen src/mkinstalldirs \
|
||||
src/.gdbinit \
|
||||
src/*.pl src/*.pod \
|
||||
examples/*/.*ignore examples/*/Makefile* \
|
||||
@@ -154,10 +157,12 @@ DISTFILES_INC = $(INFOS) .gitignore Artistic COPYING COPYING.LESSER \
|
||||
INST_PROJ_FILES = \
|
||||
bin/verilator \
|
||||
bin/verilator_coverage \
|
||||
bin/verilator_gantt \
|
||||
bin/verilator_includer \
|
||||
bin/verilator_profcfunc \
|
||||
include/verilated.mk \
|
||||
include/*.[chv]* \
|
||||
include/gtkwave/*.[chv]* \
|
||||
include/vltstd/*.[chv]* \
|
||||
|
||||
INST_PROJ_BIN_FILES = \
|
||||
@@ -183,8 +188,8 @@ all_nomsg: verilator_exe $(VL_INST_MAN_FILES)
|
||||
.PHONY:verilator_coverage_bin_dbg
|
||||
verilator_exe verilator_bin verilator_bin_dbg verilator_coverage_bin_dbg:
|
||||
@echo ------------------------------------------------------------
|
||||
@echo "making verilator in src" ; \
|
||||
(cd src && $(MAKE) $(OBJCACHE_JOBS) )
|
||||
@echo "making verilator in src"
|
||||
$(MAKE) -C src $(OBJCACHE_JOBS)
|
||||
|
||||
.PHONY:msg_test
|
||||
msg_test: all_nomsg
|
||||
@@ -210,7 +215,7 @@ smoke-test: all_nomsg
|
||||
test_regress/t/t_a_first_sc.pl
|
||||
|
||||
test_regress: all_nomsg
|
||||
@(cd test_regress && $(MAKE))
|
||||
$(MAKE) -C test_regress
|
||||
|
||||
examples: all_nomsg
|
||||
for p in examples/* ; do \
|
||||
@@ -230,8 +235,8 @@ verilator.html: ${srcdir}/bin/verilator
|
||||
pod2html $< >$@
|
||||
|
||||
# PDF needs DIST variables; but having configure.ac as dependency isn't detected
|
||||
verilator.pdf: ${srcdir}/bin/verilator Makefile
|
||||
pod2latex --full --out verilator.tex ${srcdir}/bin/verilator
|
||||
verilator.pdf: ${srcdir}/bin/verilator Makefile $(POD2LATEXFIX)
|
||||
pod2latex --full --out verilator.tex $<
|
||||
$(PERL) $(POD2LATEXFIX) "$(DISTTITLE)" "${DISTDATE}" < verilator.tex > verilator2.tex
|
||||
mv verilator2.tex verilator.tex
|
||||
pdflatex verilator.tex
|
||||
@@ -246,8 +251,8 @@ README.html: README.pod
|
||||
pod2html $< >$@
|
||||
|
||||
# PDF needs DIST variables; but having configure.ac as dependency isn't detected
|
||||
README.pdf: README.pod Makefile
|
||||
pod2latex --full --out README.tex README.pod
|
||||
README.pdf: README.pod Makefile $(POD2LATEXFIX)
|
||||
pod2latex --full --out README.tex $<
|
||||
$(PERL) $(POD2LATEXFIX) "$(DISTTITLE) README File" "${DISTDATE}" < README.tex > README2.tex
|
||||
mv README2.tex README.tex
|
||||
pdflatex README.tex
|
||||
@@ -262,8 +267,8 @@ internals.html: internals.pod
|
||||
pod2html $< >$@
|
||||
|
||||
# PDF needs DIST variables; but having configure.ac as dependency isn't detected
|
||||
internals.pdf: internals.pod Makefile
|
||||
pod2latex --full --out internals.tex internals.pod
|
||||
internals.pdf: internals.pod Makefile $(POD2LATEXFIX)
|
||||
pod2latex --full --out internals.tex $<
|
||||
$(PERL) $(POD2LATEXFIX) "$(DISTTITLE) Internals Manual" "${DISTDATE}" < internals.tex > internals2.tex
|
||||
mv internals2.tex internals.tex
|
||||
pdflatex internals.tex
|
||||
@@ -272,12 +277,12 @@ internals.pdf: internals.pod Makefile
|
||||
|
||||
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
|
||||
VL_INST_BIN_FILES = verilator verilator_bin verilator_bin_dbg verilator_coverage_bin_dbg \
|
||||
verilator_coverage verilator_includer verilator_profcfunc
|
||||
verilator_coverage verilator_gantt verilator_includer verilator_profcfunc
|
||||
# Some scripts go into both the search path and pkgdatadir,
|
||||
# so they can be found by the user, and under $VERILATOR_ROOT.
|
||||
|
||||
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
|
||||
VL_INST_MAN_FILES = verilator.1 verilator_coverage.1 verilator_profcfunc.1
|
||||
VL_INST_MAN_FILES = verilator.1 verilator_coverage.1 verilator_gantt.1 verilator_profcfunc.1
|
||||
|
||||
VL_INST_INC_BLDDIR_FILES = \
|
||||
include/verilated_config.h \
|
||||
@@ -286,33 +291,35 @@ VL_INST_INC_BLDDIR_FILES = \
|
||||
# Files under srcdir, instead of build time
|
||||
VL_INST_INC_SRCDIR_FILES = \
|
||||
include/*.[chv]* \
|
||||
include/gtkwave/*.[chv]* \
|
||||
include/vltstd/*.[chv]* \
|
||||
|
||||
VL_INST_DATA_SRCDIR_FILES = \
|
||||
examples/*/*.[chv]* examples/*/Makefile* \
|
||||
|
||||
installbin:
|
||||
$(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(bindir)
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(bindir)
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator $(DESTDIR)$(bindir)/verilator )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_coverage $(DESTDIR)$(bindir)/verilator_coverage )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_gantt $(DESTDIR)$(bindir)/verilator_gantt )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_profcfunc $(DESTDIR)$(bindir)/verilator_profcfunc )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_bin $(DESTDIR)$(bindir)/verilator_bin )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_bin_dbg $(DESTDIR)$(bindir)/verilator_bin_dbg )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_coverage_bin_dbg $(DESTDIR)$(bindir)/verilator_coverage_bin_dbg )
|
||||
$(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(pkgdatadir)/bin
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_bin $(DESTDIR)$(bindir)/verilator_bin )
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_bin_dbg $(DESTDIR)$(bindir)/verilator_bin_dbg )
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_coverage_bin_dbg $(DESTDIR)$(bindir)/verilator_coverage_bin_dbg )
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/bin
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_includer $(DESTDIR)$(pkgdatadir)/bin/verilator_includer )
|
||||
|
||||
# Man files can either be part of the original kit, or built in current directory
|
||||
# So important we use $< so VPATH is searched
|
||||
# So important we use $^ so VPATH is searched
|
||||
installman: $(VL_INST_MAN_FILES)
|
||||
$(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(mandir)/man1
|
||||
cd $(srcdir) \
|
||||
; for p in $(VL_INST_MAN_FILES) ; do \
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(mandir)/man1
|
||||
for p in $^ ; do \
|
||||
$(INSTALL_DATA) $$p $(DESTDIR)$(mandir)/man1/$$p; \
|
||||
done
|
||||
|
||||
installdata:
|
||||
$(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(pkgdatadir)/include/vltstd
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/include/gtkwave
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/include/vltstd
|
||||
for p in $(VL_INST_INC_BLDDIR_FILES) ; do \
|
||||
$(INSTALL_DATA) $$p $(DESTDIR)$(pkgdatadir)/$$p; \
|
||||
done
|
||||
@@ -320,15 +327,15 @@ installdata:
|
||||
; for p in $(VL_INST_INC_SRCDIR_FILES) ; do \
|
||||
$(INSTALL_DATA) $$p $(DESTDIR)$(pkgdatadir)/$$p; \
|
||||
done
|
||||
$(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(pkgdatadir)/examples/hello_world_c
|
||||
$(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(pkgdatadir)/examples/hello_world_sc
|
||||
$(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(pkgdatadir)/examples/tracing_c
|
||||
$(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(pkgdatadir)/examples/tracing_sc
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/hello_world_c
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/hello_world_sc
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/tracing_c
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/tracing_sc
|
||||
cd $(srcdir) \
|
||||
; for p in $(VL_INST_DATA_SRCDIR_FILES) ; do \
|
||||
$(INSTALL_DATA) $$p $(DESTDIR)$(pkgdatadir)/$$p; \
|
||||
done
|
||||
$(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(pkgconfigdir)
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgconfigdir)
|
||||
$(INSTALL_DATA) verilator.pc $(DESTDIR)$(pkgconfigdir)
|
||||
|
||||
# We don't trust rm -rf, so rmdir instead as it will fail if user put in other files
|
||||
@@ -341,6 +348,7 @@ uninstall:
|
||||
-cd $(DESTDIR)$(pkgdatadir) && rm -f $(VL_INST_DATA_SRCDIR_FILES)
|
||||
-rm $(DESTDIR)$(pkgconfigdir)/verilator.pc
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/bin
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/include/gtkwave
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/include/vltstd
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/include
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/examples/hello_world_c
|
||||
@@ -392,7 +400,7 @@ install-cadtools: dist
|
||||
strip bin/verilator_bin*
|
||||
strip bin/verilator_coverage_bin*
|
||||
$(MAKE) install-cadtools-quick
|
||||
$(SHELL) ${srcdir}/mkinstalldirs $(VERILATOR_CAD_DIR)/man/man1
|
||||
$(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/man/man1
|
||||
for p in $(VL_INST_MAN_FILES) ; do \
|
||||
$(INSTALL_DATA) $$p $(VERILATOR_CAD_DIR)/man/man1/$$p; \
|
||||
done
|
||||
@@ -405,8 +413,9 @@ ifeq ($(CFG_WITH_DEFENV),yes)
|
||||
false
|
||||
endif
|
||||
@echo "Install-cadtools-quick (no strip) to $(VERILATOR_CAD_DIR)"
|
||||
$(SHELL) ${srcdir}/mkinstalldirs $(VERILATOR_CAD_DIR)/include/vltstd
|
||||
$(SHELL) ${srcdir}/mkinstalldirs $(VERILATOR_CAD_DIR)/bin
|
||||
$(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/include/gtkwave
|
||||
$(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/include/vltstd
|
||||
$(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/bin
|
||||
for p in $(INST_PROJ_FILES) ; do \
|
||||
$(INST_PROJ_CVS) $$p $(VERILATOR_CAD_DIR)/$$p; \
|
||||
done
|
||||
@@ -418,11 +427,16 @@ endif
|
||||
endif
|
||||
|
||||
# Use --xml flag to see the cppcheck code to use for suppression
|
||||
CPPCHECK_CPP = $(wildcard \
|
||||
$(srcdir)/include/*.cpp \
|
||||
$(srcdir)/src/*.cpp )
|
||||
CPPCHECK_H = $(wildcard \
|
||||
$(srcdir)/include/*.h \
|
||||
$(srcdir)/src/*.h )
|
||||
CPPCHECK = src/cppcheck_filtered
|
||||
CPPCHECK_FLAGS = --enable=all --inline-suppr \
|
||||
--suppress=unusedScopedObject --suppress=cstyleCast --suppress=useInitializationList
|
||||
CPPCHECK_FLAGS += --xml
|
||||
CPPCHECK_CPP = $(wildcard $(srcdir)/include/*.cpp $(srcdir)/src/*.cpp)
|
||||
CPPCHECK_DEP = $(subst .cpp,.cppcheck,$(CPPCHECK_CPP))
|
||||
CPPCHECK_INC = -I$(srcdir)/include -I$(srcdir)/src/obj_dbg -I$(srcdir)/src
|
||||
|
||||
@@ -430,6 +444,17 @@ cppcheck: $(CPPCHECK_DEP)
|
||||
%.cppcheck: %.cpp
|
||||
$(CPPCHECK) $(CPPCHECK_FLAGS) -DVL_DEBUG=1 -DVL_CPPCHECK=1 $(CPPCHECK_INC) $<
|
||||
|
||||
CLANGTIDY = clang-tidy
|
||||
CLANGTIDY_FLAGS = -config=''
|
||||
CLANGTIDY_DEP = $(subst .h,.h.tidy,$(CPPCHECK_H)) \
|
||||
$(subst .cpp,.cpp.tidy,$(CPPCHECK_CPP))
|
||||
|
||||
clangtidy: $(CLANGTIDY_DEP)
|
||||
%.cpp.tidy: %.cpp
|
||||
$(CLANGTIDY) $(CLANGTIDY_FLAGS) $< -- -DVL_DEBUG=1 -DVL_CPPCHECK=1 $(CPPCHECK_INC) | 2>&1 tee $@
|
||||
%.h.tidy: %.h
|
||||
$(CLANGTIDY) $(CLANGTIDY_FLAGS) $< -- -DVL_DEBUG=1 -DVL_CPPCHECK=1 $(CPPCHECK_INC) | 2>&1 tee $@
|
||||
|
||||
analyzer-src:
|
||||
-rm -rf src/obj_dbg
|
||||
scan-build $(MAKE) -k verilator_coverage_bin_dbg verilator_bin_dbg
|
||||
@@ -471,13 +496,13 @@ endif
|
||||
|
||||
maintainer-clean::
|
||||
@echo "This command is intended for maintainers to use;"
|
||||
@echo "rebuilding the deleted files requires makeinfo."
|
||||
rm -f *.info* *.1 $(INFOS) configure
|
||||
@echo "rebuilding the deleted files requires autoconf."
|
||||
rm -f configure
|
||||
|
||||
clean mostlyclean distclean maintainer-clean maintainer-copy::
|
||||
for dir in $(SUBDIRS); do \
|
||||
echo making $@ in $$dir ; \
|
||||
(cd $$dir && $(MAKE) $@) ; \
|
||||
$(MAKE) -C $$dir $@ ; \
|
||||
done
|
||||
|
||||
clean mostlyclean distclean maintainer-clean::
|
||||
@@ -490,6 +515,7 @@ clean mostlyclean distclean maintainer-clean::
|
||||
rm -rf examples/*/obj_dir examples/*/logs
|
||||
|
||||
distclean maintainer-clean::
|
||||
rm -f *.info* *.1 $(INFOS)
|
||||
rm -f Makefile config.status config.cache config.log TAGS
|
||||
rm -f verilator_bin* verilator_coverage_bin*
|
||||
rm -f bin/verilator_bin* bin/verilator_coverage_bin*
|
||||
|
||||
+8
-1
@@ -78,7 +78,14 @@ instead.)
|
||||
|
||||
=item
|
||||
|
||||
You will need the C<flex> and C<bison> packages installed.
|
||||
To use Verilator you will need the C<perl>, C<make> (or C<gmake>), and
|
||||
C<g++> (or C<clang>) packages.
|
||||
|
||||
To use Verilator LXT2 or FST tracing you will need the C<gtkwave> and C<libgz> (on
|
||||
Ubuntu C<zlibc> C<zlib1g> C<zlib1g-dev>) packages installed.
|
||||
|
||||
To compile Verilator in addition to the above you need the C<flex>,
|
||||
C<bison> and C<texi2html> packages installed.
|
||||
|
||||
=item
|
||||
|
||||
|
||||
@@ -5,119 +5,115 @@
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
|
||||
* Language support:
|
||||
** Fix ordering of each bit separately in a signal (mips)
|
||||
assign b[3:0] = b[7:4]; assign b[7:4] = in;
|
||||
** Support UDP gate primitives/ cell libraries
|
||||
(have code for combos - problem is sequential udps)
|
||||
** Function to eval combo logic after /*verilator public*/ functions [gwaters]
|
||||
** Support generated clocks (correctness)
|
||||
** Recursive functions
|
||||
** Verilog configuration files
|
||||
** Expression coverage (see notes)
|
||||
** Better tristate support
|
||||
** UVM
|
||||
|
||||
Language support:
|
||||
* Fix ordering of each bit separately in a signal (mips)
|
||||
assign b[3:0] = b[7:4]; assign b[7:4] = in;
|
||||
* Support UDP gate primitives/ cell libraries
|
||||
(have code for combos - problem is sequential udps)
|
||||
* Function to eval combo logic after /*verilator public*/ functions [gwaters]
|
||||
* Support generated clocks (correctness)
|
||||
* Real numbers
|
||||
* Recursive functions
|
||||
* Verilog configuration files
|
||||
* Structs/unions (have starting point)
|
||||
* DPI to define C/C++ calls from Verilog
|
||||
* Expression coverage (see notes)
|
||||
* Better tristate support
|
||||
* Long-term Features
|
||||
** Assertions
|
||||
** Tristate support
|
||||
|
||||
Long-term Features
|
||||
* Assertions
|
||||
* Tristate support
|
||||
* Multithreaded execution
|
||||
* Configure/Make/Install
|
||||
** Distribute with flex/bison already expanded?
|
||||
Flex library not needed. Probably too difficult to be worth it.
|
||||
|
||||
Configure/Make/Install
|
||||
* Distribute with flex/bison already expanded?
|
||||
Flex library not needed. Probably too difficult to be worth it.
|
||||
* Testing:
|
||||
** Capture all inputs into global "rerun it" file
|
||||
** Code to make wrapper that sets signals, so can do comparison checks
|
||||
** New random program generator
|
||||
** Better graph viewer with search and zoom
|
||||
** Port and test against opencores.org code
|
||||
** // verilator debug in code so can see only tree affecting those nodes
|
||||
|
||||
Testing:
|
||||
* Capture all inputs into global "rerun it" file
|
||||
* Code to make wrapper that sets signals, so can do comparison checks
|
||||
* New random program generator
|
||||
* Better graph viewer with search and zoom
|
||||
* Port and test against opencores.org code
|
||||
* // verilator debug in code so can see only tree affecting those nodes
|
||||
* Usability:
|
||||
** Detect and pre-remove most UNOPTFLATs
|
||||
** Better reporting of unopt problems, including what lines of code
|
||||
** Report more errors (all of them?) before exiting [Eugene Weber]
|
||||
** Auto-create scons config files
|
||||
** Print version/etc message at runtime. (4.000?)
|
||||
Include number of lines of code, percent comments, code complexity measurement
|
||||
<-80chars------------------------------------------------------------------->
|
||||
Verilator 3.600 - The fast free open-sourced simulator. Copyright 2001-2013.
|
||||
Verilated #### modules, #### instances, ##### sigs,
|
||||
#### non-comment lines, ##### ops, ### KB model size
|
||||
|
||||
Usability:
|
||||
* Detect and pre-remove most UNOPTFLATs (4.000)
|
||||
* Better reporting of unopt problems, including what lines of code
|
||||
* Report more errors (all of them?) before exiting [Eugene Weber]
|
||||
* Auto-create scons config files
|
||||
* Print version/etc message at runtime. (4.000?)
|
||||
Include number of lines of code, percent comments, code complexity measurement
|
||||
<-80chars------------------------------------------------------------------->
|
||||
Verilator 3.600 - The fast free open-sourced simulator. Copyright 2001-2013.
|
||||
Verilated #### modules, #### instances, ##### sigs,
|
||||
#### non-comment lines, ##### ops, ### KB model size
|
||||
* Lint:
|
||||
** CDCRSTLOGIC should allow filtering with paths
|
||||
"waive CDCRSTLOGIC --from a.b.sig --to a.c.sig --via OR"
|
||||
|
||||
Lint:
|
||||
* CDCRSTLOGIC should allow filtering with paths
|
||||
"waive CDCRSTLOGIC --from a.b.sig --to a.c.sig --via OR"
|
||||
* Internal Code:
|
||||
** A Visitor class that understands how to traverse data types
|
||||
** V3Graph should be templated container type, taking in Vertex + Edge types
|
||||
** Instead of string, have an VEncodedString/VIdString which contains __DOT__ish
|
||||
things, to reduce bugs. Also add _20 trailing space to \ encoded names.
|
||||
|
||||
Internal Code:
|
||||
* A Visitor class that understands how to traverse data types
|
||||
* V3Graph should be templated container type, taking in Vertex + Edge types
|
||||
* Instead of string, have an VEncodedString/VIdString which contains __DOT__ish
|
||||
things, to reduce bugs. Also add _20 trailing space to \ encoded names. (4.000)
|
||||
* Runtime:
|
||||
** New evalulation loop ~/src/verilator/notes/event_loop.txt (4.000?)
|
||||
** Remove all private internal functions from top level wrapper header, move
|
||||
to new level
|
||||
** Completely standalone simulation
|
||||
main() records arguments for $test$plusvars
|
||||
instantiates top,
|
||||
does tracing (support $dump?)
|
||||
calls top->simulateForever()
|
||||
exits
|
||||
|
||||
Runtime:
|
||||
* New evalulation loop ~/src/verilator/notes/event_loop.txt (4.000?)
|
||||
* Remove all private internal functions from top level wrapper header, move
|
||||
to new level (4.000?)
|
||||
* Completely standalone simulation (4.000)
|
||||
main() records arguments for $test$plusvars
|
||||
instantiates top,
|
||||
does tracing (support $dump?)
|
||||
calls top->simulateForever()
|
||||
exits
|
||||
|
||||
Performance:
|
||||
* Latch optimizations
|
||||
* Constant propagation
|
||||
Extra cleaning AND: 1 & ((VARREF >> 1) | ((&VARREF >> 1) & VARREF))
|
||||
Extra shift (perhaps due to clean): if (1 & CAST (VARREF >> #))
|
||||
* Gated clock and latch conversion to flops. [JeanPaul Vanitegem]
|
||||
Could propagate the AND into pos/negedges and let domaining optimize.
|
||||
* Negedge reset
|
||||
Switch to remove negedges that don't matter
|
||||
Can't remove async resets from control flops (like in syncronizers)
|
||||
* If all references to array have a constant index, blow up into separate signals-per-index
|
||||
* Bit-multiply for faster bit swapping and a=b[1,3,2] random bit reorderings.
|
||||
* Move _last sets and all other combo logic inside master
|
||||
if() that triggers on all possible sense items
|
||||
* Rewrite and combine V3Life, V3Subst
|
||||
If block temp only ever set in one place to constant, propagate it
|
||||
Used in t_mem for array delayed assignments
|
||||
Replace variables if set later in same cfunc branch
|
||||
See for example duplicate sets of _narrow in cycle 90/91 of t_select_plusloop
|
||||
* Same assignment on both if branches
|
||||
"if (a) { ... b=2; } else { ... b=2;}" -> "b=2; if ..."
|
||||
Careful though, as b could appear in the statement or multiple times in statement
|
||||
(Could just require exatly two 'b's in statement)
|
||||
* Simplify XOR/XNOR/AND/OR bit selection trees
|
||||
Foo = A[1] ^ A[2] ^ A[3] etc are better as ^ ( A & 32'b...1110 )
|
||||
* Combine variables into wider elements
|
||||
Parallel statements on different bits should become single signal
|
||||
Variables that are always consumed in "parallel" can be joined
|
||||
* Duplicate assignments in gate optimization
|
||||
Common to have many separate posedge blocks, each with identical
|
||||
reset_r <= rst_in
|
||||
* If signal is used only once (not counting trace), always gate substitute
|
||||
Don't merge if any combining would form circ logic (out goes back to in)
|
||||
* Multiple assignments each bit can become single assign with concat
|
||||
Make sure a SEL of a CONCAT can get the single bit back.
|
||||
* Usually blocks/values
|
||||
Enable only after certain time, so VL_TIME_I(32) > 0x1e gets eliminated out
|
||||
* Better ordering of a<=b, b<=c, put all refs to 'b' next to each other to optimize caching
|
||||
* I-cache packing improvements (what/how?)
|
||||
* Data cache organization (order of vars in class)
|
||||
First have clocks,
|
||||
then bools instead of uint32_t's
|
||||
then based on what sense list they come from, all outputs, then all inputs
|
||||
finally have any signals part of a "usually" block, or constant.
|
||||
* Rather then tracking widths, have a MSB...LSB of this expression
|
||||
(or better, a bitmask of bits relevant in this expression)
|
||||
* Track recirculation and convert into clock-enables
|
||||
* Clock enables should become new clocking domains for speed
|
||||
* If floped(a) & flopped(b) and no other a&b, then instead flop(a&b).
|
||||
* Sort by output bitselects so can combine more assignments (see DDP example dx_dm signal)
|
||||
* Performance:
|
||||
** Latch optimizations
|
||||
** Constant propagation
|
||||
Extra cleaning AND: 1 & ((VARREF >> 1) | ((&VARREF >> 1) & VARREF))
|
||||
Extra shift (perhaps due to clean): if (1 & CAST (VARREF >> #))
|
||||
** Gated clock and latch conversion to flops. [JeanPaul Vanitegem]
|
||||
Could propagate the AND into pos/negedges and let domaining optimize.
|
||||
** Negedge reset
|
||||
Switch to remove negedges that don't matter
|
||||
Can't remove async resets from control flops (like in syncronizers)
|
||||
** If all references to array have a constant index, blow up into separate signals-per-index
|
||||
** Bit-multiply for faster bit swapping and a=b[1,3,2] random bit reorderings.
|
||||
** Move _last sets and all other combo logic inside master
|
||||
if() that triggers on all possible sense items
|
||||
** Rewrite and combine V3Life, V3Subst
|
||||
If block temp only ever set in one place to constant, propagate it
|
||||
Used in t_mem for array delayed assignments
|
||||
Replace variables if set later in same cfunc branch
|
||||
See for example duplicate sets of _narrow in cycle 90/91 of t_select_plusloop
|
||||
** Same assignment on both if branches
|
||||
"if (a) { ... b=2; } else { ... b=2;}" -> "b=2; if ..."
|
||||
Careful though, as b could appear in the statement or multiple times in statement
|
||||
(Could just require exatly two 'b's in statement)
|
||||
** Simplify XOR/XNOR/AND/OR bit selection trees
|
||||
Foo = A[1] ^ A[2] ^ A[3] etc are better as ^ ( A & 32'b...1110 )
|
||||
** Combine variables into wider elements
|
||||
Parallel statements on different bits should become single signal
|
||||
Variables that are always consumed in "parallel" can be joined
|
||||
** Duplicate assignments in gate optimization
|
||||
Common to have many separate posedge blocks, each with identical
|
||||
reset_r <= rst_in
|
||||
** If signal is used only once (not counting trace), always gate substitute
|
||||
Don't merge if any combining would form circ logic (out goes back to in)
|
||||
** Multiple assignments each bit can become single assign with concat
|
||||
Make sure a SEL of a CONCAT can get the single bit back.
|
||||
** Usually blocks/values
|
||||
Enable only after certain time, so VL_TIME_I(32) > 0x1e gets eliminated out
|
||||
** Better ordering of a<=b, b<=c, put all refs to 'b' next to each other to optimize caching
|
||||
** I-cache packing improvements (what/how?)
|
||||
** Data cache organization (order of vars in class)
|
||||
First have clocks,
|
||||
then bools instead of uint32_t's
|
||||
then based on what sense list they come from, all outputs, then all inputs
|
||||
finally have any signals part of a "usually" block, or constant.
|
||||
** Rather then tracking widths, have a MSB...LSB of this expression
|
||||
(or better, a bitmask of bits relevant in this expression)
|
||||
** Track recirculation and convert into clock-enables
|
||||
** Clock enables should become new clocking domains for speed
|
||||
** If floped(a) & flopped(b) and no other a&b, then instead flop(a&b).
|
||||
** Sort by output bitselects so can combine more assignments (see DDP example dx_dm signal)
|
||||
|
||||
+434
-82
@@ -134,13 +134,15 @@ sub verilator_bin {
|
||||
|| ($Debug ? "verilator_bin_dbg" : "verilator_bin"));
|
||||
if (defined($ENV{VERILATOR_ROOT})) {
|
||||
my $dir = $ENV{VERILATOR_ROOT};
|
||||
if (-x "$dir/bin/$basename") { # From a "make install" into VERILATOR_ROOT
|
||||
if (-x "$dir/bin/$basename"
|
||||
|| -x "$dir/bin/$basename.exe") { # From a "make install" into VERILATOR_ROOT
|
||||
$bin = "$dir/bin/$basename";
|
||||
} else {
|
||||
$bin = "$dir/$basename"; # From pointing to kit directory
|
||||
}
|
||||
} else {
|
||||
if (-x "$RealBin/$basename") {
|
||||
if (-x "$RealBin/$basename"
|
||||
|| -x "$RealBin/$basename.exe") {
|
||||
$bin = "$RealBin/$basename"; # From path/to/verilator with verilator_bin installed
|
||||
} else {
|
||||
$bin = $basename; # Find in PATH
|
||||
@@ -287,6 +289,7 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
|
||||
--debugi-<srcfile> <level> Enable debugging a source file at a level
|
||||
--default-language <lang> Default language to parse
|
||||
+define+<var>=<value> Set preprocessor define
|
||||
--dump-defines Show preprocessor defines with -E
|
||||
--dump-tree Enable dumping .tree files
|
||||
--dump-treei <level> Enable dumping .tree files at a level
|
||||
--dump-treei-<srcfile> <level> Enable dumping .tree file at a source file at a level
|
||||
@@ -336,8 +339,10 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
|
||||
--pins-sc-biguint Specify types for top level ports
|
||||
--pins-uint8 Specify types for top level ports
|
||||
--pipe-filter <command> Filter all input through a script
|
||||
--pp-comments Show preprocessor comments with -E
|
||||
--prefix <topname> Name of top level class
|
||||
--prof-cfuncs Name functions for profiling
|
||||
--prof-threads Enable generating gantt chart data for threads
|
||||
--private Debugging; see docs
|
||||
--public Debugging; see docs
|
||||
-pvalue+<name>=<value> Overwrite toplevel parameter
|
||||
@@ -350,8 +355,13 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
|
||||
--stats-vars Provide statistics on variables
|
||||
-sv Enable SystemVerilog parsing
|
||||
+systemverilogext+<ext> Synonym for +1800-2017ext+<ext>
|
||||
--threads <threads> Enable multithreading
|
||||
--threads-dpi <mode> Enable multithreaded DPI
|
||||
--threads-max-mtasks <mtasks> Tune maximum mtask partitioning
|
||||
--top-module <topname> Name of top level input module
|
||||
--trace Enable waveform creation
|
||||
--trace-fst Enable FST waveform creation
|
||||
--trace-lxt2 Enable LXT2 waveform creation
|
||||
--trace-depth <levels> Depth of tracing
|
||||
--trace-max-array <depth> Maximum bit width for tracing
|
||||
--trace-max-width <width> Maximum array depth for tracing
|
||||
@@ -380,6 +390,19 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
|
||||
--x-initial-edge Enable initial X->0 and X->1 edge triggers
|
||||
-y <dir> Directory to search for modules
|
||||
|
||||
This is a short summary of the arguments to run-time Verilated arguments.
|
||||
detailed descriptions in L</"RUNTIME ARGUMENTS"> for more information.
|
||||
|
||||
+verilator+debug Enable debugging
|
||||
+verilator+debugi+<value> Enable debugging at a level
|
||||
+verilator+help Display help
|
||||
+verilator+prof+threads+file+I<filename> Set profile filename
|
||||
+verilator+prof+threads+start+I<value> Set profile starting point
|
||||
+verilator+prof+threads+window+I<value> Set profile duration
|
||||
+verilator+rand+reset+<value> Set random reset technique
|
||||
+verilator+V Verbose version and config
|
||||
+verilator+version Show version and exit
|
||||
|
||||
|
||||
=head1 VERILATION ARGUMENTS
|
||||
|
||||
@@ -482,9 +505,13 @@ output files.
|
||||
|
||||
=item -CFLAGS I<flags>
|
||||
|
||||
Add specified C compiler flags to the generated makefiles. When make is
|
||||
run on the generated makefile these will be passed to the C++ compiler
|
||||
(gcc/g++/msvc++).
|
||||
Add specified C compiler flag to the generated makefiles. For multiple
|
||||
flags either pass them as a single argument with space separators quoted in
|
||||
the shell (C<-CFLAGS "-a -b">), or use multiple -CFLAGS arguments
|
||||
(C<-CFLAGS -a -CFLAGS -b>).
|
||||
|
||||
When make is run on the generated makefile these will be passed to the C++
|
||||
compiler (gcc/g++/msvc++).
|
||||
|
||||
=item --cc
|
||||
|
||||
@@ -693,6 +720,15 @@ Defines the given preprocessor symbol, or multiple symbols if separated by
|
||||
plusses. Similar to -D; +define is fairly standard across Verilog tools
|
||||
while -D is an alias for GCC compatibility.
|
||||
|
||||
=item --dump-defines
|
||||
|
||||
With -E, suppress normal output, and instead print a list of all defines
|
||||
existing at the end of pre-processing the input files. Similar to GCC "-dM"
|
||||
option. This also gives you a way of finding out what is predefined in
|
||||
Verilator using the command:
|
||||
|
||||
touch foo.v ; verilator -E --dump-defines foo.v
|
||||
|
||||
=item --dump-tree
|
||||
|
||||
Rarely needed. Enable writing .tree debug files with dumping level 3,
|
||||
@@ -841,13 +877,17 @@ small modules; they will always be inlined, if allowed.
|
||||
|
||||
=item -LDFLAGS I<flags>
|
||||
|
||||
Add specified C linker flags to the generated makefiles. When make is run
|
||||
on the generated makefile these will be passed to the C++ linker (ld)
|
||||
*after* the primary file being linked. This flag is called -LDFLAGS as
|
||||
that's the traditional name in simulators; it's would have been better
|
||||
called LDLIBS as that's the Makefile variable it controls. (In Make,
|
||||
LDFLAGS is before the first object, LDLIBS after. -L libraries need to be
|
||||
in the Make variable LDLIBS, not LDFLAGS.)
|
||||
Add specified C linker flags to the generated makefiles. For multiple
|
||||
flags either pass them as a single argument with space separators quoted in
|
||||
the shell (C<-LDFLAGS "-a -b">), or use multiple -LDFLAGS arguments
|
||||
(C<-LDFLAGS -a -LDFLAGS -b>).
|
||||
|
||||
When make is run on the generated makefile these will be passed to the C++
|
||||
linker (ld) *after* the primary file being linked. This flag is called
|
||||
-LDFLAGS as that's the traditional name in simulators; it's would have been
|
||||
better called LDLIBS as that's the Makefile variable it controls. (In
|
||||
Make, LDFLAGS is before the first object, LDLIBS after. -L libraries need
|
||||
to be in the Make variable LDLIBS, not LDFLAGS.)
|
||||
|
||||
=item --l2-name I<value>
|
||||
|
||||
@@ -1054,6 +1094,10 @@ t/t_pipe_filter test for an example.
|
||||
To debug the output of the filter, try using the -E option to see
|
||||
preprocessed output.
|
||||
|
||||
=item --pp-comments
|
||||
|
||||
With -E, show comments in preprocessor output.
|
||||
|
||||
=item --prefix I<topname>
|
||||
|
||||
Specifies the name of the top level class and makefile. Defaults to V
|
||||
@@ -1070,6 +1114,18 @@ Verilog module and line number the statement came from. This allows gprof
|
||||
or oprofile reports to be correlated with the original Verilog source
|
||||
statements. See also L<verilator_profcfunc>.
|
||||
|
||||
=item --prof-threads
|
||||
|
||||
Enable gantt chart data collection for threaded builds.
|
||||
|
||||
Verilator will record the start and end time of each macro-task across a
|
||||
number of calls to eval. (What is a macro-task? See the Verilator internals
|
||||
document.)
|
||||
|
||||
When profiling is enabled, the runtime will emit a blurb of profiling data
|
||||
in non-human-friendly form. The C<verilator_gantt> script will transform
|
||||
this into a nicer visual format and produce some related statistics.
|
||||
|
||||
=item --private
|
||||
|
||||
Opposite of --public. Is the default; this option exists for backwards
|
||||
@@ -1124,7 +1180,10 @@ Enable including save and restore functions in the generated model.
|
||||
|
||||
The user code must create a VerilatedSerialize or VerilatedDeserialze
|
||||
object then calling the << or >> operators on the generated model and any
|
||||
other data the process needs saved/restored. For example:
|
||||
other data the process needs saved/restored. These functions are not
|
||||
thread safe, and are typically called only by a main thread.
|
||||
|
||||
For example:
|
||||
|
||||
void save_model(const char* filenamep) {
|
||||
VerilatedSave os;
|
||||
@@ -1163,6 +1222,42 @@ compatibility with other simulators.
|
||||
|
||||
A synonym for C<+1800-2017ext+>I<ext>.
|
||||
|
||||
=item --threads I<threads>
|
||||
|
||||
=item --no-threads
|
||||
|
||||
With --threads 0 or --no-threads, the default, the generated model is not
|
||||
thread safe. With --threads 1, the generated model is single threaded but
|
||||
may run in a multithreaded environment. With --threads N, where N >= 2, the
|
||||
model is generated to run multithreaded on up to N threads. See
|
||||
L</"MULTITHREADING">.
|
||||
|
||||
=item --threads-dpi all
|
||||
|
||||
=item --threads-dpi none
|
||||
|
||||
=item --threads-dpi pure
|
||||
|
||||
When using --dpi with --threads, control what DPI tasks are thread safe.
|
||||
|
||||
With --threads-dpi all, enable Verilator to assume all DPI imports are
|
||||
threadsafe, and to use thread-local storage for communication with DPI,
|
||||
potentially improving performance. Any DPI libraries need appropriate
|
||||
mutexes to avoid undefined behavior.
|
||||
|
||||
With --threads-dpi none, Verilator assume DPI imports are not thread safe,
|
||||
and Verilator will serialize calls to DPI imports by default, potentially
|
||||
harming performance.
|
||||
|
||||
With --threads-dpi pure, the default, Verilator assumes DPI pure imports
|
||||
are threadsafe, but non-pure DPI imports are not.
|
||||
|
||||
=item --threads-max-mtasks I<value>
|
||||
|
||||
Rarely needed. When using --threads, specify the number of mtasks the
|
||||
model is to be partitioned into. If unspecified, Verilator approximates a
|
||||
good value.
|
||||
|
||||
=item --top-module I<topname>
|
||||
|
||||
When the input Verilog contains more than one top level module, specifies
|
||||
@@ -1172,17 +1267,29 @@ designs with only one top.
|
||||
|
||||
=item --trace
|
||||
|
||||
Adds waveform tracing code to the model. Verilator will generate
|
||||
additional {prefix}__Trace*.cpp files that will need to be compiled. In
|
||||
addition verilated_vcd_sc.cpp (for SystemC traces) or verilated_vcd_c.cpp
|
||||
(for both) must be compiled and linked in. If using the Verilator
|
||||
generated Makefiles, these files will be added as source targets for you.
|
||||
If you're not using the Verilator makefiles, you will need to add these to
|
||||
your Makefile manually.
|
||||
Adds waveform tracing code to the model using VCD format. This overrides
|
||||
C<--trace-fst> and C<--trace-lxt2>.
|
||||
|
||||
Verilator will generate additional {prefix}__Trace*.cpp files that will
|
||||
need to be compiled. In addition verilated_vcd_sc.cpp (for SystemC traces)
|
||||
or verilated_vcd_c.cpp (for both) must be compiled and linked in. If using
|
||||
the Verilator generated Makefiles, these files will be added as source
|
||||
targets for you. If you're not using the Verilator makefiles, you will
|
||||
need to add these to your Makefile manually.
|
||||
|
||||
Having tracing compiled in may result in some small performance losses,
|
||||
even when waveforms are not turned on during model execution.
|
||||
|
||||
=item --trace-fst
|
||||
|
||||
Enable FST waveform tracing in the model. This overrides C<--trace> and C<--trace-lxt2>.
|
||||
|
||||
=item --trace-lxt2
|
||||
|
||||
Enable LXT2 waveform tracing in the model. This overrides C<--trace> and
|
||||
C<--trace-fst>. This option is deprecated in favor of FST traces and may
|
||||
be removed in the near future.
|
||||
|
||||
=item --trace-depth I<levels>
|
||||
|
||||
Specify the number of levels deep to enable tracing, for example
|
||||
@@ -1432,6 +1539,67 @@ are desired for error messages instead of relative filenames.
|
||||
=back
|
||||
|
||||
|
||||
=head1 RUNTIME ARGUMENTS
|
||||
|
||||
The following are the arguments that may be passed to a Verilated
|
||||
executable, provided that executable calls Verilated::commandArgs().
|
||||
|
||||
All runtime arguments begin with +verilator, so that the user's executable
|
||||
may skip over all +verilator arguments when parsing its command line.
|
||||
|
||||
=over 4
|
||||
|
||||
=item +verilator+debug
|
||||
|
||||
Enable debugging. Equivalent to +verilator+debugi+4.
|
||||
|
||||
=item +verilator+debugi+I<value>
|
||||
|
||||
Enable debugging at the provided level.
|
||||
|
||||
=item +verilator+help
|
||||
|
||||
Display help and exit.
|
||||
|
||||
=item +verilator+prof+threads+file+I<filename>
|
||||
|
||||
When using --prof-threads, the filename to dump to. Defaults to
|
||||
"profile_threads.dat".
|
||||
|
||||
=item +verilator+prof+threads+start+I<value>
|
||||
|
||||
When using --prof-threads, Verilator will wait until $time is at this
|
||||
value, then start the profiling warmup, then capturing. Generally this
|
||||
should be set to some time that is well within the normal operation of the
|
||||
simulation, i.e. outside of reset. If 0, the dump is disabled. Defaults to
|
||||
1.
|
||||
|
||||
=item +verilator+prof+threads+window+I<value>
|
||||
|
||||
When using --prof-threads, after $time reaches
|
||||
+verilator+prof+threads+start, Verilator will warm up the profiling for
|
||||
this number of eval() calls, then will capture the profiling of this number
|
||||
of eval() calls. Defaults to 2, which makes sense for a
|
||||
single-clock-domain module where it's typical to want to capture one
|
||||
posedge eval() and one negedge eval().
|
||||
|
||||
=item +verilator+rand+reset+I<value>
|
||||
|
||||
When a model was Verilated using "-x-inital unique", sets the
|
||||
initialization technique. 0 = Reset to zeros. 1 = Reset to all-ones. 2 =
|
||||
Randomize. See L</"Unknown states">.
|
||||
|
||||
=item +verilator+V
|
||||
|
||||
Shows the verbose version, including configuration information.
|
||||
|
||||
=item +verilator+version
|
||||
|
||||
Displays program version and exits.
|
||||
|
||||
=back
|
||||
|
||||
|
||||
=head1 EXAMPLE C++ EXECUTION
|
||||
|
||||
We'll compile this example into C++.
|
||||
@@ -1586,6 +1754,9 @@ compile times, and --x-assign=fast --x-initial=fast may increase the risk
|
||||
of reset bugs in trade for performance; see the above documentation for
|
||||
these flags.
|
||||
|
||||
If using Verilated multithreaded, use C<numactl> to ensure you are using
|
||||
non-conflicting hardware resources. See L</"MULTITHREADING">.
|
||||
|
||||
Minor Verilog code changes can also give big wins. You should not have any
|
||||
UNOPTFLAT warnings from Verilator. Fixing these warnings can result in
|
||||
huge improvements; one user fixed their one UNOPTFLAT warning by making a
|
||||
@@ -1662,34 +1833,45 @@ For -cc and -sc mode, it also creates:
|
||||
|
||||
{prefix}.cpp // Top level C++ file
|
||||
{prefix}.h // Top level header
|
||||
{prefix}__Slow{__n}.cpp // Constructors and infrequent routines
|
||||
{prefix}{__n}.cpp // Additional top C++ files (--output-split)
|
||||
{prefix}{each_verilog_module}.cpp // Lower level internal C++ files
|
||||
{prefix}{each_verilog_module}.h // Lower level internal header files
|
||||
{prefix}{each_verilog_module}{__n}.cpp // Additional lower C++ files (--output-split)
|
||||
|
||||
In certain optimization modes, it also creates:
|
||||
In certain debug and other modes, it also creates:
|
||||
|
||||
{prefix}.xml // XML tree information (--xml)
|
||||
{prefix}__Dpi.cpp // DPI import and export wrappers
|
||||
{prefix}__Dpi.h // DPI import and export declarations
|
||||
{prefix}__Inlines.h // Inline support functions
|
||||
{prefix}__Slow.cpp // Constructors and infrequent routines
|
||||
{prefix}__Syms.cpp // Global symbol table C++
|
||||
{prefix}__Syms.h // Global symbol table header
|
||||
{prefix}__Trace.cpp // Wave file generation code (--trace)
|
||||
{prefix}__Trace__Slow{__n}.cpp // Wave file generation code (--trace)
|
||||
{prefix}__Trace{__n}.cpp // Wave file generation code (--trace)
|
||||
{prefix}__cdc.txt // Clock Domain Crossing checks (--cdc)
|
||||
{prefix}__stats.txt // Statistics (--stats)
|
||||
|
||||
It also creates internal files that can be mostly ignored:
|
||||
|
||||
{each_verilog_module}.vpp // Post-processed verilog (--debug)
|
||||
{prefix}.flags_vbin // Verilator dependencies
|
||||
{prefix}.flags_vpp // Pre-processor dependencies
|
||||
{mod_prefix}_{each_verilog_module}{__n}.vpp // Post-processed verilog
|
||||
{prefix}__ver.d // Make dependencies (-MMD)
|
||||
{prefix}__verFiles.dat // Timestamps for skip-identical
|
||||
{prefix}{misc}.d // Make dependencies (-MMD)
|
||||
{prefix}{misc}.dot // Debugging graph files (--debug)
|
||||
{prefix}{misc}.tree // Debugging files (--debug)
|
||||
|
||||
After running Make, the C++ compiler should produce the following:
|
||||
After running Make, the C++ compiler may produce the following:
|
||||
|
||||
verilated{misc}.d // Intermediate dependencies
|
||||
verilated{misc}.o // Intermediate objects
|
||||
{mod_prefix}{misc}.d // Intermediate dependencies
|
||||
{mod_prefix}{misc}.o // Intermediate objects
|
||||
{prefix} // Final executable (w/--exe argument)
|
||||
{prefix}__ALL.a // Library of all Verilated objects
|
||||
{prefix}__ALLboth.cpp // Include of classes for single compile
|
||||
{prefix}__ALLcls.cpp // Include of user classes for single compile
|
||||
{prefix}__ALLsup.cpp // Include of support files for single compile
|
||||
{prefix}{misc}.d // Intermediate dependencies
|
||||
{prefix}{misc}.o // Intermediate objects
|
||||
|
||||
|
||||
@@ -1707,8 +1889,8 @@ needed at runtime.
|
||||
|
||||
Optionally specifies a caching or distribution program to place in front of
|
||||
all runs of the C++ Compiler. For example, "objcache --read --write", or
|
||||
"ccache". If using distcc, it would generally be run under either objcache
|
||||
or ccache; see the documentation for those programs.
|
||||
"ccache". If using distcc or icecc/icecream, they would generally be run
|
||||
under either objcache or ccache; see the documentation for those programs.
|
||||
|
||||
=item SYSTEMC
|
||||
|
||||
@@ -2127,6 +2309,91 @@ the names of the .cpp files to compile in from the make variables generated
|
||||
in obj_dir/Vour_classes.mk.
|
||||
|
||||
|
||||
=head1 MULTITHREADING
|
||||
|
||||
Verilator experimentally supports multithreading.
|
||||
|
||||
With --no-threads, the default, the model is not thread safe, and any use
|
||||
of more than one thread calling into one or even different Verilated models
|
||||
may result in unpredictable behavior. This gives the highest single thread
|
||||
performance.
|
||||
|
||||
With --threads 1, the generated model is single threaded, however the
|
||||
support libraries are multithread safe. This allows different
|
||||
instantiations of model(s) to potentially each be run under a different
|
||||
thread. All threading is the responsibility of the user's C++ testbench.
|
||||
|
||||
With --threads N, where N is at least 2, the generated model will be
|
||||
designed to run in parallel on N threads. The thread calling eval()
|
||||
provides one of those threads, and the generated model will create and
|
||||
manage the other N-1 threads. It's the client's responsibility not to
|
||||
oversubscribe the available CPU cores. Under CPU oversubscription, the
|
||||
Verilated model should not livelock nor deadlock, however, you can expect
|
||||
performance to be far worse than it would be with proper stoichiometry of
|
||||
threads and CPU cores.
|
||||
|
||||
The remainder of this section describe behavior with --threads 1 or
|
||||
--threads N (not --no-threads).
|
||||
|
||||
VL_THREADED is defined when compiling a threaded Verilated module, causing
|
||||
the Verilated support classes become threadsafe.
|
||||
|
||||
The thread used for constructing a model must the the same thread that
|
||||
calls eval() into the model, this is called the "eval thread". The thread
|
||||
used to perform certain global operations such as saving and tracing must
|
||||
be done by a "main thread". In most cases the eval thread and main thread
|
||||
are the same thread (i.e. the user's top C++ testbench runs on a single
|
||||
thread), but this is not required.
|
||||
|
||||
When running a multithreaded model, the default Linux task scheduler often
|
||||
works against the model, by assuming threads are short lived, and thus
|
||||
often schedules threads using multiple hyperthreads within the same
|
||||
physical core. For best performance use the C<numactl> program to (when the
|
||||
threading count fits) select unique physical cores on the same socket. For
|
||||
example, if a model was Verilated with "--threads 4", we consult
|
||||
|
||||
egrep 'processor|physical id|core id' /proc/cpuinfo
|
||||
|
||||
To select cores 0, 1, 2, and 3 that are all located on the same socket (0)
|
||||
but different physical cores. (Also useful is "numactl --hardware", or
|
||||
C<lscpu> but those doesn't show Hyperthreading cores.) Then we execute
|
||||
|
||||
numactl -m 0 -C 0,1,2,3 -- verilated_executable_name
|
||||
|
||||
This will limit memory to socket 0, and threads to cores 0, 1, 2, 3,
|
||||
(presumably on socket 0) optimizing performance. Of course this must be
|
||||
adjusted if you want another simulator using e.g. socket 1, or if you
|
||||
Verilated with a different number of threads. To see what CPUs are
|
||||
actually used, use --prof-threads.
|
||||
|
||||
=head2 Multithreaded Verilog and Library Support
|
||||
|
||||
$display/$stop/$finish are delayed until the end of an eval() call in order
|
||||
to maintain ordering between threads. This may result in additional tasks
|
||||
completing after the $stop or $finish.
|
||||
|
||||
=over 4
|
||||
|
||||
If using --coverage, the coverage routines are fully thread safe.
|
||||
|
||||
If using --dpi, Verilator assumes pure DPI imports are thread safe,
|
||||
balancing performance versus saftey. See --threads-dpi.
|
||||
|
||||
If using --savable, the save/restore classes are not multithreaded and are
|
||||
must be called only by the eval thread.
|
||||
|
||||
If using --sc, the SystemC kernel is not thread safe, therefore the eval
|
||||
thread and main thread must be the same.
|
||||
|
||||
If using --trace, the tracing classes must be constructed and called from
|
||||
the main thread.
|
||||
|
||||
If using --vpi, since SystemVerilog VPI was not architected by IEEE to be
|
||||
multithreaded, Verilator requires all VPI calls are only made from the main
|
||||
thread.
|
||||
|
||||
=back
|
||||
|
||||
=head1 CONFIGURATION FILES
|
||||
|
||||
In addition to the command line, warnings and other features may be
|
||||
@@ -3361,10 +3628,14 @@ correctly.
|
||||
|
||||
=item MODDUP
|
||||
|
||||
Error that a module has multiple definitions. Generally this indicates a
|
||||
Warns that a module has multiple definitions. Generally this indicates a
|
||||
coding error, or a mistake in a library file and it's good practice to have
|
||||
one module per file to avoid these issues. For some gate level netlists
|
||||
duplicates are unavoidable, and this error may be disabled.
|
||||
one module per file (and only put each file once on the command line) to
|
||||
avoid these issues. For some gate level netlists duplicates are sometimes
|
||||
unavoidable, and MODDUP should be disabled.
|
||||
|
||||
Ignoring this warning will cause the more recent module definition to be
|
||||
discarded.
|
||||
|
||||
=item MULTIDRIVEN
|
||||
|
||||
@@ -3503,6 +3774,17 @@ Change this to:
|
||||
Verilator doesn't do this conversion for you, as some more complicated
|
||||
cases would result in simulator mismatches.
|
||||
|
||||
=item TICKCOUNT
|
||||
|
||||
Warns that the number of ticks to delay a $past variable is greater than
|
||||
10. At present Verilator effectively creates a flop for each delayed
|
||||
signals, and as such any large counts may lead to large design size
|
||||
increases.
|
||||
|
||||
Ignoring this warning will only slow simulations, it will simulate
|
||||
correctly.
|
||||
|
||||
|
||||
=item UNDRIVEN
|
||||
|
||||
Warns that the specified signal is never sourced. Verilator is fairly
|
||||
@@ -3587,6 +3869,21 @@ section for more details.
|
||||
Ignoring this warning will only slow simulations, it will simulate
|
||||
correctly.
|
||||
|
||||
=item UNOPTTHREADS
|
||||
|
||||
Warns that the thread scheduler was unable to partition the design to fill
|
||||
the requested number of threads.
|
||||
|
||||
One workaround is to request fewer threads with C<--threads>.
|
||||
|
||||
Another possible workaround is to allow more MTasks in the runtime, by
|
||||
increasing the value of --threads-max-mtasks. More MTasks will result in
|
||||
more communication and synchronization overhead at runtime; the scheduler
|
||||
attempts to minimize the number of MTasks for this reason.
|
||||
|
||||
Ignoring this warning will only slow simulations, it will simulate
|
||||
correctly.
|
||||
|
||||
=item UNPACKED
|
||||
|
||||
Warns that unpacked structs and unions are not supported.
|
||||
@@ -3711,17 +4008,30 @@ For example:
|
||||
will toggle forever and thus the executable will give the didn't converge
|
||||
error to prevent an infinite loop.
|
||||
|
||||
To debug this, run Verilator with --prof-cfuncs. Run make on the
|
||||
generated files with "OPT=-DVL_DEBUG". Then call Verilated::debug(1) in
|
||||
your main.cpp.
|
||||
To debug this, first is to review any UNOPTFLAT warnings that were ignored,
|
||||
though typically these can be ignored (at a performance cost), convergence
|
||||
issues can also be flagged with this warning as Verilator didn't know if
|
||||
they would eventually converge.
|
||||
|
||||
Next, run Verilator with --prof-cfuncs. Run make on the generated
|
||||
files with "OPT=-DVL_DEBUG". Then call Verilated::debug(1) in your
|
||||
main.cpp.
|
||||
|
||||
This will cause each change in a variable to print a message. Near the
|
||||
bottom you'll see the code and variable that causes the problem. For the
|
||||
program above:
|
||||
bottom you'll see the variables that causes the problem. For the program
|
||||
above:
|
||||
|
||||
CHANGE: filename.v:1: b
|
||||
CHANGE: filename.v:2: a
|
||||
|
||||
If many signals are getting printed then most likely each are oscillating
|
||||
(or there is a bug). It may also be that e.g. "a" may be oscillating, then
|
||||
"a" feeds signal "c" which then is also reported as oscillating.
|
||||
|
||||
Finally, rare more difficult cases can be debugged like a "C" program;
|
||||
either enter GDB and use its tracing facilities, or edit the generated C++
|
||||
code to add appropriate prints to see what is going on.
|
||||
|
||||
=back
|
||||
|
||||
|
||||
@@ -3862,11 +4172,46 @@ trace file if you want all data to land in the same output file.
|
||||
tfp->close();
|
||||
}
|
||||
|
||||
=item How do I generate FST waveforms (traces) in C++?
|
||||
|
||||
FST a format by GTKWave.
|
||||
This version provides a basic FST support.
|
||||
To dump FST format, add the --trace switch to Verilator and change the include path in the testbench to:
|
||||
|
||||
#include "verilated_fst_c.h"
|
||||
VerilatedFstC* tfp = new VerilatedFstC;
|
||||
|
||||
Note that currently supporting both FST and VCD in a single simulation is impossible,
|
||||
but such requirement could be rare.
|
||||
|
||||
=item How do I generate FST waveforms (traces) in SystemC?
|
||||
|
||||
The FST library from GTKWave does not currently support SystemC; use VCD
|
||||
format instead.
|
||||
|
||||
=item How do I generate LXT2 waveforms (traces) in C++?
|
||||
|
||||
LXT2 a format by GTKWave, which is usually 10x smaller than VCD format.
|
||||
This version provides a basic LXT2 support.
|
||||
To dump LXT2 format, add the --trace switch to Verilator and change the include path in the testbench to:
|
||||
|
||||
#include "verilated_lxt2_c.h"
|
||||
VerilatedLxt2C* tfp = new VerilatedLxt2C;
|
||||
|
||||
Note that currently supporting both LXT2 and VCD in a single simulation is impossible,
|
||||
but such requirement could be rare.
|
||||
|
||||
=item How do I generate LXT2 waveforms (traces) in SystemC?
|
||||
|
||||
The LXT2 library from GTKWave does not currently support SystemC; use VCD
|
||||
format instead.
|
||||
|
||||
=item How do I view waveforms (traces)?
|
||||
|
||||
Verilator makes standard VCD (Value Change Dump) files. They are viewable
|
||||
with the public domain GtkWave (recommended) or Dinotrace (legacy)
|
||||
programs, or any of the many commercial offerings.
|
||||
Verilator makes standard VCD (Value Change Dump), LXT2 and FST files. VCD files are viewable
|
||||
with the public domain GTKWave (recommended) or Dinotrace (legacy)
|
||||
programs, or any of the many commercial offerings;
|
||||
LXT2 and FST are supported by GTKWave only.
|
||||
|
||||
=item How do I reduce the size of large waveform (trace) files?
|
||||
|
||||
@@ -4136,6 +4481,8 @@ performance gain.
|
||||
|
||||
In 2009, major SystemVerilog and DPI language support was added.
|
||||
|
||||
In 2018, Verilator 4.000 was released with multithreaded support.
|
||||
|
||||
Currently, various language features and performance enhancements are added
|
||||
as the need arises. Verilator is now about 3x faster than in 2002, and is
|
||||
faster than many popular commercial simulators.
|
||||
@@ -4147,7 +4494,7 @@ When possible, please instead report bugs to L<http://www.veripool.org/>.
|
||||
|
||||
Wilson Snyder <[email protected]>
|
||||
|
||||
Major concepts by Paul Wasson, Duane Galbi and Jie Xu.
|
||||
Major concepts by Paul Wasson, Duane Galbi, John Coiner and Jie Xu.
|
||||
|
||||
|
||||
=head1 CONTRIBUTORS
|
||||
@@ -4171,52 +4518,57 @@ Some of the people who have provided ideas and feedback for Verilator
|
||||
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,
|
||||
Bennett, Michael Berman, Victor Besyakov, David Binderman, Johan Bjork,
|
||||
David Black, Tymoteusz Blazejczyk, 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, Sebastien Van Cauwenberghe,
|
||||
Terry Chen, Enzo Chi, Robert A. Clark, Allan Cochrane, John Coiner, Laurens
|
||||
van Dam, Gunter Dannoritzer, Ashutosh Das, Bernard Deadman, John Demme,
|
||||
Mike Denio, John Deroo, Philip Derrick, Joe DErrico, 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, Trevor Elbourne, Robert Farrell, Eugen Fekete, Fabrizio
|
||||
Ferrandi, Brian Flachs, Andrea Foletto, Bob Fredieu, Duane Galbi, 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, Joel
|
||||
Holdsworth, Hiroki Honda, Alex Hornung, David Horton, Jae Hossell, Alan
|
||||
Hunter, James Hutchinson, Jamie Iles, Ben Jackson, Shareef Jalloq,
|
||||
Krzysztof Jankowski, HyungKi Jeong, Iztok Jeras, James Johnson, Christophe
|
||||
Joly, Franck Jullien, James Jung, Mike Kagen, Arthur Kahlich, Kaalia Kahn,
|
||||
Guy-Armand Kamendje, Vasu Kandadi, Patricio Kaplan, Ralf Karge, Dan Katz,
|
||||
Sol Katzman, Jonathan Kimmitt, Olof Kindgren, Dan Kirkham, 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, Yu Sheng Lin, Charlie Lind, Andrew Ling, Paul Liu,
|
||||
Derek Lockhart, Arthur Low, Stefan Ludwig, Dan Lussier, Fred Ma, Duraid
|
||||
Madina, Julien Margetts, 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, 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.
|
||||
Priyadharshini, Mark Jackson Pulver, Prateek Puri, Marshal Qiao, Chris
|
||||
Randall, Anton Rapp, Josh Redford, Odd Magne Reitan, Frederic Requin,
|
||||
Alberto Del Rio, Oleg Rodionov, Paul Rolfe, Arjen Roodselaar, Jan Egil
|
||||
Ruud, John Sanguinetti, Galen Seitz, Salman Sheikh, Mike Shinkarovsky,
|
||||
Rafael Shirakawa, Jeffrey Short, Rodney Sinclair, Steven Slatter, Brian
|
||||
Small, Wilson Snyder, Alex Solomatnikov, Wei Song, Art Stamness, John
|
||||
Stevenson, Patrick Stewart, Rob Stoddard, Todd Strader, John Stroebel, Sven
|
||||
Stucki, Emerson Suguimoto, Gene Sullivan, Renga Sundararajan, Yutetsu
|
||||
Takatsukasa, Peter Tengstrand, Wesley Terpstra, Rui Terra, Stefan Thiede,
|
||||
Gary Thomas, Kevin Thompson, Ian Thompson, Mike Thyer, Hans Tichelaar,
|
||||
Steve Tong, Michael Tresidder, Holger Waechtler, Stefan Wallentowitz, Shawn
|
||||
Wang, Paul Wasson, 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, Junyi Xi, Ding
|
||||
Xiaoliang, Jie Xu, Mandy Xu, Luke Yang, and Amir Yazdanbakhsh.
|
||||
|
||||
Thanks to them, and all those we've missed including above.
|
||||
Thanks to them, and all those we've missed including above, or wished to
|
||||
remain anonymous.
|
||||
|
||||
|
||||
=head1 DISTRIBUTION
|
||||
@@ -4231,7 +4583,7 @@ License Version 2.0.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<verilator_coverage>, L<verilator_profcfunc>, L<make>,
|
||||
L<verilator_coverage>, L<verilator_gantt>, L<verilator_profcfunc>, L<make>,
|
||||
|
||||
L<verilator --help> which is the source for this document,
|
||||
|
||||
|
||||
@@ -93,13 +93,15 @@ sub verilator_coverage_bin {
|
||||
|| "verilator_coverage_bin_dbg");
|
||||
if (defined($ENV{VERILATOR_ROOT})) {
|
||||
my $dir = $ENV{VERILATOR_ROOT};
|
||||
if (-x "$dir/bin/$basename") { # From a "make install" into VERILATOR_ROOT
|
||||
if (-x "$dir/bin/$basename"
|
||||
|| -x "$dir/bin/$basename.exe") { # From a "make install" into VERILATOR_ROOT
|
||||
$bin = "$dir/bin/$basename";
|
||||
} else {
|
||||
$bin = "$dir/$basename"; # From pointing to kit directory
|
||||
}
|
||||
} else {
|
||||
if (-x "$RealBin/$basename") {
|
||||
if (-x "$RealBin/$basename"
|
||||
|| -x "$RealBin/$basename.exe") {
|
||||
$bin = "$RealBin/$basename"; # From path/to/verilator with verilator_bin installed
|
||||
} else {
|
||||
$bin = $basename; # Find in PATH
|
||||
|
||||
Executable
+560
@@ -0,0 +1,560 @@
|
||||
: # -*-Mode: perl;-*- use perl, wherever it is
|
||||
eval 'exec perl -wS $0 ${1+"$@"}'
|
||||
if 0;
|
||||
# See copyright, etc in below POD section.
|
||||
######################################################################
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Getopt::Long;
|
||||
use IO::File;
|
||||
use Pod::Usage;
|
||||
use vars qw ($Debug);
|
||||
|
||||
$Debug = 0;
|
||||
my $Opt_File;
|
||||
my $Opt_Time_Per_Char = 0; # rdtsc ticks per char in gantt chart, 0=auto
|
||||
my $opt_vcd = "profile_threads.vcd";
|
||||
|
||||
our %Threads;
|
||||
our %Mtasks;
|
||||
our %Global;
|
||||
|
||||
autoflush STDOUT 1;
|
||||
autoflush STDERR 1;
|
||||
Getopt::Long::config("no_auto_abbrev");
|
||||
if (! GetOptions(
|
||||
"help" => \&usage,
|
||||
"scale=i" => \$Opt_Time_Per_Char,
|
||||
"debug" => sub { $Debug = 1; },
|
||||
"vcd=s" => \$opt_vcd,
|
||||
"no-vcd!" => sub { $opt_vcd = undef; },
|
||||
"<>" => \¶meter,
|
||||
)) {
|
||||
die "%Error: Bad usage, try 'verilator_gantt --help'\n";
|
||||
}
|
||||
|
||||
$Opt_File = "profile_threads.dat" if !defined $Opt_File;
|
||||
|
||||
process($Opt_File);
|
||||
write_vcd($opt_vcd) if defined $opt_vcd;
|
||||
exit(0);
|
||||
|
||||
#######################################################################
|
||||
|
||||
sub usage {
|
||||
pod2usage(-verbose=>2, -exitval=>2, -output=>\*STDOUT);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
sub parameter {
|
||||
my $param = shift;
|
||||
if (!defined $Opt_File) {
|
||||
$Opt_File = $param;
|
||||
} else {
|
||||
die "%Error: Unknown parameter: $param\n";
|
||||
}
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
|
||||
sub process {
|
||||
my $filename = shift;
|
||||
|
||||
read_data($filename);
|
||||
report();
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
|
||||
sub read_data {
|
||||
my $filename = shift;
|
||||
|
||||
%Global = (rdtsc_cycle_time => 0);
|
||||
|
||||
my $fh = IO::File->new ($filename) or die "%Error: $! $filename,";
|
||||
while (my $line = $fh->getline) {
|
||||
if ($line =~ m/VLPROF mtask\s(\d+)\sstart\s(\d+)\send\s(\d+)\selapsed\s(\d+)\spredict_time\s(\d+)\scpu\s(\d+)\son thread (\d+)/) {
|
||||
my $mtask = $1;
|
||||
my $start = $2;
|
||||
my $end = $3;
|
||||
my $elapsed_time = $4;
|
||||
my $predict_time = $5;
|
||||
my $cpu = $6;
|
||||
my $thread = $7;
|
||||
$Threads{$thread}{$start}{mtask} = $mtask;
|
||||
$Threads{$thread}{$start}{end} = $end;
|
||||
$Threads{$thread}{$start}{cpu} = $cpu;
|
||||
|
||||
if (!exists $Mtasks{$mtask}{elapsed}) {
|
||||
$Mtasks{$mtask}{elapsed} = 0;
|
||||
}
|
||||
$Mtasks{$mtask}{elapsed} += $elapsed_time;
|
||||
$Mtasks{$mtask}{predict} = $predict_time;
|
||||
$Mtasks{$mtask}{end} = max($Mtasks{$mtask}{end}, $end);
|
||||
}
|
||||
elsif ($line =~ /^VLPROFTHREAD/) {}
|
||||
elsif ($line =~ m/VLPROF arg\s+(\S+)\+([0-9.])\s*$/
|
||||
|| $line =~ m/VLPROF arg\s+(\S+)\s+([0-9.])\s*$/) {
|
||||
$Global{args}{$1} = $2;
|
||||
}
|
||||
elsif ($line =~ m/VLPROF stat\s+(\S+)\s+([0-9.]+)/) {
|
||||
$Global{stats}{$1} = $2;
|
||||
}
|
||||
elsif ($line =~ /^#/) {}
|
||||
elsif ($Debug) {
|
||||
chomp $line;
|
||||
print "Unk: $line\n";
|
||||
}
|
||||
# TODO -- this is parsing text printed by a client.
|
||||
# Really, verilator proper should generate this
|
||||
# if it's useful...
|
||||
if ($line =~ m/rdtsc time = (\d+) ticks/) {
|
||||
$Global{rdtsc_cycle_time} = $1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub report {
|
||||
print "Verilator Gantt report\n";
|
||||
|
||||
print "\nArgument settings:\n";
|
||||
foreach my $arg (sort keys %{$Global{args}}) {
|
||||
my $plus = ($arg =~ /^\+/) ? "+" : " ";
|
||||
printf " %s%s%d\n", $arg, $plus, $Global{args}{$arg};
|
||||
}
|
||||
|
||||
my $nthreads = scalar keys %Threads;
|
||||
$Global{cpus}{cpu_time} = {};
|
||||
foreach my $thread (keys %Threads) {
|
||||
# Make potentially multiple characters per column
|
||||
foreach my $start (keys %{$Threads{$thread}}) {
|
||||
my $cpu = $Threads{$thread}{$start}{cpu};
|
||||
my $elapsed = $Threads{$thread}{$start}{end} - $start;
|
||||
$Global{cpus}{cpu_time}{$cpu} += $elapsed;
|
||||
}
|
||||
}
|
||||
|
||||
my $mt_mtask_time = 0;
|
||||
my $long_mtask_time = 0;
|
||||
my $last_end = 0;
|
||||
foreach my $mtask (keys %Mtasks) {
|
||||
$mt_mtask_time += $Mtasks{$mtask}{elapsed};
|
||||
$last_end = max($last_end, $Mtasks{$mtask}{end});
|
||||
$long_mtask_time = max($long_mtask_time, $Mtasks{$mtask}{elapsed});
|
||||
}
|
||||
$Global{last_end} = $last_end;
|
||||
|
||||
report_graph();
|
||||
|
||||
# If we know cycle time in the same (rdtsc) units,
|
||||
# this will give us an actual utilization number,
|
||||
# (how effectively we keep the cores busy.)
|
||||
#
|
||||
# It also gives us a number we can compare against
|
||||
# serial mode, to estimate the overhead of data sharing,
|
||||
# which will show up in the total elapsed time. (Overhead
|
||||
# of synchronization and scheduling should not.)
|
||||
print "\nAnalysis:\n";
|
||||
printf " Total threads = %d\n", $nthreads;
|
||||
printf " Total mtasks = %d\n", scalar(keys %Mtasks);
|
||||
printf " Total cpus used = %d\n", scalar(keys %{$Global{cpus}});
|
||||
printf " Total yields = %d\n", $Global{stats}{yields};
|
||||
printf " Total eval time = %d rdtsc ticks\n", $Global{last_end};
|
||||
printf " Longest mtask time = %d rdtsc ticks\n", $long_mtask_time;
|
||||
printf " All-thread mtask time = %d rdtsc ticks\n", $mt_mtask_time;
|
||||
my $long_efficiency = $long_mtask_time/($Global{last_end});
|
||||
printf " Longest-thread efficiency = %0.1f%%\n", $long_efficiency*100;
|
||||
my $mt_efficiency = $mt_mtask_time/($Global{last_end}*$nthreads);
|
||||
printf " All-thread efficiency = %0.1f%%\n", $mt_efficiency*100;
|
||||
printf " All-thread speedup = %0.1f\n", $mt_efficiency*$nthreads;
|
||||
if ($Global{rdtsc_cycle_time} > 0) {
|
||||
my $ut = $mt_mtask_time / $Global{rdtsc_cycle_time};
|
||||
print "tot_mtask_cpu=$mt_mtask_time cyc=$Global{rdtsc_cycle_time} ut=$ut\n";
|
||||
}
|
||||
|
||||
my @p2e_ratios;
|
||||
my $min_p2e = 1000000;
|
||||
my $min_mtask;
|
||||
my $max_p2e = -1000000;
|
||||
my $max_mtask;
|
||||
foreach my $mtask (sort keys %Mtasks) {
|
||||
if ($Mtasks{$mtask}{elapsed} > 0) {
|
||||
if ($Mtasks{$mtask}{predict} == 0) {
|
||||
$Mtasks{$mtask}{predict} = 1; # don't log(0) below
|
||||
}
|
||||
my $p2e_ratio = log( $Mtasks{$mtask}{predict} / $Mtasks{$mtask}{elapsed} );
|
||||
#print "log(p2e $mtask) = $p2e_ratio (predict $Mtasks{$mtask}{predict}, elapsed $Mtasks{$mtask}{elapsed})\n";
|
||||
push @p2e_ratios, $p2e_ratio;
|
||||
|
||||
if ($p2e_ratio > $max_p2e) {
|
||||
$max_p2e = $p2e_ratio;
|
||||
$max_mtask = $mtask;
|
||||
}
|
||||
if ($p2e_ratio < $min_p2e) {
|
||||
$min_p2e = $p2e_ratio;
|
||||
$min_mtask = $mtask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print "\nStatistics:\n";
|
||||
print " min log(p2e) = $min_p2e from mtask $min_mtask (predict $Mtasks{$min_mtask}{predict}, elapsed $Mtasks{$min_mtask}{elapsed})\n";
|
||||
print " max log(p2e) = $max_p2e from mtask $max_mtask (predict $Mtasks{$max_mtask}{predict}, elapsed $Mtasks{$max_mtask}{elapsed})\n";
|
||||
|
||||
my $stddev = stddev(\@p2e_ratios);
|
||||
my $mean = mean(\@p2e_ratios);
|
||||
print " mean = " . ($mean) . "\n";
|
||||
print " stddev = " . ($stddev) . "\n";
|
||||
print " e ^ stddev = " . exp($stddev). "\n";
|
||||
print "\n";
|
||||
}
|
||||
|
||||
sub report_graph {
|
||||
my $time_per = $Opt_Time_Per_Char;
|
||||
if ($time_per == 0) {
|
||||
$time_per = ($Global{last_end} / 40); # Start with 40 columns
|
||||
while ($time_per > 10) {
|
||||
my ($graph, $conflicts) = _make_graph($time_per);
|
||||
last if !$conflicts;
|
||||
$time_per = int($time_per/2);
|
||||
}
|
||||
# One more step so we can fit more labels
|
||||
$time_per = int($time_per/2);
|
||||
}
|
||||
|
||||
my ($graph, $conflicts) = _make_graph($time_per);
|
||||
|
||||
print "\nThread gantt graph:\n";
|
||||
print " Legend: One character width = $time_per rdtsc ticks\n";
|
||||
print " Legend: '&' = multiple mtasks in this period (character width)\n";
|
||||
|
||||
my $scale = " <-".$Global{last_end}." rdtsc total";
|
||||
for (my $col = length($scale); # -2 for '->' below
|
||||
$col < ($Global{last_end}/$time_per); ++$col) {
|
||||
$scale .= "-";
|
||||
}
|
||||
print " $scale->\n";
|
||||
|
||||
foreach my $thread (sort keys %{$graph}) {
|
||||
print " t: ";
|
||||
_print_graph_line($graph->{$thread}, '');
|
||||
}
|
||||
}
|
||||
|
||||
sub _make_graph {
|
||||
my $time_per = shift;
|
||||
|
||||
my $graph = {}; # {thread}{column}{char=>'x' or chars=>#}
|
||||
my $conflicts = 0;
|
||||
foreach my $thread (keys %Threads) {
|
||||
# Make potentially multiple characters per column
|
||||
foreach my $start (sort {$a <=> $b} keys %{$Threads{$thread}}) {
|
||||
my $end = $Threads{$thread}{$start}{end};
|
||||
my $mtask = $Threads{$thread}{$start}{mtask};
|
||||
my $cpu = $Threads{$thread}{$start}{cpu};
|
||||
|
||||
my $startcol = _time_col($time_per, $start);
|
||||
my $endcol = _time_col($time_per, $end);
|
||||
|
||||
my $label = "[";
|
||||
$label .= "$cpu"; # Maybe make optional in future
|
||||
my $width = $endcol - $startcol + 1;
|
||||
while (length($label) < ($width-1)) { # -1 for ']'
|
||||
$label .= "-";
|
||||
}
|
||||
$label .= "]";
|
||||
$graph->{$thread}[$startcol]{char} .= $label;
|
||||
}
|
||||
if ($Debug) {
|
||||
print "# Multicol: "; _print_graph_line($graph->{$thread}, '|');
|
||||
}
|
||||
# Expand line to one char per column
|
||||
for (my $col = 0; $col <= $#{$graph->{$thread}}; ++$col) {
|
||||
if (my $chars = $graph->{$thread}[$col]{char}) {
|
||||
my $ok = 1;
|
||||
for (my $coladd = 1; $coladd<length($chars); ++$coladd) {
|
||||
if ($graph->{$thread}[$col + $coladd]{char}) {
|
||||
$ok = 0; last;
|
||||
}
|
||||
}
|
||||
if (!$ok) {
|
||||
if ($chars =~ /\[.*\[/) { # Two begins or more
|
||||
$conflicts++;
|
||||
$graph->{$thread}[$col]{char} = "&";
|
||||
} else {
|
||||
$graph->{$thread}[$col]{char} = "[";
|
||||
}
|
||||
for (my $coladd = 1; $coladd<length($chars); ++$coladd) {
|
||||
if ($graph->{$thread}[$col + $coladd]{char}) {
|
||||
last;
|
||||
} else {
|
||||
$graph->{$thread}[$col + $coladd]{char} = 'x';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
my $coladd = 0;
|
||||
foreach my $char (split //, $chars) {
|
||||
$graph->{$thread}[$col+$coladd]{char} = $char;
|
||||
++$coladd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($Debug) {
|
||||
print "# Singlcol: "; _print_graph_line($graph->{$thread}, '|');
|
||||
}
|
||||
}
|
||||
print "# Conflicts $conflicts\n" if $Debug;
|
||||
return ($graph, $conflicts);
|
||||
}
|
||||
|
||||
sub _print_graph_line {
|
||||
my $graph_thread = shift;
|
||||
my $sep = shift;
|
||||
for (my $col = 0; $col <= $#{$graph_thread}; ++$col) {
|
||||
my $c = $graph_thread->[$col]{char}; $c=' ' if !defined $c;
|
||||
print $c, $sep;
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
||||
sub _time_col {
|
||||
my $time_per = shift;
|
||||
my $time = shift;
|
||||
return int($time/$time_per);
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
|
||||
sub write_vcd {
|
||||
my $filename = shift;
|
||||
print "Writing $filename\n";
|
||||
my $fh = IO::File->new(">$filename") or die "%Error: $! $filename,";
|
||||
my $vcd = {values => {}, # {<time>}{<code>} = value
|
||||
sigs => {}, # {<module>}{<sig}} = code
|
||||
code => 0,
|
||||
};
|
||||
|
||||
my %parallelism;
|
||||
foreach my $thread (keys %Threads) {
|
||||
my $mcode = ($vcd->{sigs}{threads}{"thread${thread}_mtask"} ||= $vcd->{code}++);
|
||||
foreach my $start (sort {$a <=> $b} keys %{$Threads{$thread}}) {
|
||||
my $end = $Threads{$thread}{$start}{end};
|
||||
my $mtask = $Threads{$thread}{$start}{mtask};
|
||||
my $cpu = $Threads{$thread}{$start}{cpu};
|
||||
$vcd->{values}{$start}{$mcode} = $mtask;
|
||||
$vcd->{values}{$end}{$mcode} = undef;
|
||||
$parallelism{$start}++;
|
||||
$parallelism{$end}--;
|
||||
|
||||
my $ccode = $vcd->{sigs}{cpus}{"cpu${cpu}_thread"} ||= $vcd->{code}++;
|
||||
$vcd->{values}{$start}{$ccode} = $thread;
|
||||
$vcd->{values}{$end}{$ccode} = undef;
|
||||
|
||||
my $mcode = $vcd->{sigs}{mtasks}{"mtask${mtask}_cpu"} ||= $vcd->{code}++;
|
||||
$vcd->{values}{$start}{$mcode} = $cpu;
|
||||
$vcd->{values}{$end}{$mcode} = undef;
|
||||
}
|
||||
}
|
||||
{
|
||||
my $pcode = ($vcd->{sigs}{Stats}{"parallelism"} ||= $vcd->{code}++);
|
||||
my $value = 0;
|
||||
foreach my $time (sort {$a<=>$b} keys %parallelism) {
|
||||
$value += $parallelism{$time};
|
||||
$vcd->{values}{$time}{$pcode} = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$fh->print('$version Generated by verilator_gantt $end'."\n");
|
||||
$fh->print('$timescale 1ns $end'."\n");
|
||||
$fh->print("\n");
|
||||
|
||||
my %all_codes;
|
||||
$fh->print(' $scope module gantt $end'."\n");
|
||||
foreach my $module (sort keys %{$vcd->{sigs}}) {
|
||||
$fh->printf(' $scope module %s $end'."\n", $module);
|
||||
foreach my $sig (sort keys %{$vcd->{sigs}{$module}}) {
|
||||
my $code = $vcd->{sigs}{$module}{$sig};
|
||||
$fh->printf(' $var wire 32 v%x %s [31:0] $end'."\n",
|
||||
$code, $sig);
|
||||
$all_codes{$code} = 1;
|
||||
}
|
||||
$fh->print(' $upscope $end'."\n");
|
||||
}
|
||||
$fh->print(' $upscope $end'."\n");
|
||||
$fh->print('$enddefinitions $end'."\n");
|
||||
$fh->print("\n");
|
||||
|
||||
my $first = 1;
|
||||
foreach my $time (sort {$a <=> $b} keys %{$vcd->{values}}) {
|
||||
if ($first) {
|
||||
$first = 0;
|
||||
# Start with Z for any signals without time zero data
|
||||
foreach my $code (keys %all_codes) {
|
||||
if (!defined $vcd->{values}{$time}{$code}) {
|
||||
$vcd->{values}{$time}{$code} = undef;
|
||||
}
|
||||
}
|
||||
}
|
||||
$fh->printf("#%d\n", $time);
|
||||
foreach my $code (sort keys %{$vcd->{values}{$time}}) {
|
||||
my $value = $vcd->{values}{$time}{$code};
|
||||
if (defined $value) {
|
||||
$fh->printf("b%b v%x\n", $value, $code);
|
||||
} else {
|
||||
$fh->printf("bz v%x\n", $code);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
# Similar to Statistics::Basic functions, but avoid a package dependency
|
||||
|
||||
sub max {
|
||||
my $n = $_[0]; shift;
|
||||
while (defined $_[0]) {
|
||||
$n = $_[0] if !defined $n || $_[0] > $n;
|
||||
shift;
|
||||
}
|
||||
return $n;
|
||||
}
|
||||
|
||||
sub mean {
|
||||
my $arrayref = shift;
|
||||
my $n = 0;
|
||||
my $sum = 0;
|
||||
foreach my $v (@$arrayref) {
|
||||
$sum += $v;
|
||||
$n++;
|
||||
}
|
||||
return undef if !$n;
|
||||
return $sum/$n;
|
||||
}
|
||||
|
||||
sub stddev {
|
||||
my $arrayref = shift;
|
||||
my $n = 0;
|
||||
my $sum = 0;
|
||||
my $sumsq = 0;
|
||||
foreach my $v (@$arrayref) {
|
||||
$sum += $v;
|
||||
$sumsq += $v**2;
|
||||
$n++;
|
||||
}
|
||||
return undef if !$n;
|
||||
return sqrt(($sumsq/$n) - ($sum/$n)**2);
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
verilator_gantt - Create Gantt chart of multi-threaded execution
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Creates a visual representation to help analyze Verilator multithreaded
|
||||
simulation performance, by showing when each macro-task starts and ends,
|
||||
and showing when each thread is busy or idle.
|
||||
|
||||
The generated Gantt chart has time on the X-axis. Times shown are to the
|
||||
scale printed, i.e. a certain about of time for each character width. The
|
||||
Y-axis shows threads, each thread's execution is shown on one line. That
|
||||
line shows "[" at the position in time when it executes.
|
||||
|
||||
Following the "[" is the cpu number the task executed on, followed by zero
|
||||
or more "-" to make the width of the characters match the scaled execution
|
||||
time, followed by a "]". If the scale is too small, the cpu number and
|
||||
mtask number will not be printed. If the scale is very small, a "&"
|
||||
indicates multiple mtasks started at that time position.
|
||||
|
||||
Also creates a value change dump (VCD) format dump file which may be viewed
|
||||
in a waveform viewer (e.g. C<GTKWave>). See below.
|
||||
|
||||
=head1 USAGE
|
||||
|
||||
Build with --prof-threads.
|
||||
|
||||
Run a sim with +verilator+prof+threads+window 2.
|
||||
|
||||
This will create profile_threads.dat.
|
||||
|
||||
Then run:
|
||||
|
||||
verilator_gantt profile_threads.dat
|
||||
|
||||
The report will be printed on standard output, this also generates
|
||||
profile_threads.vcd
|
||||
|
||||
View profile_threads.vcd in a waveform viewer.
|
||||
|
||||
=head1 VCD SIGNALS
|
||||
|
||||
In waveforms there are the following signals. Most signals the "decimal"
|
||||
format will remove the leading zeros and make the traces easier to read.
|
||||
|
||||
parallelism: The number of mtasks active at this time, for best performance
|
||||
this will match the thread count. You may want to use an "analog step"
|
||||
format to view this signal.
|
||||
|
||||
cpu#_thread: For the given CPU number, the thread number executing.
|
||||
|
||||
mtask#_cpu; For the given mtask id, the CPU it is executing on.
|
||||
|
||||
thread#_mtask: For the given thread number, the mtask id executing.
|
||||
|
||||
=head1 ARGUMENTS
|
||||
|
||||
=over 4
|
||||
|
||||
=item I<filename>
|
||||
|
||||
The filename to read data from, defaults to "profile_threads.dat".
|
||||
|
||||
=item --help
|
||||
|
||||
Displays this message and program version and exits.
|
||||
|
||||
=item --scale I<n>
|
||||
|
||||
On the X-axis of the generated Gantt chart, each character represents this
|
||||
many time units. (On x86, time units are rdtsc ticks.) Defaults to 0,
|
||||
which will automatically compute a reasonable scale where no two mtasks
|
||||
need to fit into same character width's worth of scaled time.
|
||||
|
||||
=item --no-vcd
|
||||
|
||||
=item --vcd I<filename>
|
||||
|
||||
Set output filename for vcd dump, or disable. Default is
|
||||
verilator_gantt.vcd.
|
||||
|
||||
=back
|
||||
|
||||
=head1 DISTRIBUTION
|
||||
|
||||
The latest version is available from L<http://www.veripool.org/>.
|
||||
|
||||
Copyright 2018-2018 by Wilson Snyder. Verilator is free software; you can
|
||||
redistribute it and/or modify it under the terms of either the GNU Lesser
|
||||
General Public License Version 3 or the Perl Artistic License Version 2.0.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Wilson Snyder <[email protected]>
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
C<verilator>
|
||||
|
||||
=cut
|
||||
|
||||
######################################################################
|
||||
### Local Variables:
|
||||
### compile-command: "$V4/bin/verilator_gantt $V4/test_regress/obj_vltmt/t_gantt/vlt_sim.log"
|
||||
### End:
|
||||
+85
-9
@@ -6,7 +6,9 @@
|
||||
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[3.924 2018-06-12])
|
||||
AC_INIT([Verilator],[4.006 2018-10-27],
|
||||
[https://www.veripool.org/verilator],
|
||||
[verilator],[https://www.veripool.org/verilator])
|
||||
# When releasing, also update header of Changes file
|
||||
# and commit using "devel release" or "Version bump" message
|
||||
|
||||
@@ -15,6 +17,14 @@ AC_CONFIG_FILES(Makefile src/Makefile src/Makefile_obj include/verilated.mk incl
|
||||
|
||||
AC_MSG_RESULT([configuring for $PACKAGE_STRING])
|
||||
|
||||
# Ignore automake flags passed by Ubuntu builds
|
||||
AC_ARG_ENABLE([dependency-tracking],
|
||||
[AS_HELP_STRING([--disable-dependency-tracking], [ignored])])
|
||||
AC_ARG_ENABLE([maintainer-mode],
|
||||
[AS_HELP_STRING([--enable-maintainer-mode], [ignored])])
|
||||
AC_ARG_ENABLE([silent-rules],
|
||||
[AS_HELP_STRING([--disable-silent-rules], [ignored])])
|
||||
|
||||
# Special Substitutions - CFG_WITH_DEFENV
|
||||
AC_MSG_CHECKING(whether to use hardcoded paths)
|
||||
AC_ARG_ENABLE([defenv],
|
||||
@@ -65,6 +75,21 @@ AC_ARG_ENABLE([longtests],
|
||||
AC_SUBST(CFG_WITH_LONGTESTS)
|
||||
AC_MSG_RESULT($CFG_WITH_LONGTESTS)
|
||||
|
||||
# CFG_WITH_PREC11
|
||||
AC_MSG_CHECKING(whether allow pre-C++11)
|
||||
AC_ARG_ENABLE([prec11],
|
||||
[AS_HELP_STRING([--enable-prec11],
|
||||
[enable pre-C++11 compilers])],
|
||||
[case "${enableval}" in
|
||||
yes) CFG_WITH_PREC11=yes ;;
|
||||
no) CFG_WITH_PREC11=no ;;
|
||||
*) AC_MSG_ERROR([bad value ${enableval} for --enable-prec11]) ;;
|
||||
esac],
|
||||
[CFG_WITH_PREC11=no;]
|
||||
)
|
||||
AC_SUBST(CFG_WITH_PREC11)
|
||||
AC_MSG_RESULT($CFG_WITH_PREC11)
|
||||
|
||||
# Compiler flags
|
||||
CFLAGS=-I${includedir}
|
||||
CPPFLAGS=-I${includedir}
|
||||
@@ -165,29 +190,58 @@ AC_DEFUN([_MY_CXX_CHECK_OPT],
|
||||
fi
|
||||
])
|
||||
|
||||
AC_DEFUN([_MY_LDLIBS_CHECK_FLAG],
|
||||
[# _MY_LDLIBS_CHECK_FLAG(flag) -- Check if linker supports specific options
|
||||
# Set $_my_result appropriately
|
||||
ACO_SAVE_LIBS="$LIBS"
|
||||
LIBS="$LIBS $1"
|
||||
AC_MSG_CHECKING([whether $CXX linker accepts $1])
|
||||
AC_LINK_IFELSE(
|
||||
[AC_LANG_PROGRAM([[]])],
|
||||
[_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])
|
||||
AC_MSG_RESULT($_my_result)
|
||||
LIBS="$ACO_SAVE_LIBS"
|
||||
])
|
||||
|
||||
AC_DEFUN([_MY_LDLIBS_CHECK_OPT],
|
||||
[# _MY_LDLIBS_CHECK_OPT(flag) -- Check if linker supports specific options
|
||||
# If it does, append flag to variable
|
||||
_MY_LDLIBS_CHECK_FLAG($2)
|
||||
if test "$_my_result" = "yes" ; then
|
||||
$1="$$1 $2"
|
||||
fi
|
||||
])
|
||||
|
||||
# Flag to select newest language standard supported
|
||||
# Macros work such that first option that passes is the one we take
|
||||
# gnu++17 code is clean, but SystemC in 2018 doesn't link with it (bug1339)
|
||||
# gnu++14 is the newest that Verilator supports
|
||||
# std++03 is the oldest that Verilator supports
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++17)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++17)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++14)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++11)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++03)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++17)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++17)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++14)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++11)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++03)
|
||||
AC_SUBST(CFG_CXXFLAGS_STD_NEWEST)
|
||||
# And likewise oldest standard (same list above, backwards)
|
||||
# This is used for internal testing
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=std++03)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=std++11)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=std++14)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=std++17)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++03)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++11)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++14)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=std++17)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++03)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++11)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++14)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++17)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++17)
|
||||
AC_SUBST(CFG_CXXFLAGS_STD_OLDEST)
|
||||
|
||||
# Flags for compiling Verilator internals including parser, and Verilated files
|
||||
@@ -201,7 +255,6 @@ AC_SUBST(CFG_CXXFLAGS_WEXTRA)
|
||||
# Flags for compiling Verilator internals including parser always
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Qunused-arguments)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-faligned-new)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-delete-null-pointer-checks)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Wno-unused-parameter)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Wno-undefined-bool-conversion)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Wno-shadow)
|
||||
@@ -233,6 +286,13 @@ _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)
|
||||
|
||||
# Find multithread linker flags
|
||||
_MY_LDLIBS_CHECK_OPT(CFG_LDLIBS_THREADS,-mt)
|
||||
_MY_LDLIBS_CHECK_OPT(CFG_LDLIBS_THREADS,-pthread)
|
||||
_MY_LDLIBS_CHECK_OPT(CFG_LDLIBS_THREADS,-lpthread)
|
||||
_MY_LDLIBS_CHECK_OPT(CFG_LDLIBS_THREADS,-latomic)
|
||||
AC_SUBST(CFG_LDLIBS_THREADS)
|
||||
|
||||
# Set CFG_WITH_THREADED if can support threading
|
||||
AC_MSG_CHECKING(whether $CXX supports Verilated threads)
|
||||
ACO_SAVE_CXXFLAGS="$CXXFLAGS"
|
||||
@@ -250,6 +310,22 @@ CFG_WITH_THREADED=$_my_result
|
||||
AC_SUBST(CFG_WITH_THREADED)
|
||||
AC_MSG_RESULT($CFG_WITH_THREADED)
|
||||
|
||||
# Check compiler flag
|
||||
if test "$CFG_WITH_THREADED" = "no" ; then
|
||||
if test "$CFG_WITH_PREC11" = "no" ; then
|
||||
AC_MSG_NOTICE([[]])
|
||||
AC_MSG_ERROR([[the $CXX compiler appears to not support C++11.
|
||||
|
||||
Verilator plans to require a C++11 or newer compiler in a future release,
|
||||
unless sufficient people report problems. Therefore, if you do not have a
|
||||
C++11 compiler, please post a message to
|
||||
https://www.veripool.org/boards/3/topics/2580-Verilator-Requiring-C-11-compiler
|
||||
indicating your OS and when you think C++11 might be ok, and then rerun
|
||||
configure with the --enable-prec11 argument. Thanks.]])
|
||||
|
||||
fi
|
||||
fi
|
||||
|
||||
# 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])],
|
||||
|
||||
+1
-5
@@ -630,12 +630,8 @@ WARN_LOGFILE =
|
||||
# with spaces.
|
||||
|
||||
INPUT = doxygen-mainpage \
|
||||
include \
|
||||
include \
|
||||
src \
|
||||
test_c \
|
||||
test_regress \
|
||||
test_sc \
|
||||
test_v
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
|
||||
|
||||
@@ -17,6 +17,9 @@ default: Vtop
|
||||
# Include the rules made by Verilator
|
||||
include Vtop.mk
|
||||
|
||||
# Use OBJCACHE (ccache) if using gmake and its installed
|
||||
COMPILE.cc = $(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
|
||||
|
||||
#######################################################################
|
||||
# Compile flags
|
||||
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
// Include common routines
|
||||
#include <verilated.h>
|
||||
|
||||
#include <sys/stat.h> // mkdir
|
||||
|
||||
// Include model header, generated from Verilating "top.v"
|
||||
#include "Vtop.h"
|
||||
|
||||
@@ -20,8 +18,8 @@
|
||||
// Current simulation time (64-bit unsigned)
|
||||
vluint64_t main_time = 0;
|
||||
// Called by $time in Verilog
|
||||
double sc_time_stamp () {
|
||||
return main_time; // Note does conversion to real, to match SystemC
|
||||
double sc_time_stamp() {
|
||||
return main_time; // Note does conversion to real, to match SystemC
|
||||
}
|
||||
|
||||
int main(int argc, char** argv, char** env) {
|
||||
@@ -51,7 +49,7 @@ int main(int argc, char** argv, char** env) {
|
||||
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);
|
||||
Verilated::mkdir("logs");
|
||||
tfp->open("logs/vlt_dump.vcd"); // Open the dump file
|
||||
}
|
||||
#endif
|
||||
@@ -112,7 +110,7 @@ int main(int argc, char** argv, char** env) {
|
||||
|
||||
// Coverage analysis (since test passed)
|
||||
#if VM_COVERAGE
|
||||
mkdir("logs", 0777);
|
||||
Verilated::mkdir("logs");
|
||||
VerilatedCov::write("logs/coverage.dat");
|
||||
#endif
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@ default: Vtop
|
||||
# Include the rules made by Verilator
|
||||
include Vtop.mk
|
||||
|
||||
# Use OBJCACHE (ccache) if using gmake and its installed
|
||||
COMPILE.cc = $(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
|
||||
|
||||
#######################################################################
|
||||
# Compile flags
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ int sc_main(int argc, char* argv[]) {
|
||||
cout << "Enabling waves into logs/vlt_dump.vcd...\n";
|
||||
tfp = new VerilatedVcdSc;
|
||||
top->trace(tfp, 99);
|
||||
mkdir("logs", 0777);
|
||||
Verilated::mkdir("logs");
|
||||
tfp->open("logs/vlt_dump.vcd");
|
||||
}
|
||||
#endif
|
||||
@@ -134,7 +134,7 @@ int sc_main(int argc, char* argv[]) {
|
||||
|
||||
// Coverage analysis (since test passed)
|
||||
#if VM_COVERAGE
|
||||
mkdir("logs", 0777);
|
||||
Verilated::mkdir("logs");
|
||||
VerilatedCov::write("logs/coverage.dat");
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,547 @@
|
||||
/*
|
||||
FastLZ - lightning-fast lossless compression library
|
||||
|
||||
Copyright (C) 2007 Ariya Hidayat ([email protected])
|
||||
Copyright (C) 2006 Ariya Hidayat ([email protected])
|
||||
Copyright (C) 2005 Ariya Hidayat ([email protected])
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "fastlz.h"
|
||||
|
||||
#if !defined(FASTLZ__COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR)
|
||||
|
||||
/*
|
||||
* Always check for bound when decompressing.
|
||||
* Generally it is best to leave it defined.
|
||||
*/
|
||||
#define FASTLZ_SAFE
|
||||
|
||||
|
||||
/*
|
||||
* Give hints to the compiler for branch prediction optimization.
|
||||
*/
|
||||
#if defined(__GNUC__) && (__GNUC__ > 2)
|
||||
#define FASTLZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1))
|
||||
#define FASTLZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0))
|
||||
#else
|
||||
#define FASTLZ_EXPECT_CONDITIONAL(c) (c)
|
||||
#define FASTLZ_UNEXPECT_CONDITIONAL(c) (c)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Use inlined functions for supported systems.
|
||||
*/
|
||||
#if defined(__GNUC__) || defined(__DMC__) || defined(__POCC__) || defined(__WATCOMC__) || defined(__SUNPRO_C)
|
||||
#define FASTLZ_INLINE inline
|
||||
#elif defined(__BORLANDC__) || defined(_MSC_VER) || defined(__LCC__)
|
||||
#define FASTLZ_INLINE __inline
|
||||
#else
|
||||
#define FASTLZ_INLINE
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Prevent accessing more than 8-bit at once, except on x86 architectures.
|
||||
*/
|
||||
#if !defined(FASTLZ_STRICT_ALIGN)
|
||||
#define FASTLZ_STRICT_ALIGN
|
||||
#if defined(__i386__) || defined(__386) /* GNU C, Sun Studio */
|
||||
#undef FASTLZ_STRICT_ALIGN
|
||||
#elif defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(__amd64) /* GNU C */
|
||||
#undef FASTLZ_STRICT_ALIGN
|
||||
#elif defined(_M_IX86) /* Intel, MSVC */
|
||||
#undef FASTLZ_STRICT_ALIGN
|
||||
#elif defined(__386)
|
||||
#undef FASTLZ_STRICT_ALIGN
|
||||
#elif defined(_X86_) /* MinGW */
|
||||
#undef FASTLZ_STRICT_ALIGN
|
||||
#elif defined(__I86__) /* Digital Mars */
|
||||
#undef FASTLZ_STRICT_ALIGN
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* prototypes */
|
||||
int fastlz_compress(const void* input, int length, void* output);
|
||||
int fastlz_compress_level(int level, const void* input, int length, void* output);
|
||||
int fastlz_decompress(const void* input, int length, void* output, int maxout);
|
||||
|
||||
#define MAX_COPY 32
|
||||
#define MAX_LEN 264 /* 256 + 8 */
|
||||
#define MAX_DISTANCE 8192
|
||||
|
||||
#if !defined(FASTLZ_STRICT_ALIGN)
|
||||
#define FASTLZ_READU16(p) *((const flzuint16*)(p))
|
||||
#else
|
||||
#define FASTLZ_READU16(p) ((p)[0] | (p)[1]<<8)
|
||||
#endif
|
||||
|
||||
#define HASH_LOG 13
|
||||
#define HASH_SIZE (1<< HASH_LOG)
|
||||
#define HASH_MASK (HASH_SIZE-1)
|
||||
#define HASH_FUNCTION(v,p) { v = FASTLZ_READU16(p); v ^= FASTLZ_READU16(p+1)^(v>>(16-HASH_LOG));v &= HASH_MASK; }
|
||||
|
||||
#undef FASTLZ_LEVEL
|
||||
#define FASTLZ_LEVEL 1
|
||||
|
||||
#undef FASTLZ_COMPRESSOR
|
||||
#undef FASTLZ_DECOMPRESSOR
|
||||
#define FASTLZ_COMPRESSOR fastlz1_compress
|
||||
#define FASTLZ_DECOMPRESSOR fastlz1_decompress
|
||||
static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output);
|
||||
static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout);
|
||||
#include "fastlz.c"
|
||||
|
||||
#undef FASTLZ_LEVEL
|
||||
#define FASTLZ_LEVEL 2
|
||||
|
||||
#undef MAX_DISTANCE
|
||||
#define MAX_DISTANCE 8191
|
||||
#define MAX_FARDISTANCE (65535+MAX_DISTANCE-1)
|
||||
|
||||
#undef FASTLZ_COMPRESSOR
|
||||
#undef FASTLZ_DECOMPRESSOR
|
||||
#define FASTLZ_COMPRESSOR fastlz2_compress
|
||||
#define FASTLZ_DECOMPRESSOR fastlz2_decompress
|
||||
static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output);
|
||||
static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout);
|
||||
#include "fastlz.c"
|
||||
|
||||
int fastlz_compress(const void* input, int length, void* output)
|
||||
{
|
||||
/* for short block, choose fastlz1 */
|
||||
if(length < 65536)
|
||||
return fastlz1_compress(input, length, output);
|
||||
|
||||
/* else... */
|
||||
return fastlz2_compress(input, length, output);
|
||||
}
|
||||
|
||||
int fastlz_decompress(const void* input, int length, void* output, int maxout)
|
||||
{
|
||||
/* magic identifier for compression level */
|
||||
int level = ((*(const flzuint8*)input) >> 5) + 1;
|
||||
|
||||
if(level == 1)
|
||||
return fastlz1_decompress(input, length, output, maxout);
|
||||
if(level == 2)
|
||||
return fastlz2_decompress(input, length, output, maxout);
|
||||
|
||||
/* unknown level, trigger error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fastlz_compress_level(int level, const void* input, int length, void* output)
|
||||
{
|
||||
if(level == 1)
|
||||
return fastlz1_compress(input, length, output);
|
||||
if(level == 2)
|
||||
return fastlz2_compress(input, length, output);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */
|
||||
|
||||
static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output)
|
||||
{
|
||||
const flzuint8* ip = (const flzuint8*) input;
|
||||
const flzuint8* ip_bound = ip + length - 2;
|
||||
const flzuint8* ip_limit = ip + length - 12;
|
||||
flzuint8* op = (flzuint8*) output;
|
||||
|
||||
const flzuint8* htab[HASH_SIZE];
|
||||
const flzuint8** hslot;
|
||||
flzuint32 hval;
|
||||
|
||||
flzuint32 copy;
|
||||
|
||||
/* sanity check */
|
||||
if(FASTLZ_UNEXPECT_CONDITIONAL(length < 4))
|
||||
{
|
||||
if(length)
|
||||
{
|
||||
/* create literal copy only */
|
||||
*op++ = length-1;
|
||||
ip_bound++;
|
||||
while(ip <= ip_bound)
|
||||
*op++ = *ip++;
|
||||
return length+1;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* initializes hash table */
|
||||
for (hslot = htab; hslot < htab + HASH_SIZE; hslot++)
|
||||
*hslot = ip;
|
||||
|
||||
/* we start with literal copy */
|
||||
copy = 2;
|
||||
*op++ = MAX_COPY-1;
|
||||
*op++ = *ip++;
|
||||
*op++ = *ip++;
|
||||
|
||||
/* main loop */
|
||||
while(FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit))
|
||||
{
|
||||
const flzuint8* ref;
|
||||
flzuint32 distance;
|
||||
|
||||
/* minimum match length */
|
||||
flzuint32 len = 3;
|
||||
|
||||
/* comparison starting-point */
|
||||
const flzuint8* anchor = ip;
|
||||
|
||||
/* check for a run */
|
||||
#if FASTLZ_LEVEL==2
|
||||
if(ip[0] == ip[-1] && FASTLZ_READU16(ip-1)==FASTLZ_READU16(ip+1))
|
||||
{
|
||||
distance = 1;
|
||||
/* ip += 3; */ /* scan-build, never used */
|
||||
ref = anchor - 1 + 3;
|
||||
goto match;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* find potential match */
|
||||
HASH_FUNCTION(hval,ip);
|
||||
hslot = htab + hval;
|
||||
ref = htab[hval];
|
||||
|
||||
/* calculate distance to the match */
|
||||
distance = anchor - ref;
|
||||
|
||||
/* update hash table */
|
||||
*hslot = anchor;
|
||||
|
||||
/* is this a match? check the first 3 bytes */
|
||||
if(distance==0 ||
|
||||
#if FASTLZ_LEVEL==1
|
||||
(distance >= MAX_DISTANCE) ||
|
||||
#else
|
||||
(distance >= MAX_FARDISTANCE) ||
|
||||
#endif
|
||||
*ref++ != *ip++ || *ref++!=*ip++ || *ref++!=*ip++)
|
||||
goto literal;
|
||||
|
||||
#if FASTLZ_LEVEL==2
|
||||
/* far, needs at least 5-byte match */
|
||||
if(distance >= MAX_DISTANCE)
|
||||
{
|
||||
if(*ip++ != *ref++ || *ip++!= *ref++)
|
||||
goto literal;
|
||||
len += 2;
|
||||
}
|
||||
|
||||
match:
|
||||
#endif
|
||||
|
||||
/* last matched byte */
|
||||
ip = anchor + len;
|
||||
|
||||
/* distance is biased */
|
||||
distance--;
|
||||
|
||||
if(!distance)
|
||||
{
|
||||
/* zero distance means a run */
|
||||
flzuint8 x = ip[-1];
|
||||
while(ip < ip_bound)
|
||||
if(*ref++ != x) break; else ip++;
|
||||
}
|
||||
else
|
||||
for(;;)
|
||||
{
|
||||
/* safe because the outer check against ip limit */
|
||||
if(*ref++ != *ip++) break;
|
||||
if(*ref++ != *ip++) break;
|
||||
if(*ref++ != *ip++) break;
|
||||
if(*ref++ != *ip++) break;
|
||||
if(*ref++ != *ip++) break;
|
||||
if(*ref++ != *ip++) break;
|
||||
if(*ref++ != *ip++) break;
|
||||
if(*ref++ != *ip++) break;
|
||||
while(ip < ip_bound)
|
||||
if(*ref++ != *ip++) break;
|
||||
break;
|
||||
}
|
||||
|
||||
/* if we have copied something, adjust the copy count */
|
||||
if(copy)
|
||||
/* copy is biased, '0' means 1 byte copy */
|
||||
*(op-copy-1) = copy-1;
|
||||
else
|
||||
/* back, to overwrite the copy count */
|
||||
op--;
|
||||
|
||||
/* reset literal counter */
|
||||
copy = 0;
|
||||
|
||||
/* length is biased, '1' means a match of 3 bytes */
|
||||
ip -= 3;
|
||||
len = ip - anchor;
|
||||
|
||||
/* encode the match */
|
||||
#if FASTLZ_LEVEL==2
|
||||
if(distance < MAX_DISTANCE)
|
||||
{
|
||||
if(len < 7)
|
||||
{
|
||||
*op++ = (len << 5) + (distance >> 8);
|
||||
*op++ = (distance & 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
*op++ = (7 << 5) + (distance >> 8);
|
||||
for(len-=7; len >= 255; len-= 255)
|
||||
*op++ = 255;
|
||||
*op++ = len;
|
||||
*op++ = (distance & 255);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* far away, but not yet in the another galaxy... */
|
||||
if(len < 7)
|
||||
{
|
||||
distance -= MAX_DISTANCE;
|
||||
*op++ = (len << 5) + 31;
|
||||
*op++ = 255;
|
||||
*op++ = distance >> 8;
|
||||
*op++ = distance & 255;
|
||||
}
|
||||
else
|
||||
{
|
||||
distance -= MAX_DISTANCE;
|
||||
*op++ = (7 << 5) + 31;
|
||||
for(len-=7; len >= 255; len-= 255)
|
||||
*op++ = 255;
|
||||
*op++ = len;
|
||||
*op++ = 255;
|
||||
*op++ = distance >> 8;
|
||||
*op++ = distance & 255;
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
if(FASTLZ_UNEXPECT_CONDITIONAL(len > MAX_LEN-2))
|
||||
while(len > MAX_LEN-2)
|
||||
{
|
||||
*op++ = (7 << 5) + (distance >> 8);
|
||||
*op++ = MAX_LEN - 2 - 7 -2;
|
||||
*op++ = (distance & 255);
|
||||
len -= MAX_LEN-2;
|
||||
}
|
||||
|
||||
if(len < 7)
|
||||
{
|
||||
*op++ = (len << 5) + (distance >> 8);
|
||||
*op++ = (distance & 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
*op++ = (7 << 5) + (distance >> 8);
|
||||
*op++ = len - 7;
|
||||
*op++ = (distance & 255);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* update the hash at match boundary */
|
||||
HASH_FUNCTION(hval,ip);
|
||||
htab[hval] = ip++;
|
||||
HASH_FUNCTION(hval,ip);
|
||||
htab[hval] = ip++;
|
||||
|
||||
/* assuming literal copy */
|
||||
*op++ = MAX_COPY-1;
|
||||
|
||||
continue;
|
||||
|
||||
literal:
|
||||
*op++ = *anchor++;
|
||||
ip = anchor;
|
||||
copy++;
|
||||
if(FASTLZ_UNEXPECT_CONDITIONAL(copy == MAX_COPY))
|
||||
{
|
||||
copy = 0;
|
||||
*op++ = MAX_COPY-1;
|
||||
}
|
||||
}
|
||||
|
||||
/* left-over as literal copy */
|
||||
ip_bound++;
|
||||
while(ip <= ip_bound)
|
||||
{
|
||||
*op++ = *ip++;
|
||||
copy++;
|
||||
if(copy == MAX_COPY)
|
||||
{
|
||||
copy = 0;
|
||||
*op++ = MAX_COPY-1;
|
||||
}
|
||||
}
|
||||
|
||||
/* if we have copied something, adjust the copy length */
|
||||
if(copy)
|
||||
*(op-copy-1) = copy-1;
|
||||
else
|
||||
op--;
|
||||
|
||||
#if FASTLZ_LEVEL==2
|
||||
/* marker for fastlz2 */
|
||||
*(flzuint8*)output |= (1 << 5);
|
||||
#endif
|
||||
|
||||
return op - (flzuint8*)output;
|
||||
}
|
||||
|
||||
static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout)
|
||||
{
|
||||
const flzuint8* ip = (const flzuint8*) input;
|
||||
const flzuint8* ip_limit = ip + length;
|
||||
flzuint8* op = (flzuint8*) output;
|
||||
flzuint8* op_limit = op + maxout;
|
||||
flzuint32 ctrl = (*ip++) & 31;
|
||||
int loop = 1;
|
||||
|
||||
do
|
||||
{
|
||||
const flzuint8* ref = op;
|
||||
flzuint32 len = ctrl >> 5;
|
||||
flzuint32 ofs = (ctrl & 31) << 8;
|
||||
|
||||
if(ctrl >= 32)
|
||||
{
|
||||
#if FASTLZ_LEVEL==2
|
||||
flzuint8 code;
|
||||
#endif
|
||||
len--;
|
||||
ref -= ofs;
|
||||
if (len == 7-1)
|
||||
#if FASTLZ_LEVEL==1
|
||||
len += *ip++;
|
||||
ref -= *ip++;
|
||||
#else
|
||||
do
|
||||
{
|
||||
code = *ip++;
|
||||
len += code;
|
||||
} while (code==255);
|
||||
code = *ip++;
|
||||
ref -= code;
|
||||
|
||||
/* match from 16-bit distance */
|
||||
if(FASTLZ_UNEXPECT_CONDITIONAL(code==255))
|
||||
if(FASTLZ_EXPECT_CONDITIONAL(ofs==(31 << 8)))
|
||||
{
|
||||
ofs = (*ip++) << 8;
|
||||
ofs += *ip++;
|
||||
ref = op - ofs - MAX_DISTANCE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FASTLZ_SAFE
|
||||
if (FASTLZ_UNEXPECT_CONDITIONAL(op + len + 3 > op_limit))
|
||||
return 0;
|
||||
|
||||
if (FASTLZ_UNEXPECT_CONDITIONAL(ref-1 < (flzuint8 *)output))
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
if(FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit))
|
||||
ctrl = *ip++;
|
||||
else
|
||||
loop = 0;
|
||||
|
||||
if(ref == op)
|
||||
{
|
||||
/* optimize copy for a run */
|
||||
flzuint8 b = ref[-1];
|
||||
*op++ = b;
|
||||
*op++ = b;
|
||||
*op++ = b;
|
||||
for(; len; --len)
|
||||
*op++ = b;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !defined(FASTLZ_STRICT_ALIGN)
|
||||
const flzuint16* p;
|
||||
flzuint16* q;
|
||||
#endif
|
||||
/* copy from reference */
|
||||
ref--;
|
||||
*op++ = *ref++;
|
||||
*op++ = *ref++;
|
||||
*op++ = *ref++;
|
||||
|
||||
#if !defined(FASTLZ_STRICT_ALIGN)
|
||||
/* copy a byte, so that now it's word aligned */
|
||||
if(len & 1)
|
||||
{
|
||||
*op++ = *ref++;
|
||||
len--;
|
||||
}
|
||||
|
||||
/* copy 16-bit at once */
|
||||
q = (flzuint16*) op;
|
||||
op += len;
|
||||
p = (const flzuint16*) ref;
|
||||
for(len>>=1; len > 4; len-=4)
|
||||
{
|
||||
*q++ = *p++;
|
||||
*q++ = *p++;
|
||||
*q++ = *p++;
|
||||
*q++ = *p++;
|
||||
}
|
||||
for(; len; --len)
|
||||
*q++ = *p++;
|
||||
#else
|
||||
for(; len; --len)
|
||||
*op++ = *ref++;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ctrl++;
|
||||
#ifdef FASTLZ_SAFE
|
||||
if (FASTLZ_UNEXPECT_CONDITIONAL(op + ctrl > op_limit))
|
||||
return 0;
|
||||
if (FASTLZ_UNEXPECT_CONDITIONAL(ip + ctrl > ip_limit))
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
*op++ = *ip++;
|
||||
for(--ctrl; ctrl; ctrl--)
|
||||
*op++ = *ip++;
|
||||
|
||||
loop = FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit);
|
||||
if(loop)
|
||||
ctrl = *ip++;
|
||||
}
|
||||
}
|
||||
while(FASTLZ_EXPECT_CONDITIONAL(loop));
|
||||
|
||||
return op - (flzuint8*)output;
|
||||
}
|
||||
|
||||
#endif /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
FastLZ - lightning-fast lossless compression library
|
||||
|
||||
Copyright (C) 2007 Ariya Hidayat ([email protected])
|
||||
Copyright (C) 2006 Ariya Hidayat ([email protected])
|
||||
Copyright (C) 2005 Ariya Hidayat ([email protected])
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef FASTLZ_H
|
||||
#define FASTLZ_H
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define flzuint8 uint8_t
|
||||
#define flzuint16 uint16_t
|
||||
#define flzuint32 uint32_t
|
||||
|
||||
|
||||
#define FASTLZ_VERSION 0x000100
|
||||
|
||||
#define FASTLZ_VERSION_MAJOR 0
|
||||
#define FASTLZ_VERSION_MINOR 0
|
||||
#define FASTLZ_VERSION_REVISION 0
|
||||
|
||||
#define FASTLZ_VERSION_STRING "0.1.0"
|
||||
|
||||
#if defined (__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
Compress a block of data in the input buffer and returns the size of
|
||||
compressed block. The size of input buffer is specified by length. The
|
||||
minimum input buffer size is 16.
|
||||
|
||||
The output buffer must be at least 5% larger than the input buffer
|
||||
and can not be smaller than 66 bytes.
|
||||
|
||||
If the input is not compressible, the return value might be larger than
|
||||
length (input buffer size).
|
||||
|
||||
The input buffer and the output buffer can not overlap.
|
||||
*/
|
||||
|
||||
int fastlz_compress(const void* input, int length, void* output);
|
||||
|
||||
/**
|
||||
Decompress a block of compressed data and returns the size of the
|
||||
decompressed block. If error occurs, e.g. the compressed data is
|
||||
corrupted or the output buffer is not large enough, then 0 (zero)
|
||||
will be returned instead.
|
||||
|
||||
The input buffer and the output buffer can not overlap.
|
||||
|
||||
Decompression is memory safe and guaranteed not to write the output buffer
|
||||
more than what is specified in maxout.
|
||||
*/
|
||||
|
||||
int fastlz_decompress(const void* input, int length, void* output, int maxout);
|
||||
|
||||
/**
|
||||
Compress a block of data in the input buffer and returns the size of
|
||||
compressed block. The size of input buffer is specified by length. The
|
||||
minimum input buffer size is 16.
|
||||
|
||||
The output buffer must be at least 5% larger than the input buffer
|
||||
and can not be smaller than 66 bytes.
|
||||
|
||||
If the input is not compressible, the return value might be larger than
|
||||
length (input buffer size).
|
||||
|
||||
The input buffer and the output buffer can not overlap.
|
||||
|
||||
Compression level can be specified in parameter level. At the moment,
|
||||
only level 1 and level 2 are supported.
|
||||
Level 1 is the fastest compression and generally useful for short data.
|
||||
Level 2 is slightly slower but it gives better compression ratio.
|
||||
|
||||
Note that the compressed data, regardless of the level, can always be
|
||||
decompressed using the function fastlz_decompress above.
|
||||
*/
|
||||
|
||||
int fastlz_compress_level(int level, const void* input, int length, void* output);
|
||||
|
||||
#if defined (__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FASTLZ_H */
|
||||
@@ -0,0 +1,15 @@
|
||||
/* This file specifically for FST usage */
|
||||
/* config.h. Generated from config.h.in by configure. */
|
||||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Define to 1 if you have `alloca', as a function or macro. */
|
||||
#define HAVE_ALLOCA 1
|
||||
|
||||
/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix). */
|
||||
#define HAVE_ALLOCA_H 1
|
||||
#if !defined(__MINGW32__)
|
||||
# define HAVE_ALLOCA_H 1
|
||||
#endif
|
||||
|
||||
/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
|
||||
#define HAVE_FSEEKO 1
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,452 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2018 Tony Bybell.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef FST_API_H
|
||||
#define FST_API_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <zlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
||||
#define FST_RDLOAD "FSTLOAD | "
|
||||
|
||||
typedef uint32_t fstHandle;
|
||||
typedef uint32_t fstEnumHandle;
|
||||
|
||||
enum fstWriterPackType {
|
||||
FST_WR_PT_ZLIB = 0,
|
||||
FST_WR_PT_FASTLZ = 1,
|
||||
FST_WR_PT_LZ4 = 2
|
||||
};
|
||||
|
||||
enum fstFileType {
|
||||
FST_FT_MIN = 0,
|
||||
|
||||
FST_FT_VERILOG = 0,
|
||||
FST_FT_VHDL = 1,
|
||||
FST_FT_VERILOG_VHDL = 2,
|
||||
|
||||
FST_FT_MAX = 2
|
||||
};
|
||||
|
||||
enum fstBlockType {
|
||||
FST_BL_HDR = 0,
|
||||
FST_BL_VCDATA = 1,
|
||||
FST_BL_BLACKOUT = 2,
|
||||
FST_BL_GEOM = 3,
|
||||
FST_BL_HIER = 4,
|
||||
FST_BL_VCDATA_DYN_ALIAS = 5,
|
||||
FST_BL_HIER_LZ4 = 6,
|
||||
FST_BL_HIER_LZ4DUO = 7,
|
||||
FST_BL_VCDATA_DYN_ALIAS2 = 8,
|
||||
|
||||
FST_BL_ZWRAPPER = 254, /* indicates that whole trace is gz wrapped */
|
||||
FST_BL_SKIP = 255 /* used while block is being written */
|
||||
};
|
||||
|
||||
enum fstScopeType {
|
||||
FST_ST_MIN = 0,
|
||||
|
||||
FST_ST_VCD_MODULE = 0,
|
||||
FST_ST_VCD_TASK = 1,
|
||||
FST_ST_VCD_FUNCTION = 2,
|
||||
FST_ST_VCD_BEGIN = 3,
|
||||
FST_ST_VCD_FORK = 4,
|
||||
FST_ST_VCD_GENERATE = 5,
|
||||
FST_ST_VCD_STRUCT = 6,
|
||||
FST_ST_VCD_UNION = 7,
|
||||
FST_ST_VCD_CLASS = 8,
|
||||
FST_ST_VCD_INTERFACE = 9,
|
||||
FST_ST_VCD_PACKAGE = 10,
|
||||
FST_ST_VCD_PROGRAM = 11,
|
||||
|
||||
FST_ST_VHDL_ARCHITECTURE = 12,
|
||||
FST_ST_VHDL_PROCEDURE = 13,
|
||||
FST_ST_VHDL_FUNCTION = 14,
|
||||
FST_ST_VHDL_RECORD = 15,
|
||||
FST_ST_VHDL_PROCESS = 16,
|
||||
FST_ST_VHDL_BLOCK = 17,
|
||||
FST_ST_VHDL_FOR_GENERATE = 18,
|
||||
FST_ST_VHDL_IF_GENERATE = 19,
|
||||
FST_ST_VHDL_GENERATE = 20,
|
||||
FST_ST_VHDL_PACKAGE = 21,
|
||||
|
||||
FST_ST_MAX = 21,
|
||||
|
||||
FST_ST_GEN_ATTRBEGIN = 252,
|
||||
FST_ST_GEN_ATTREND = 253,
|
||||
|
||||
FST_ST_VCD_SCOPE = 254,
|
||||
FST_ST_VCD_UPSCOPE = 255
|
||||
};
|
||||
|
||||
enum fstVarType {
|
||||
FST_VT_MIN = 0, /* start of vartypes */
|
||||
|
||||
FST_VT_VCD_EVENT = 0,
|
||||
FST_VT_VCD_INTEGER = 1,
|
||||
FST_VT_VCD_PARAMETER = 2,
|
||||
FST_VT_VCD_REAL = 3,
|
||||
FST_VT_VCD_REAL_PARAMETER = 4,
|
||||
FST_VT_VCD_REG = 5,
|
||||
FST_VT_VCD_SUPPLY0 = 6,
|
||||
FST_VT_VCD_SUPPLY1 = 7,
|
||||
FST_VT_VCD_TIME = 8,
|
||||
FST_VT_VCD_TRI = 9,
|
||||
FST_VT_VCD_TRIAND = 10,
|
||||
FST_VT_VCD_TRIOR = 11,
|
||||
FST_VT_VCD_TRIREG = 12,
|
||||
FST_VT_VCD_TRI0 = 13,
|
||||
FST_VT_VCD_TRI1 = 14,
|
||||
FST_VT_VCD_WAND = 15,
|
||||
FST_VT_VCD_WIRE = 16,
|
||||
FST_VT_VCD_WOR = 17,
|
||||
FST_VT_VCD_PORT = 18,
|
||||
FST_VT_VCD_SPARRAY = 19, /* used to define the rownum (index) port for a sparse array */
|
||||
FST_VT_VCD_REALTIME = 20,
|
||||
|
||||
FST_VT_GEN_STRING = 21, /* generic string type (max len is defined dynamically via fstWriterEmitVariableLengthValueChange) */
|
||||
|
||||
FST_VT_SV_BIT = 22,
|
||||
FST_VT_SV_LOGIC = 23,
|
||||
FST_VT_SV_INT = 24, /* declare as size = 32 */
|
||||
FST_VT_SV_SHORTINT = 25, /* declare as size = 16 */
|
||||
FST_VT_SV_LONGINT = 26, /* declare as size = 64 */
|
||||
FST_VT_SV_BYTE = 27, /* declare as size = 8 */
|
||||
FST_VT_SV_ENUM = 28, /* declare as appropriate type range */
|
||||
FST_VT_SV_SHORTREAL = 29, /* declare and emit same as FST_VT_VCD_REAL (needs to be emitted as double, not a float) */
|
||||
|
||||
FST_VT_MAX = 29 /* end of vartypes */
|
||||
};
|
||||
|
||||
enum fstVarDir {
|
||||
FST_VD_MIN = 0,
|
||||
|
||||
FST_VD_IMPLICIT = 0,
|
||||
FST_VD_INPUT = 1,
|
||||
FST_VD_OUTPUT = 2,
|
||||
FST_VD_INOUT = 3,
|
||||
FST_VD_BUFFER = 4,
|
||||
FST_VD_LINKAGE = 5,
|
||||
|
||||
FST_VD_MAX = 5
|
||||
};
|
||||
|
||||
enum fstHierType {
|
||||
FST_HT_MIN = 0,
|
||||
|
||||
FST_HT_SCOPE = 0,
|
||||
FST_HT_UPSCOPE = 1,
|
||||
FST_HT_VAR = 2,
|
||||
FST_HT_ATTRBEGIN = 3,
|
||||
FST_HT_ATTREND = 4,
|
||||
|
||||
/* FST_HT_TREEBEGIN and FST_HT_TREEEND are not yet used by FST but are currently used when fstHier bridges other formats */
|
||||
FST_HT_TREEBEGIN = 5,
|
||||
FST_HT_TREEEND = 6,
|
||||
|
||||
FST_HT_MAX = 6
|
||||
};
|
||||
|
||||
enum fstAttrType {
|
||||
FST_AT_MIN = 0,
|
||||
|
||||
FST_AT_MISC = 0, /* self-contained: does not need matching FST_HT_ATTREND */
|
||||
FST_AT_ARRAY = 1,
|
||||
FST_AT_ENUM = 2,
|
||||
FST_AT_PACK = 3,
|
||||
|
||||
FST_AT_MAX = 3
|
||||
};
|
||||
|
||||
enum fstMiscType {
|
||||
FST_MT_MIN = 0,
|
||||
|
||||
FST_MT_COMMENT = 0, /* use fstWriterSetComment() to emit */
|
||||
FST_MT_ENVVAR = 1, /* use fstWriterSetEnvVar() to emit */
|
||||
FST_MT_SUPVAR = 2, /* use fstWriterCreateVar2() to emit */
|
||||
FST_MT_PATHNAME = 3, /* reserved for fstWriterSetSourceStem() string -> number management */
|
||||
FST_MT_SOURCESTEM = 4, /* use fstWriterSetSourceStem() to emit */
|
||||
FST_MT_SOURCEISTEM = 5, /* use fstWriterSetSourceInstantiationStem() to emit */
|
||||
FST_MT_VALUELIST = 6, /* use fstWriterSetValueList() to emit, followed by fstWriterCreateVar*() */
|
||||
FST_MT_ENUMTABLE = 7, /* use fstWriterCreateEnumTable() and fstWriterEmitEnumTableRef() to emit */
|
||||
FST_MT_UNKNOWN = 8,
|
||||
|
||||
FST_MT_MAX = 8
|
||||
};
|
||||
|
||||
enum fstArrayType {
|
||||
FST_AR_MIN = 0,
|
||||
|
||||
FST_AR_NONE = 0,
|
||||
FST_AR_UNPACKED = 1,
|
||||
FST_AR_PACKED = 2,
|
||||
FST_AR_SPARSE = 3,
|
||||
|
||||
FST_AR_MAX = 3
|
||||
};
|
||||
|
||||
enum fstEnumValueType {
|
||||
FST_EV_SV_INTEGER = 0,
|
||||
FST_EV_SV_BIT = 1,
|
||||
FST_EV_SV_LOGIC = 2,
|
||||
FST_EV_SV_INT = 3,
|
||||
FST_EV_SV_SHORTINT = 4,
|
||||
FST_EV_SV_LONGINT = 5,
|
||||
FST_EV_SV_BYTE = 6,
|
||||
FST_EV_SV_UNSIGNED_INTEGER = 7,
|
||||
FST_EV_SV_UNSIGNED_BIT = 8,
|
||||
FST_EV_SV_UNSIGNED_LOGIC = 9,
|
||||
FST_EV_SV_UNSIGNED_INT = 10,
|
||||
FST_EV_SV_UNSIGNED_SHORTINT = 11,
|
||||
FST_EV_SV_UNSIGNED_LONGINT = 12,
|
||||
FST_EV_SV_UNSIGNED_BYTE = 13,
|
||||
|
||||
FST_EV_REG = 14,
|
||||
FST_EV_TIME = 15,
|
||||
|
||||
FST_EV_MAX = 15
|
||||
};
|
||||
|
||||
enum fstPackType {
|
||||
FST_PT_NONE = 0,
|
||||
FST_PT_UNPACKED = 1,
|
||||
FST_PT_PACKED = 2,
|
||||
FST_PT_TAGGED_PACKED = 3,
|
||||
|
||||
FST_PT_MAX = 3
|
||||
};
|
||||
|
||||
enum fstSupplementalVarType {
|
||||
FST_SVT_MIN = 0,
|
||||
|
||||
FST_SVT_NONE = 0,
|
||||
|
||||
FST_SVT_VHDL_SIGNAL = 1,
|
||||
FST_SVT_VHDL_VARIABLE = 2,
|
||||
FST_SVT_VHDL_CONSTANT = 3,
|
||||
FST_SVT_VHDL_FILE = 4,
|
||||
FST_SVT_VHDL_MEMORY = 5,
|
||||
|
||||
FST_SVT_MAX = 5
|
||||
};
|
||||
|
||||
enum fstSupplementalDataType {
|
||||
FST_SDT_MIN = 0,
|
||||
|
||||
FST_SDT_NONE = 0,
|
||||
|
||||
FST_SDT_VHDL_BOOLEAN = 1,
|
||||
FST_SDT_VHDL_BIT = 2,
|
||||
FST_SDT_VHDL_BIT_VECTOR = 3,
|
||||
FST_SDT_VHDL_STD_ULOGIC = 4,
|
||||
FST_SDT_VHDL_STD_ULOGIC_VECTOR = 5,
|
||||
FST_SDT_VHDL_STD_LOGIC = 6,
|
||||
FST_SDT_VHDL_STD_LOGIC_VECTOR = 7,
|
||||
FST_SDT_VHDL_UNSIGNED = 8,
|
||||
FST_SDT_VHDL_SIGNED = 9,
|
||||
FST_SDT_VHDL_INTEGER = 10,
|
||||
FST_SDT_VHDL_REAL = 11,
|
||||
FST_SDT_VHDL_NATURAL = 12,
|
||||
FST_SDT_VHDL_POSITIVE = 13,
|
||||
FST_SDT_VHDL_TIME = 14,
|
||||
FST_SDT_VHDL_CHARACTER = 15,
|
||||
FST_SDT_VHDL_STRING = 16,
|
||||
|
||||
FST_SDT_MAX = 16,
|
||||
|
||||
FST_SDT_SVT_SHIFT_COUNT = 10, /* FST_SVT_* is ORed in by fstWriterCreateVar2() to the left after shifting FST_SDT_SVT_SHIFT_COUNT */
|
||||
FST_SDT_ABS_MAX = ((1<<(FST_SDT_SVT_SHIFT_COUNT))-1)
|
||||
};
|
||||
|
||||
|
||||
struct fstHier
|
||||
{
|
||||
unsigned char htyp;
|
||||
|
||||
union {
|
||||
/* if htyp == FST_HT_SCOPE */
|
||||
struct fstHierScope {
|
||||
unsigned char typ; /* FST_ST_MIN ... FST_ST_MAX */
|
||||
const char *name;
|
||||
const char *component;
|
||||
uint32_t name_length; /* strlen(u.scope.name) */
|
||||
uint32_t component_length; /* strlen(u.scope.component) */
|
||||
} scope;
|
||||
|
||||
/* if htyp == FST_HT_VAR */
|
||||
struct fstHierVar {
|
||||
unsigned char typ; /* FST_VT_MIN ... FST_VT_MAX */
|
||||
unsigned char direction; /* FST_VD_MIN ... FST_VD_MAX */
|
||||
unsigned char svt_workspace; /* zeroed out by FST reader, for client code use */
|
||||
unsigned char sdt_workspace; /* zeroed out by FST reader, for client code use */
|
||||
unsigned int sxt_workspace; /* zeroed out by FST reader, for client code use */
|
||||
const char *name;
|
||||
uint32_t length;
|
||||
fstHandle handle;
|
||||
uint32_t name_length; /* strlen(u.var.name) */
|
||||
unsigned is_alias : 1;
|
||||
} var;
|
||||
|
||||
/* if htyp == FST_HT_ATTRBEGIN */
|
||||
struct fstHierAttr {
|
||||
unsigned char typ; /* FST_AT_MIN ... FST_AT_MAX */
|
||||
unsigned char subtype; /* from fstMiscType, fstArrayType, fstEnumValueType, fstPackType */
|
||||
const char *name;
|
||||
uint64_t arg; /* number of array elements, struct members, or some other payload (possibly ignored) */
|
||||
uint64_t arg_from_name; /* for when name is overloaded as a variable-length integer (FST_AT_MISC + FST_MT_SOURCESTEM) */
|
||||
uint32_t name_length; /* strlen(u.attr.name) */
|
||||
} attr;
|
||||
} u;
|
||||
};
|
||||
|
||||
|
||||
struct fstETab
|
||||
{
|
||||
char *name;
|
||||
uint32_t elem_count;
|
||||
char **literal_arr;
|
||||
char **val_arr;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* writer functions
|
||||
*/
|
||||
void fstWriterClose(void *ctx);
|
||||
void * fstWriterCreate(const char *nam, int use_compressed_hier);
|
||||
fstEnumHandle fstWriterCreateEnumTable(void *ctx, const char *name, uint32_t elem_count, unsigned int min_valbits, const char **literal_arr, const char **val_arr);
|
||||
/* used for Verilog/SV */
|
||||
fstHandle fstWriterCreateVar(void *ctx, enum fstVarType vt, enum fstVarDir vd,
|
||||
uint32_t len, const char *nam, fstHandle aliasHandle);
|
||||
/* future expansion for VHDL and other languages. The variable type, data type, etc map onto
|
||||
the current Verilog/SV one. The "type" string is optional for a more verbose or custom description */
|
||||
fstHandle fstWriterCreateVar2(void *ctx, enum fstVarType vt, enum fstVarDir vd,
|
||||
uint32_t len, const char *nam, fstHandle aliasHandle,
|
||||
const char *type, enum fstSupplementalVarType svt, enum fstSupplementalDataType sdt);
|
||||
void fstWriterEmitDumpActive(void *ctx, int enable);
|
||||
void fstWriterEmitEnumTableRef(void *ctx, fstEnumHandle handle);
|
||||
void fstWriterEmitValueChange(void *ctx, fstHandle handle, const void *val);
|
||||
void fstWriterEmitVariableLengthValueChange(void *ctx, fstHandle handle, const void *val, uint32_t len);
|
||||
void fstWriterEmitTimeChange(void *ctx, uint64_t tim);
|
||||
void fstWriterFlushContext(void *ctx);
|
||||
int fstWriterGetDumpSizeLimitReached(void *ctx);
|
||||
int fstWriterGetFseekFailed(void *ctx);
|
||||
void fstWriterSetAttrBegin(void *ctx, enum fstAttrType attrtype, int subtype,
|
||||
const char *attrname, uint64_t arg);
|
||||
void fstWriterSetAttrEnd(void *ctx);
|
||||
void fstWriterSetComment(void *ctx, const char *comm);
|
||||
void fstWriterSetDate(void *ctx, const char *dat);
|
||||
void fstWriterSetDumpSizeLimit(void *ctx, uint64_t numbytes);
|
||||
void fstWriterSetEnvVar(void *ctx, const char *envvar);
|
||||
void fstWriterSetFileType(void *ctx, enum fstFileType filetype);
|
||||
void fstWriterSetPackType(void *ctx, enum fstWriterPackType typ);
|
||||
void fstWriterSetParallelMode(void *ctx, int enable);
|
||||
void fstWriterSetRepackOnClose(void *ctx, int enable); /* type = 0 (none), 1 (libz) */
|
||||
void fstWriterSetScope(void *ctx, enum fstScopeType scopetype,
|
||||
const char *scopename, const char *scopecomp);
|
||||
void fstWriterSetSourceInstantiationStem(void *ctx, const char *path, unsigned int line, unsigned int use_realpath);
|
||||
void fstWriterSetSourceStem(void *ctx, const char *path, unsigned int line, unsigned int use_realpath);
|
||||
void fstWriterSetTimescale(void *ctx, int ts);
|
||||
void fstWriterSetTimescaleFromString(void *ctx, const char *s);
|
||||
void fstWriterSetTimezero(void *ctx, int64_t tim);
|
||||
void fstWriterSetUpscope(void *ctx);
|
||||
void fstWriterSetValueList(void *ctx, const char *vl);
|
||||
void fstWriterSetVersion(void *ctx, const char *vers);
|
||||
|
||||
|
||||
/*
|
||||
* reader functions
|
||||
*/
|
||||
void fstReaderClose(void *ctx);
|
||||
void fstReaderClrFacProcessMask(void *ctx, fstHandle facidx);
|
||||
void fstReaderClrFacProcessMaskAll(void *ctx);
|
||||
uint64_t fstReaderGetAliasCount(void *ctx);
|
||||
const char * fstReaderGetCurrentFlatScope(void *ctx);
|
||||
void * fstReaderGetCurrentScopeUserInfo(void *ctx);
|
||||
int fstReaderGetCurrentScopeLen(void *ctx);
|
||||
const char * fstReaderGetDateString(void *ctx);
|
||||
int fstReaderGetDoubleEndianMatchState(void *ctx);
|
||||
uint64_t fstReaderGetDumpActivityChangeTime(void *ctx, uint32_t idx);
|
||||
unsigned char fstReaderGetDumpActivityChangeValue(void *ctx, uint32_t idx);
|
||||
uint64_t fstReaderGetEndTime(void *ctx);
|
||||
int fstReaderGetFacProcessMask(void *ctx, fstHandle facidx);
|
||||
int fstReaderGetFileType(void *ctx);
|
||||
int fstReaderGetFseekFailed(void *ctx);
|
||||
fstHandle fstReaderGetMaxHandle(void *ctx);
|
||||
uint64_t fstReaderGetMemoryUsedByWriter(void *ctx);
|
||||
uint32_t fstReaderGetNumberDumpActivityChanges(void *ctx);
|
||||
uint64_t fstReaderGetScopeCount(void *ctx);
|
||||
uint64_t fstReaderGetStartTime(void *ctx);
|
||||
signed char fstReaderGetTimescale(void *ctx);
|
||||
int64_t fstReaderGetTimezero(void *ctx);
|
||||
uint64_t fstReaderGetValueChangeSectionCount(void *ctx);
|
||||
char * fstReaderGetValueFromHandleAtTime(void *ctx, uint64_t tim, fstHandle facidx, char *buf);
|
||||
uint64_t fstReaderGetVarCount(void *ctx);
|
||||
const char * fstReaderGetVersionString(void *ctx);
|
||||
struct fstHier *fstReaderIterateHier(void *ctx);
|
||||
int fstReaderIterateHierRewind(void *ctx);
|
||||
int fstReaderIterBlocks(void *ctx,
|
||||
void (*value_change_callback)(void *user_callback_data_pointer, uint64_t time, fstHandle facidx, const unsigned char *value),
|
||||
void *user_callback_data_pointer, FILE *vcdhandle);
|
||||
int fstReaderIterBlocks2(void *ctx,
|
||||
void (*value_change_callback)(void *user_callback_data_pointer, uint64_t time, fstHandle facidx, const unsigned char *value),
|
||||
void (*value_change_callback_varlen)(void *user_callback_data_pointer, uint64_t time, fstHandle facidx, const unsigned char *value, uint32_t len),
|
||||
void *user_callback_data_pointer, FILE *vcdhandle);
|
||||
void fstReaderIterBlocksSetNativeDoublesOnCallback(void *ctx, int enable);
|
||||
void * fstReaderOpen(const char *nam);
|
||||
void * fstReaderOpenForUtilitiesOnly(void);
|
||||
const char * fstReaderPopScope(void *ctx);
|
||||
int fstReaderProcessHier(void *ctx, FILE *vcdhandle);
|
||||
const char * fstReaderPushScope(void *ctx, const char *nam, void *user_info);
|
||||
void fstReaderResetScope(void *ctx);
|
||||
void fstReaderSetFacProcessMask(void *ctx, fstHandle facidx);
|
||||
void fstReaderSetFacProcessMaskAll(void *ctx);
|
||||
void fstReaderSetLimitTimeRange(void *ctx, uint64_t start_time, uint64_t end_time);
|
||||
void fstReaderSetUnlimitedTimeRange(void *ctx);
|
||||
void fstReaderSetVcdExtensions(void *ctx, int enable);
|
||||
|
||||
|
||||
/*
|
||||
* utility functions
|
||||
*/
|
||||
int fstUtilityBinToEscConvertedLen(const unsigned char *s, int len); /* used for mallocs for fstUtilityBinToEsc() */
|
||||
int fstUtilityBinToEsc(unsigned char *d, const unsigned char *s, int len);
|
||||
int fstUtilityEscToBin(unsigned char *d, unsigned char *s, int len);
|
||||
struct fstETab *fstUtilityExtractEnumTableFromString(const char *s);
|
||||
void fstUtilityFreeEnumTable(struct fstETab *etab); /* must use to free fstETab properly */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2012 Tony Bybell.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef DEFS_LXTW_H
|
||||
#define DEFS_LXTW_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#ifndef HAVE_FSEEKO
|
||||
#define fseeko fseek
|
||||
#define ftello ftell
|
||||
#endif
|
||||
|
||||
#include "wavealloca.h"
|
||||
|
||||
#define LXT2_WR_HDRID (0x1380)
|
||||
#define LXT2_WR_VERSION (0x0001)
|
||||
|
||||
#define LXT2_WR_GRANULE_SIZE (64)
|
||||
#define LXT2_WR_GRANULE_NUM (256)
|
||||
#define LXT2_WR_PARTIAL_SIZE (2048)
|
||||
|
||||
#define LXT2_WR_GRAN_SECT_TIME 0
|
||||
#define LXT2_WR_GRAN_SECT_DICT 1
|
||||
#define LXT2_WR_GRAN_SECT_TIME_PARTIAL 2
|
||||
|
||||
#define LXT2_WR_GZWRITE_BUFFER 4096
|
||||
#define LXT2_WR_SYMPRIME 500009
|
||||
|
||||
typedef uint64_t lxttime_t;
|
||||
typedef int64_t lxtstime_t;
|
||||
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#ifdef __MINGW32__
|
||||
#define LXT2_WR_LLD "%I64d"
|
||||
#else
|
||||
#define LXT2_WR_LLD "%lld"
|
||||
#endif
|
||||
#define LXT2_WR_LLDESC(x) x##LL
|
||||
#define LXT2_WR_ULLDESC(x) x##ULL
|
||||
#else
|
||||
#define LXT2_WR_LLD "%I64d"
|
||||
#define LXT2_WR_LLDESC(x) x##i64
|
||||
#define LXT2_WR_ULLDESC(x) x##i64
|
||||
#endif
|
||||
|
||||
#if LXT2_WR_GRANULE_SIZE > 32
|
||||
typedef unsigned long long granmsk_t;
|
||||
#define LXT2_WR_GRAN_0VAL (LXT2_WR_ULLDESC(0))
|
||||
#define LXT2_WR_GRAN_1VAL (LXT2_WR_ULLDESC(1))
|
||||
#else
|
||||
typedef unsigned int granmsk_t;
|
||||
#define LXT2_WR_GRAN_0VAL (0)
|
||||
#define LXT2_WR_GRAN_1VAL (1)
|
||||
#endif
|
||||
|
||||
|
||||
enum LXT2_WR_Encodings {
|
||||
LXT2_WR_ENC_0,
|
||||
LXT2_WR_ENC_1,
|
||||
LXT2_WR_ENC_INV,
|
||||
LXT2_WR_ENC_LSH0,
|
||||
LXT2_WR_ENC_LSH1,
|
||||
LXT2_WR_ENC_RSH0,
|
||||
LXT2_WR_ENC_RSH1,
|
||||
|
||||
LXT2_WR_ENC_ADD1,
|
||||
LXT2_WR_ENC_ADD2,
|
||||
LXT2_WR_ENC_ADD3,
|
||||
LXT2_WR_ENC_ADD4,
|
||||
|
||||
LXT2_WR_ENC_SUB1,
|
||||
LXT2_WR_ENC_SUB2,
|
||||
LXT2_WR_ENC_SUB3,
|
||||
LXT2_WR_ENC_SUB4,
|
||||
|
||||
LXT2_WR_ENC_X,
|
||||
LXT2_WR_ENC_Z,
|
||||
|
||||
LXT2_WR_ENC_BLACKOUT,
|
||||
|
||||
LXT2_WR_DICT_START
|
||||
};
|
||||
|
||||
/*
|
||||
* integer splay
|
||||
*/
|
||||
typedef struct lxt2_wr_ds_tree_node lxt2_wr_ds_Tree;
|
||||
struct lxt2_wr_ds_tree_node {
|
||||
lxt2_wr_ds_Tree * left, * right;
|
||||
granmsk_t item;
|
||||
int val;
|
||||
lxt2_wr_ds_Tree * next;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* string splay
|
||||
*/
|
||||
typedef struct lxt2_wr_dslxt_tree_node lxt2_wr_dslxt_Tree;
|
||||
struct lxt2_wr_dslxt_tree_node {
|
||||
lxt2_wr_dslxt_Tree * left, * right;
|
||||
char *item;
|
||||
unsigned int val;
|
||||
lxt2_wr_dslxt_Tree * next;
|
||||
};
|
||||
|
||||
|
||||
struct lxt2_wr_trace
|
||||
{
|
||||
FILE *handle;
|
||||
gzFile zhandle;
|
||||
|
||||
lxt2_wr_dslxt_Tree *dict; /* dictionary manipulation */
|
||||
unsigned int num_dict_entries;
|
||||
unsigned int dict_string_mem_required;
|
||||
lxt2_wr_dslxt_Tree *dict_head;
|
||||
lxt2_wr_dslxt_Tree *dict_curr;
|
||||
|
||||
lxt2_wr_ds_Tree *mapdict; /* bitmap compression */
|
||||
unsigned int num_map_entries;
|
||||
lxt2_wr_ds_Tree *mapdict_head;
|
||||
lxt2_wr_ds_Tree *mapdict_curr;
|
||||
|
||||
off_t position;
|
||||
off_t zfacname_predec_size, zfacname_size, zfacgeometry_size;
|
||||
off_t zpackcount, zpackcount_cumulative;
|
||||
off_t current_chunk, current_chunkz;
|
||||
|
||||
struct lxt2_wr_symbol *sym[LXT2_WR_SYMPRIME];
|
||||
struct lxt2_wr_symbol **sorted_facs;
|
||||
struct lxt2_wr_symbol *symchain;
|
||||
unsigned int numfacs, numalias;
|
||||
int numfacbytes;
|
||||
int longestname;
|
||||
|
||||
int numsections, numblock;
|
||||
off_t facname_offset, facgeometry_offset;
|
||||
|
||||
lxttime_t mintime, maxtime;
|
||||
lxtstime_t timezero;
|
||||
unsigned int timegranule;
|
||||
int timescale;
|
||||
unsigned int timepos;
|
||||
unsigned int maxgranule;
|
||||
lxttime_t firsttime, lasttime;
|
||||
lxttime_t timetable[LXT2_WR_GRANULE_SIZE];
|
||||
|
||||
unsigned int partial_iter;
|
||||
|
||||
char *compress_fac_str;
|
||||
int compress_fac_len;
|
||||
|
||||
lxttime_t flushtime;
|
||||
unsigned flush_valid : 1;
|
||||
|
||||
unsigned do_strip_brackets : 1;
|
||||
unsigned emitted : 1; /* gate off change field zmode changes when set */
|
||||
unsigned timeset : 1; /* time has been modified from 0..0 */
|
||||
unsigned bumptime : 1; /* says that must go to next time position in granule as value change exists for current time */
|
||||
unsigned granule_dirty : 1; /* for flushing out final block */
|
||||
unsigned blackout : 1; /* blackout on/off */
|
||||
unsigned partial : 1; /* partial (vertical) trace support */
|
||||
unsigned partial_zip : 1; /* partial (vertical) trace support for zip subregions */
|
||||
unsigned no_checkpoint : 1; /* turns off interblock checkpointing */
|
||||
unsigned partial_preference : 1; /* partial preference encountered on some facs */
|
||||
|
||||
char initial_value;
|
||||
|
||||
char zmode[4]; /* fills in with "wb0".."wb9" */
|
||||
unsigned int gzbufpnt;
|
||||
unsigned char gzdest[LXT2_WR_GZWRITE_BUFFER + 4]; /* enough for zlib buffering */
|
||||
|
||||
char *lxtname;
|
||||
off_t break_size;
|
||||
off_t break_header_size;
|
||||
unsigned int break_number;
|
||||
};
|
||||
|
||||
|
||||
struct lxt2_wr_symbol
|
||||
{
|
||||
struct lxt2_wr_symbol *next;
|
||||
struct lxt2_wr_symbol *symchain;
|
||||
char *name;
|
||||
int namlen;
|
||||
|
||||
int facnum;
|
||||
struct lxt2_wr_symbol *aliased_to;
|
||||
|
||||
char *value; /* fac's actual value */
|
||||
|
||||
unsigned int rows;
|
||||
int msb, lsb;
|
||||
int len;
|
||||
int flags;
|
||||
|
||||
unsigned partial_preference : 1; /* in order to shove nets to the first partial group */
|
||||
|
||||
unsigned int chgpos;
|
||||
granmsk_t msk; /* must contain LXT2_WR_GRANULE_SIZE bits! */
|
||||
unsigned int chg[LXT2_WR_GRANULE_SIZE];
|
||||
};
|
||||
|
||||
|
||||
#define LXT2_WR_SYM_F_BITS (0)
|
||||
#define LXT2_WR_SYM_F_INTEGER (1<<0)
|
||||
#define LXT2_WR_SYM_F_DOUBLE (1<<1)
|
||||
#define LXT2_WR_SYM_F_STRING (1<<2)
|
||||
#define LXT2_WR_SYM_F_TIME (LXT2_WR_SYM_F_STRING) /* user must correctly format this as a string */
|
||||
#define LXT2_WR_SYM_F_ALIAS (1<<3)
|
||||
|
||||
#define LXT2_WR_SYM_F_SIGNED (1<<4)
|
||||
#define LXT2_WR_SYM_F_BOOLEAN (1<<5)
|
||||
#define LXT2_WR_SYM_F_NATURAL ((1<<6)|(LXT2_WR_SYM_F_INTEGER))
|
||||
#define LXT2_WR_SYM_F_POSITIVE ((1<<7)|(LXT2_WR_SYM_F_INTEGER))
|
||||
#define LXT2_WR_SYM_F_CHARACTER (1<<8)
|
||||
|
||||
#define LXT2_WR_SYM_F_CONSTANT (1<<9)
|
||||
#define LXT2_WR_SYM_F_VARIABLE (1<<10)
|
||||
#define LXT2_WR_SYM_F_SIGNAL (1<<11)
|
||||
|
||||
#define LXT2_WR_SYM_F_IN (1<<12)
|
||||
#define LXT2_WR_SYM_F_OUT (1<<13)
|
||||
#define LXT2_WR_SYM_F_INOUT (1<<14)
|
||||
|
||||
#define LXT2_WR_SYM_F_WIRE (1<<15)
|
||||
#define LXT2_WR_SYM_F_REG (1<<16)
|
||||
|
||||
|
||||
/* file I/O */
|
||||
struct lxt2_wr_trace * lxt2_wr_init(const char *name);
|
||||
void lxt2_wr_flush(struct lxt2_wr_trace *lt);
|
||||
void lxt2_wr_close(struct lxt2_wr_trace *lt);
|
||||
|
||||
/* for dealing with very large traces, split into multiple files approximately "siz" in length */
|
||||
void lxt2_wr_set_break_size(struct lxt2_wr_trace *lt, off_t siz);
|
||||
|
||||
/* 0 = no compression, 9 = best compression, 4 = default */
|
||||
void lxt2_wr_set_compression_depth(struct lxt2_wr_trace *lt, unsigned int depth);
|
||||
|
||||
/* default is partial off, turning on makes for faster trace reads, nonzero zipmode causes vertical compression */
|
||||
void lxt2_wr_set_partial_off(struct lxt2_wr_trace *lt);
|
||||
void lxt2_wr_set_partial_on(struct lxt2_wr_trace *lt, int zipmode);
|
||||
void lxt2_wr_set_partial_preference(struct lxt2_wr_trace *lt, const char *name);
|
||||
|
||||
/* turning off checkpointing makes for smaller files */
|
||||
void lxt2_wr_set_checkpoint_off(struct lxt2_wr_trace *lt);
|
||||
void lxt2_wr_set_checkpoint_on(struct lxt2_wr_trace *lt);
|
||||
|
||||
/* facility creation */
|
||||
void lxt2_wr_set_initial_value(struct lxt2_wr_trace *lt, char value);
|
||||
struct lxt2_wr_symbol * lxt2_wr_symbol_find(struct lxt2_wr_trace *lt, const char *name);
|
||||
struct lxt2_wr_symbol * lxt2_wr_symbol_add(struct lxt2_wr_trace *lt, const char *name, unsigned int rows, int msb, int lsb, int flags);
|
||||
struct lxt2_wr_symbol * lxt2_wr_symbol_alias(struct lxt2_wr_trace *lt, const char *existing_name, const char *alias, int msb, int lsb);
|
||||
void lxt2_wr_symbol_bracket_stripping(struct lxt2_wr_trace *lt, int doit);
|
||||
|
||||
/* each granule is LXT2_WR_GRANULE_SIZE (32 or 64) timesteps, default is 256 per section */
|
||||
void lxt2_wr_set_maxgranule(struct lxt2_wr_trace *lt, unsigned int maxgranule);
|
||||
|
||||
/* time ops */
|
||||
void lxt2_wr_set_timescale(struct lxt2_wr_trace *lt, int timescale);
|
||||
void lxt2_wr_set_timezero(struct lxt2_wr_trace *lt, lxtstime_t timeval);
|
||||
int lxt2_wr_set_time(struct lxt2_wr_trace *lt, unsigned int timeval);
|
||||
int lxt2_wr_inc_time_by_delta(struct lxt2_wr_trace *lt, unsigned int timeval);
|
||||
int lxt2_wr_set_time64(struct lxt2_wr_trace *lt, lxttime_t timeval);
|
||||
int lxt2_wr_inc_time_by_delta64(struct lxt2_wr_trace *lt, lxttime_t timeval);
|
||||
|
||||
/* allows blackout regions in LXT files */
|
||||
void lxt2_wr_set_dumpoff(struct lxt2_wr_trace *lt);
|
||||
void lxt2_wr_set_dumpon(struct lxt2_wr_trace *lt);
|
||||
|
||||
/* left fill on bit_string uses vcd semantics (left fill with value[0] unless value[0]=='1', then use '0') */
|
||||
int lxt2_wr_emit_value_int(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, int value);
|
||||
int lxt2_wr_emit_value_double(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, double value);
|
||||
int lxt2_wr_emit_value_string(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, char *value);
|
||||
int lxt2_wr_emit_value_bit_string(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, char *value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
LZ4 - Fast LZ compression algorithm
|
||||
Header File
|
||||
Copyright (C) 2011-2015, Yann Collet.
|
||||
|
||||
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
You can contact the author at :
|
||||
- LZ4 source repository : https://github.com/Cyan4973/lz4
|
||||
- LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#if defined (__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* lz4.h provides block compression functions, and gives full buffer control to programmer.
|
||||
* If you need to generate inter-operable compressed data (respecting LZ4 frame specification),
|
||||
* and can let the library handle its own memory, please use lz4frame.h instead.
|
||||
*/
|
||||
|
||||
/**************************************
|
||||
* Version
|
||||
**************************************/
|
||||
#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */
|
||||
#define LZ4_VERSION_MINOR 7 /* for new (non-breaking) interface capabilities */
|
||||
#define LZ4_VERSION_RELEASE 1 /* for tweaks, bug-fixes, or development */
|
||||
#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
|
||||
int LZ4_versionNumber (void);
|
||||
|
||||
/**************************************
|
||||
* Tuning parameter
|
||||
**************************************/
|
||||
/*
|
||||
* LZ4_MEMORY_USAGE :
|
||||
* Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
|
||||
* Increasing memory usage improves compression ratio
|
||||
* Reduced memory usage can improve speed, due to cache effect
|
||||
* Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache
|
||||
*/
|
||||
#define LZ4_MEMORY_USAGE 14
|
||||
|
||||
|
||||
/**************************************
|
||||
* Simple Functions
|
||||
**************************************/
|
||||
|
||||
int LZ4_compress_default(const char* source, char* dest, int sourceSize, int maxDestSize);
|
||||
int LZ4_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize);
|
||||
|
||||
/*
|
||||
LZ4_compress_default() :
|
||||
Compresses 'sourceSize' bytes from buffer 'source'
|
||||
into already allocated 'dest' buffer of size 'maxDestSize'.
|
||||
Compression is guaranteed to succeed if 'maxDestSize' >= LZ4_compressBound(sourceSize).
|
||||
It also runs faster, so it's a recommended setting.
|
||||
If the function cannot compress 'source' into a more limited 'dest' budget,
|
||||
compression stops *immediately*, and the function result is zero.
|
||||
As a consequence, 'dest' content is not valid.
|
||||
This function never writes outside 'dest' buffer, nor read outside 'source' buffer.
|
||||
sourceSize : Max supported value is LZ4_MAX_INPUT_VALUE
|
||||
maxDestSize : full or partial size of buffer 'dest' (which must be already allocated)
|
||||
return : the number of bytes written into buffer 'dest' (necessarily <= maxOutputSize)
|
||||
or 0 if compression fails
|
||||
|
||||
LZ4_decompress_safe() :
|
||||
compressedSize : is the precise full size of the compressed block.
|
||||
maxDecompressedSize : is the size of destination buffer, which must be already allocated.
|
||||
return : the number of bytes decompressed into destination buffer (necessarily <= maxDecompressedSize)
|
||||
If destination buffer is not large enough, decoding will stop and output an error code (<0).
|
||||
If the source stream is detected malformed, the function will stop decoding and return a negative result.
|
||||
This function is protected against buffer overflow exploits, including malicious data packets.
|
||||
It never writes outside output buffer, nor reads outside input buffer.
|
||||
*/
|
||||
|
||||
|
||||
/**************************************
|
||||
* Advanced Functions
|
||||
**************************************/
|
||||
#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */
|
||||
#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
|
||||
|
||||
/*
|
||||
LZ4_compressBound() :
|
||||
Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible)
|
||||
This function is primarily useful for memory allocation purposes (destination buffer size).
|
||||
Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example).
|
||||
Note that LZ4_compress_default() compress faster when dest buffer size is >= LZ4_compressBound(srcSize)
|
||||
inputSize : max supported value is LZ4_MAX_INPUT_SIZE
|
||||
return : maximum output size in a "worst case" scenario
|
||||
or 0, if input size is too large ( > LZ4_MAX_INPUT_SIZE)
|
||||
*/
|
||||
int LZ4_compressBound(int inputSize);
|
||||
|
||||
/*
|
||||
LZ4_compress_fast() :
|
||||
Same as LZ4_compress_default(), but allows to select an "acceleration" factor.
|
||||
The larger the acceleration value, the faster the algorithm, but also the lesser the compression.
|
||||
It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.
|
||||
An acceleration value of "1" is the same as regular LZ4_compress_default()
|
||||
Values <= 0 will be replaced by ACCELERATION_DEFAULT (see lz4.c), which is 1.
|
||||
*/
|
||||
int LZ4_compress_fast (const char* source, char* dest, int sourceSize, int maxDestSize, int acceleration);
|
||||
|
||||
|
||||
/*
|
||||
LZ4_compress_fast_extState() :
|
||||
Same compression function, just using an externally allocated memory space to store compression state.
|
||||
Use LZ4_sizeofState() to know how much memory must be allocated,
|
||||
and allocate it on 8-bytes boundaries (using malloc() typically).
|
||||
Then, provide it as 'void* state' to compression function.
|
||||
*/
|
||||
int LZ4_sizeofState(void);
|
||||
int LZ4_compress_fast_extState (void* state, const char* source, char* dest, int inputSize, int maxDestSize, int acceleration);
|
||||
|
||||
|
||||
/*
|
||||
LZ4_compress_destSize() :
|
||||
Reverse the logic, by compressing as much data as possible from 'source' buffer
|
||||
into already allocated buffer 'dest' of size 'targetDestSize'.
|
||||
This function either compresses the entire 'source' content into 'dest' if it's large enough,
|
||||
or fill 'dest' buffer completely with as much data as possible from 'source'.
|
||||
*sourceSizePtr : will be modified to indicate how many bytes where read from 'source' to fill 'dest'.
|
||||
New value is necessarily <= old value.
|
||||
return : Nb bytes written into 'dest' (necessarily <= targetDestSize)
|
||||
or 0 if compression fails
|
||||
*/
|
||||
int LZ4_compress_destSize (const char* source, char* dest, int* sourceSizePtr, int targetDestSize);
|
||||
|
||||
|
||||
/*
|
||||
LZ4_decompress_fast() :
|
||||
originalSize : is the original and therefore uncompressed size
|
||||
return : the number of bytes read from the source buffer (in other words, the compressed size)
|
||||
If the source stream is detected malformed, the function will stop decoding and return a negative result.
|
||||
Destination buffer must be already allocated. Its size must be a minimum of 'originalSize' bytes.
|
||||
note : This function fully respect memory boundaries for properly formed compressed data.
|
||||
It is a bit faster than LZ4_decompress_safe().
|
||||
However, it does not provide any protection against intentionally modified data stream (malicious input).
|
||||
Use this function in trusted environment only (data to decode comes from a trusted source).
|
||||
*/
|
||||
int LZ4_decompress_fast (const char* source, char* dest, int originalSize);
|
||||
|
||||
/*
|
||||
LZ4_decompress_safe_partial() :
|
||||
This function decompress a compressed block of size 'compressedSize' at position 'source'
|
||||
into destination buffer 'dest' of size 'maxDecompressedSize'.
|
||||
The function tries to stop decompressing operation as soon as 'targetOutputSize' has been reached,
|
||||
reducing decompression time.
|
||||
return : the number of bytes decoded in the destination buffer (necessarily <= maxDecompressedSize)
|
||||
Note : this number can be < 'targetOutputSize' should the compressed block to decode be smaller.
|
||||
Always control how many bytes were decoded.
|
||||
If the source stream is detected malformed, the function will stop decoding and return a negative result.
|
||||
This function never writes outside of output buffer, and never reads outside of input buffer. It is therefore protected against malicious data packets
|
||||
*/
|
||||
int LZ4_decompress_safe_partial (const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize);
|
||||
|
||||
|
||||
/***********************************************
|
||||
* Streaming Compression Functions
|
||||
***********************************************/
|
||||
#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4)
|
||||
#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(long long))
|
||||
/*
|
||||
* LZ4_stream_t
|
||||
* information structure to track an LZ4 stream.
|
||||
* important : init this structure content before first use !
|
||||
* note : only allocated directly the structure if you are statically linking LZ4
|
||||
* If you are using liblz4 as a DLL, please use below construction methods instead.
|
||||
*/
|
||||
typedef struct { long long table[LZ4_STREAMSIZE_U64]; } LZ4_stream_t;
|
||||
|
||||
/*
|
||||
* LZ4_resetStream
|
||||
* Use this function to init an allocated LZ4_stream_t structure
|
||||
*/
|
||||
void LZ4_resetStream (LZ4_stream_t* streamPtr);
|
||||
|
||||
/*
|
||||
* LZ4_createStream will allocate and initialize an LZ4_stream_t structure
|
||||
* LZ4_freeStream releases its memory.
|
||||
* In the context of a DLL (liblz4), please use these methods rather than the static struct.
|
||||
* They are more future proof, in case of a change of LZ4_stream_t size.
|
||||
*/
|
||||
LZ4_stream_t* LZ4_createStream(void);
|
||||
int LZ4_freeStream (LZ4_stream_t* streamPtr);
|
||||
|
||||
/*
|
||||
* LZ4_loadDict
|
||||
* Use this function to load a static dictionary into LZ4_stream.
|
||||
* Any previous data will be forgotten, only 'dictionary' will remain in memory.
|
||||
* Loading a size of 0 is allowed.
|
||||
* Return : dictionary size, in bytes (necessarily <= 64 KB)
|
||||
*/
|
||||
int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);
|
||||
|
||||
/*
|
||||
* LZ4_compress_fast_continue
|
||||
* Compress buffer content 'src', using data from previously compressed blocks as dictionary to improve compression ratio.
|
||||
* Important : Previous data blocks are assumed to still be present and unmodified !
|
||||
* 'dst' buffer must be already allocated.
|
||||
* If maxDstSize >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.
|
||||
* If not, and if compressed data cannot fit into 'dst' buffer size, compression stops, and function returns a zero.
|
||||
*/
|
||||
int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int maxDstSize, int acceleration);
|
||||
|
||||
/*
|
||||
* LZ4_saveDict
|
||||
* If previously compressed data block is not guaranteed to remain available at its memory location
|
||||
* save it into a safer place (char* safeBuffer)
|
||||
* Note : you don't need to call LZ4_loadDict() afterwards,
|
||||
* dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue()
|
||||
* Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error
|
||||
*/
|
||||
int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int dictSize);
|
||||
|
||||
|
||||
/************************************************
|
||||
* Streaming Decompression Functions
|
||||
************************************************/
|
||||
|
||||
#define LZ4_STREAMDECODESIZE_U64 4
|
||||
#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long))
|
||||
typedef struct { unsigned long long table[LZ4_STREAMDECODESIZE_U64]; } LZ4_streamDecode_t;
|
||||
/*
|
||||
* LZ4_streamDecode_t
|
||||
* information structure to track an LZ4 stream.
|
||||
* init this structure content using LZ4_setStreamDecode or memset() before first use !
|
||||
*
|
||||
* In the context of a DLL (liblz4) please prefer usage of construction methods below.
|
||||
* They are more future proof, in case of a change of LZ4_streamDecode_t size in the future.
|
||||
* LZ4_createStreamDecode will allocate and initialize an LZ4_streamDecode_t structure
|
||||
* LZ4_freeStreamDecode releases its memory.
|
||||
*/
|
||||
LZ4_streamDecode_t* LZ4_createStreamDecode(void);
|
||||
int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);
|
||||
|
||||
/*
|
||||
* LZ4_setStreamDecode
|
||||
* Use this function to instruct where to find the dictionary.
|
||||
* Setting a size of 0 is allowed (same effect as reset).
|
||||
* Return : 1 if OK, 0 if error
|
||||
*/
|
||||
int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize);
|
||||
|
||||
/*
|
||||
*_continue() :
|
||||
These decoding functions allow decompression of multiple blocks in "streaming" mode.
|
||||
Previously decoded blocks *must* remain available at the memory position where they were decoded (up to 64 KB)
|
||||
In the case of a ring buffers, decoding buffer must be either :
|
||||
- Exactly same size as encoding buffer, with same update rule (block boundaries at same positions)
|
||||
In which case, the decoding & encoding ring buffer can have any size, including very small ones ( < 64 KB).
|
||||
- Larger than encoding buffer, by a minimum of maxBlockSize more bytes.
|
||||
maxBlockSize is implementation dependent. It's the maximum size you intend to compress into a single block.
|
||||
In which case, encoding and decoding buffers do not need to be synchronized,
|
||||
and encoding ring buffer can have any size, including small ones ( < 64 KB).
|
||||
- _At least_ 64 KB + 8 bytes + maxBlockSize.
|
||||
In which case, encoding and decoding buffers do not need to be synchronized,
|
||||
and encoding ring buffer can have any size, including larger than decoding buffer.
|
||||
Whenever these conditions are not possible, save the last 64KB of decoded data into a safe buffer,
|
||||
and indicate where it is saved using LZ4_setStreamDecode()
|
||||
*/
|
||||
int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxDecompressedSize);
|
||||
int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize);
|
||||
|
||||
|
||||
/*
|
||||
Advanced decoding functions :
|
||||
*_usingDict() :
|
||||
These decoding functions work the same as
|
||||
a combination of LZ4_setStreamDecode() followed by LZ4_decompress_x_continue()
|
||||
They are stand-alone. They don't need nor update an LZ4_streamDecode_t structure.
|
||||
*/
|
||||
int LZ4_decompress_safe_usingDict (const char* source, char* dest, int compressedSize, int maxDecompressedSize, const char* dictStart, int dictSize);
|
||||
int LZ4_decompress_fast_usingDict (const char* source, char* dest, int originalSize, const char* dictStart, int dictSize);
|
||||
|
||||
|
||||
|
||||
/**************************************
|
||||
* Obsolete Functions
|
||||
**************************************/
|
||||
/* Deprecate Warnings */
|
||||
/* Should these warnings messages be a problem,
|
||||
it is generally possible to disable them,
|
||||
with -Wno-deprecated-declarations for gcc
|
||||
or _CRT_SECURE_NO_WARNINGS in Visual for example.
|
||||
You can also define LZ4_DEPRECATE_WARNING_DEFBLOCK. */
|
||||
#ifndef LZ4_DEPRECATE_WARNING_DEFBLOCK
|
||||
# define LZ4_DEPRECATE_WARNING_DEFBLOCK
|
||||
# define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
|
||||
# if (LZ4_GCC_VERSION >= 405) || defined(__clang__)
|
||||
# define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))
|
||||
# elif (LZ4_GCC_VERSION >= 301)
|
||||
# define LZ4_DEPRECATED(message) __attribute__((deprecated))
|
||||
# elif defined(_MSC_VER)
|
||||
# define LZ4_DEPRECATED(message) __declspec(deprecated(message))
|
||||
# else
|
||||
# pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler")
|
||||
# define LZ4_DEPRECATED(message)
|
||||
# endif
|
||||
#endif /* LZ4_DEPRECATE_WARNING_DEFBLOCK */
|
||||
|
||||
/* Obsolete compression functions */
|
||||
/* These functions are planned to start generate warnings by r131 approximately */
|
||||
int LZ4_compress (const char* source, char* dest, int sourceSize);
|
||||
int LZ4_compress_limitedOutput (const char* source, char* dest, int sourceSize, int maxOutputSize);
|
||||
int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize);
|
||||
int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);
|
||||
int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize);
|
||||
int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize);
|
||||
|
||||
/* Obsolete decompression functions */
|
||||
/* These function names are completely deprecated and must no longer be used.
|
||||
They are only provided here for compatibility with older programs.
|
||||
- LZ4_uncompress is the same as LZ4_decompress_fast
|
||||
- LZ4_uncompress_unknownOutputSize is the same as LZ4_decompress_safe
|
||||
These function prototypes are now disabled; uncomment them only if you really need them.
|
||||
It is highly recommended to stop using these prototypes and migrate to maintained ones */
|
||||
/* int LZ4_uncompress (const char* source, char* dest, int outputSize); */
|
||||
/* int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); */
|
||||
|
||||
/* Obsolete streaming functions; use new streaming interface whenever possible */
|
||||
LZ4_DEPRECATED("use LZ4_createStream() instead") void* LZ4_create (char* inputBuffer);
|
||||
LZ4_DEPRECATED("use LZ4_createStream() instead") int LZ4_sizeofStreamState(void);
|
||||
LZ4_DEPRECATED("use LZ4_resetStream() instead") int LZ4_resetStreamState(void* state, char* inputBuffer);
|
||||
LZ4_DEPRECATED("use LZ4_saveDict() instead") char* LZ4_slideInputBuffer (void* state);
|
||||
|
||||
/* Obsolete streaming decoding functions */
|
||||
LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize);
|
||||
LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize);
|
||||
|
||||
|
||||
#if defined (__cplusplus)
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 1999 Tony Bybell.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef WAVE_ALLOCA_H
|
||||
#define WAVE_ALLOCA_H
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_ALLOCA_H
|
||||
#include <alloca.h>
|
||||
#elif defined(__GNUC__)
|
||||
#ifndef __MINGW32__
|
||||
#ifndef alloca
|
||||
#define alloca __builtin_alloca
|
||||
#endif
|
||||
#else
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
#elif defined(_MSC_VER)
|
||||
#include <malloc.h>
|
||||
#define alloca _alloca
|
||||
#endif
|
||||
#define wave_alloca alloca
|
||||
#endif
|
||||
|
||||
+192
-82
@@ -24,8 +24,18 @@
|
||||
//=========================================================================
|
||||
|
||||
#define _VERILATED_CPP_
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated_imp.h"
|
||||
|
||||
#include "verilated_config.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <sys/stat.h> // mkdir
|
||||
|
||||
#if defined(WIN32) || defined(__MINGW32__)
|
||||
# include <direct.h> // mkdir
|
||||
#endif
|
||||
|
||||
#define VL_VALUE_STRING_MAX_WIDTH 8192 ///< Max static char array for VL_VALUE_STRING
|
||||
|
||||
@@ -38,6 +48,7 @@ VerilatedVoidCb Verilated::s_flushCb = NULL;
|
||||
|
||||
// Keep below together in one cache line
|
||||
Verilated::Serialized Verilated::s_s;
|
||||
Verilated::NonSerialized Verilated::s_ns;
|
||||
VL_THREAD_LOCAL Verilated::ThreadLocal Verilated::t_s;
|
||||
|
||||
Verilated::CommandArgValues Verilated::s_args;
|
||||
@@ -48,7 +59,7 @@ 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) VL_MT_UNSAFE {
|
||||
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); // Not VL_PRINTF_MT, already on main thread
|
||||
if (Verilated::gotFinish()) {
|
||||
@@ -61,15 +72,15 @@ void vl_finish (const char* filename, int linenum, const char* hier) VL_MT_UNSAF
|
||||
#endif
|
||||
|
||||
#ifndef VL_USER_STOP // Define this to override this function
|
||||
void vl_stop (const char* filename, int linenum, const char* hier) VL_MT_UNSAFE {
|
||||
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");
|
||||
vl_fatal(filename,linenum,hier,"Verilog $stop");
|
||||
}
|
||||
#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) VL_MT_UNSAFE {
|
||||
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); // Not VL_PRINTF_MT, already on main thread
|
||||
@@ -84,7 +95,7 @@ void vl_fatal (const char* filename, int linenum, const char* hier, const char*
|
||||
//===========================================================================
|
||||
// Wrapper to call certain functions via messages when multithreaded
|
||||
|
||||
void VL_FINISH_MT (const char* filename, int linenum, const char* hier) VL_MT_SAFE {
|
||||
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);
|
||||
@@ -94,7 +105,7 @@ void VL_FINISH_MT (const char* filename, int linenum, const char* hier) VL_MT_SA
|
||||
#endif
|
||||
}
|
||||
|
||||
void VL_STOP_MT (const char* filename, int linenum, const char* hier) VL_MT_SAFE {
|
||||
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);
|
||||
@@ -104,7 +115,7 @@ void VL_STOP_MT (const char* filename, int linenum, const char* hier) VL_MT_SAFE
|
||||
#endif
|
||||
}
|
||||
|
||||
void VL_FATAL_MT (const char* filename, int linenum, const char* hier, const char* msg) VL_MT_SAFE {
|
||||
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);
|
||||
@@ -124,13 +135,11 @@ std::string _vl_string_vprintf(const char* formatp, va_list ap) VL_MT_SAFE {
|
||||
int len = VL_VSNPRINTF(NULL, 0, formatp, aq);
|
||||
va_end(aq);
|
||||
if (VL_UNLIKELY(len < 1)) {
|
||||
va_end(ap);
|
||||
return "";
|
||||
}
|
||||
|
||||
char* bufp = new char[len+1];
|
||||
VL_VSNPRINTF(bufp, len+1, formatp, ap);
|
||||
va_end(ap);
|
||||
|
||||
std::string out = std::string(bufp, len);
|
||||
delete[] bufp;
|
||||
@@ -196,28 +205,24 @@ Verilated::Serialized::Serialized() {
|
||||
s_fatalOnVpiError = true; // retains old default behaviour
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Random reset -- Only called at init time, so don't inline.
|
||||
|
||||
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 lock(s_mutex);
|
||||
seedval = lrand48()<<16 ^ lrand48();
|
||||
if (!seedval) seedval++;
|
||||
}
|
||||
srand48_r(seedval, &t_buffer);
|
||||
Verilated::NonSerialized::NonSerialized() {
|
||||
s_profThreadsStart = 1;
|
||||
s_profThreadsWindow = 2;
|
||||
s_profThreadsFilenamep = strdup("profile_threads.dat");
|
||||
}
|
||||
Verilated::NonSerialized::~NonSerialized() {
|
||||
if (s_profThreadsFilenamep) {
|
||||
free(const_cast<char*>(s_profThreadsFilenamep)); s_profThreadsFilenamep=NULL;
|
||||
}
|
||||
long v0; lrand48_r(&t_buffer, &v0);
|
||||
long v1; lrand48_r(&t_buffer, &v1);
|
||||
return (v0<<16) ^ v1;
|
||||
#elif defined(_WIN32) && !defined(__CYGWIN__)
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Random -- Mostly called at init time, so not inline.
|
||||
|
||||
static vluint32_t vl_sys_rand32() VL_MT_UNSAFE {
|
||||
// Return random 32-bits using system library.
|
||||
// Used only to construct seed for Verilator's PNRG.
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
// Windows doesn't have lrand48(), although Cygwin does.
|
||||
return (rand()<<16) ^ rand();
|
||||
#else
|
||||
@@ -225,21 +230,45 @@ IData VL_RAND32() VL_MT_SAFE {
|
||||
#endif
|
||||
}
|
||||
|
||||
vluint64_t vl_rand64() VL_MT_SAFE {
|
||||
static VerilatedMutex s_mutex;
|
||||
static VL_THREAD_LOCAL bool t_seeded = false;
|
||||
static VL_THREAD_LOCAL vluint64_t t_state[2];
|
||||
if (VL_UNLIKELY(!t_seeded)) {
|
||||
t_seeded = true;
|
||||
{
|
||||
VerilatedLockGuard lock(s_mutex);
|
||||
t_state[0] = ((static_cast<vluint64_t>(vl_sys_rand32()) << 32)
|
||||
^ (static_cast<vluint64_t>(vl_sys_rand32())));
|
||||
t_state[1] = ((static_cast<vluint64_t>(vl_sys_rand32()) << 32)
|
||||
^ (static_cast<vluint64_t>(vl_sys_rand32())));
|
||||
// Fix state as algorithm is slow to randomize if many zeros
|
||||
// This causes a loss of ~ 1 bit of seed entropy, no big deal
|
||||
if (VL_COUNTONES_I(t_state[0]) < 10) t_state[0] = ~t_state[0];
|
||||
if (VL_COUNTONES_I(t_state[1]) < 10) t_state[1] = ~t_state[1];
|
||||
}
|
||||
}
|
||||
// Xoroshiro128+ algorithm
|
||||
vluint64_t result = t_state[0] + t_state[1];
|
||||
t_state[1] ^= t_state[0];
|
||||
t_state[0] = (((t_state[0] << 55) | (t_state[0] >> 9))
|
||||
^ t_state[1] ^ (t_state[1] << 14));
|
||||
t_state[1] = (t_state[1] << 36) | (t_state[1] >> 28);
|
||||
return result;
|
||||
}
|
||||
|
||||
IData VL_RANDOM_I(int obits) VL_MT_SAFE {
|
||||
return VL_RAND32() & VL_MASK_I(obits);
|
||||
return vl_rand64() & VL_MASK_I(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);
|
||||
return vl_rand64() & VL_MASK_Q(obits);
|
||||
}
|
||||
|
||||
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();
|
||||
outwp[i] = vl_rand64();
|
||||
} else {
|
||||
outwp[i] = VL_RAND32() & VL_MASK_I(obits);
|
||||
outwp[i] = vl_rand64() & VL_MASK_I(obits);
|
||||
}
|
||||
}
|
||||
return outwp;
|
||||
@@ -254,7 +283,6 @@ IData VL_RAND_RESET_I(int obits) VL_MT_SAFE {
|
||||
if (obits<32) data &= VL_MASK_I(obits);
|
||||
return data;
|
||||
}
|
||||
|
||||
QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE {
|
||||
if (Verilated::randReset()==0) return 0;
|
||||
QData data = VL_ULL(~0);
|
||||
@@ -264,7 +292,6 @@ QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE {
|
||||
if (obits<64) data &= VL_MASK_Q(obits);
|
||||
return data;
|
||||
}
|
||||
|
||||
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)) {
|
||||
@@ -1216,19 +1243,19 @@ IData VL_SSCANF_INX(int, const std::string& ld, const char* formatp, ...) VL_MT_
|
||||
}
|
||||
|
||||
void VL_WRITEMEM_Q(bool hex, int width, int depth, int array_lsb, int,
|
||||
QData ofilename, const void* memp, IData start,
|
||||
QData filename, const void* memp, IData start,
|
||||
IData end) VL_MT_SAFE {
|
||||
WData fnw[2]; VL_SET_WQ(fnw, ofilename);
|
||||
return VL_WRITEMEM_W(hex, width,depth,array_lsb,2,fnw,memp,start,end);
|
||||
WData fnw[2]; VL_SET_WQ(fnw, filename);
|
||||
return VL_WRITEMEM_W(hex, width, depth, array_lsb,2, fnw, memp, start, end);
|
||||
}
|
||||
|
||||
void VL_WRITEMEM_W(bool hex, int width, int depth, int array_lsb, int fnwords,
|
||||
WDataInP ofilenamep, const void* memp, IData start,
|
||||
WDataInP filenamep, const 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);
|
||||
return VL_WRITEMEM_N(hex, width,depth,array_lsb,ofilenames,memp,start,end);
|
||||
char filenamez[VL_TO_STRING_MAX_WORDS*VL_WORDSIZE+1];
|
||||
_VL_VINT_TO_STRING(fnwords*VL_WORDSIZE, filenamez, filenamep);
|
||||
std::string filenames(filenamez);
|
||||
return VL_WRITEMEM_N(hex, width, depth, array_lsb, filenames, memp, start, end);
|
||||
}
|
||||
|
||||
const char* memhFormat(int nBits) {
|
||||
@@ -1255,19 +1282,19 @@ void VL_WRITEMEM_N(
|
||||
int depth, // Number of rows
|
||||
int array_lsb, // Index of first row. Valid row addresses
|
||||
// // range from array_lsb up to (array_lsb + depth - 1)
|
||||
const std::string& ofilenamep, // Output file name
|
||||
const std::string& filename, // Output file name
|
||||
const void* memp, // Array state
|
||||
IData start, // First array row address to write
|
||||
IData end // Last address to write
|
||||
) VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(!hex)) {
|
||||
VL_FATAL_MT(ofilenamep.c_str(), 0, "",
|
||||
VL_FATAL_MT(filename.c_str(), 0, "",
|
||||
"VL_WRITEMEM_N only supports hex format for now, sorry!");
|
||||
return;
|
||||
}
|
||||
FILE* fp = fopen(ofilenamep.c_str(), "w");
|
||||
FILE* fp = fopen(filename.c_str(), "w");
|
||||
if (VL_UNLIKELY(!fp)) {
|
||||
VL_FATAL_MT(ofilenamep.c_str(), 0, "", "$writemem file not found");
|
||||
VL_FATAL_MT(filename.c_str(), 0, "", "$writemem file not found");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1276,7 +1303,7 @@ void VL_WRITEMEM_N(
|
||||
|| (row_addr > array_lsb + depth - 1)) {
|
||||
vluint32_t endmax = ~0;
|
||||
if (end != endmax) {
|
||||
VL_FATAL_MT(ofilenamep.c_str(), 0, "",
|
||||
VL_FATAL_MT(filename.c_str(), 0, "",
|
||||
"$writemem specified address out-of-bounds");
|
||||
}
|
||||
// else, it's not an error to overflow due to end == endmax,
|
||||
@@ -1340,17 +1367,17 @@ void VL_WRITEMEM_N(
|
||||
}
|
||||
|
||||
void VL_READMEM_Q(bool hex, int width, int depth, int array_lsb, int,
|
||||
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);
|
||||
QData filename, void* memp, IData start, IData end) VL_MT_SAFE {
|
||||
WData fnw[2]; VL_SET_WQ(fnw, filename);
|
||||
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) 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);
|
||||
return VL_READMEM_N(hex,width,depth,array_lsb,ofilenames,memp,start,end);
|
||||
WDataInP filenamep, void* memp, IData start, IData end) VL_MT_SAFE {
|
||||
char filenamez[VL_TO_STRING_MAX_WORDS*VL_WORDSIZE+1];
|
||||
_VL_VINT_TO_STRING(fnwords*VL_WORDSIZE, filenamez, filenamep);
|
||||
std::string filenames(filenamez);
|
||||
return VL_READMEM_N(hex, width, depth, array_lsb, filenames, memp, start, end);
|
||||
}
|
||||
|
||||
void VL_READMEM_N(
|
||||
@@ -1359,15 +1386,15 @@ void VL_READMEM_N(
|
||||
int depth, // Number of rows
|
||||
int array_lsb, // Index of first row. Valid row addresses
|
||||
// // range from array_lsb up to (array_lsb + depth - 1)
|
||||
const std::string& ofilenamep, // Input file name
|
||||
const std::string& filename, // Input file name
|
||||
void* memp, // Array state
|
||||
IData start, // First array row address to read
|
||||
IData end // Last row address to read
|
||||
) VL_MT_SAFE {
|
||||
FILE* fp = fopen(ofilenamep.c_str(), "r");
|
||||
FILE* fp = fopen(filename.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_MT (ofilenamep.c_str(), 0, "", "$readmem file not found");
|
||||
VL_FATAL_MT(filename.c_str(), 0, "", "$readmem file not found");
|
||||
return;
|
||||
}
|
||||
// Prep for reading
|
||||
@@ -1414,8 +1441,8 @@ void VL_READMEM_N(
|
||||
//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_MT (ofilenamep.c_str(), linenum, "",
|
||||
"$readmem file address beyond bounds of array");
|
||||
VL_FATAL_MT(filename.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);
|
||||
@@ -1444,15 +1471,15 @@ void VL_READMEM_N(
|
||||
datap[0] |= value;
|
||||
}
|
||||
if (VL_UNLIKELY(value>=(1<<shift))) {
|
||||
VL_FATAL_MT (ofilenamep.c_str(), linenum, "",
|
||||
"$readmemb (binary) file contains hex characters");
|
||||
VL_FATAL_MT(filename.c_str(), linenum, "",
|
||||
"$readmemb (binary) file contains hex characters");
|
||||
}
|
||||
}
|
||||
}
|
||||
innum = true;
|
||||
}
|
||||
else {
|
||||
VL_FATAL_MT (ofilenamep.c_str(), linenum, "", "$readmem file syntax error");
|
||||
VL_FATAL_MT(filename.c_str(), linenum, "", "$readmem file syntax error");
|
||||
}
|
||||
}
|
||||
lastc = c;
|
||||
@@ -1462,7 +1489,8 @@ void VL_READMEM_N(
|
||||
// Final checks
|
||||
fclose(fp);
|
||||
if (VL_UNLIKELY(end != VL_UL(0xffffffff) && addr != (end+1))) {
|
||||
VL_FATAL_MT (ofilenamep.c_str(), linenum, "", "$readmem file ended before specified ending-address");
|
||||
VL_FATAL_MT(filename.c_str(), linenum, "",
|
||||
"$readmem file ended before specified ending-address");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1470,16 +1498,16 @@ 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) VL_MT_SAFE {
|
||||
IData VL_SYSTEM_IW(int lhswords, WDataInP lhsp) VL_MT_SAFE {
|
||||
char filenamez[VL_TO_STRING_MAX_WORDS*VL_WORDSIZE+1];
|
||||
_VL_VINT_TO_STRING(lhswords*VL_WORDSIZE, filenamez, filenamep);
|
||||
_VL_VINT_TO_STRING(lhswords*VL_WORDSIZE, filenamez, lhsp);
|
||||
int code = system(filenamez); // Yes, system() is threadsafe
|
||||
return code >> 8; // Want exit status
|
||||
}
|
||||
|
||||
IData VL_TESTPLUSARGS_I(const char* formatp) VL_MT_SAFE {
|
||||
const std::string& match = VerilatedImp::argPlusMatch(formatp);
|
||||
if (match == "") return 0;
|
||||
if (match.empty()) return 0;
|
||||
else return 1;
|
||||
}
|
||||
|
||||
@@ -1509,7 +1537,7 @@ IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_M
|
||||
|
||||
const std::string& match = VerilatedImp::argPlusMatch(prefix.c_str());
|
||||
const char* dp = match.c_str() + 1 /*leading + */ + prefix.length();
|
||||
if (match == "") return 0;
|
||||
if (match.empty()) return 0;
|
||||
|
||||
VL_ZERO_RESET_W(rbits, rwp);
|
||||
switch (tolower(fmt)) {
|
||||
@@ -1565,7 +1593,7 @@ IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_S
|
||||
}
|
||||
const std::string& match = VerilatedImp::argPlusMatch(prefix.c_str());
|
||||
const char* dp = match.c_str() + 1 /*leading + */ + prefix.length();
|
||||
if (match == "") return 0;
|
||||
if (match.empty()) return 0;
|
||||
rdr = std::string(dp);
|
||||
return 1;
|
||||
}
|
||||
@@ -1573,7 +1601,7 @@ IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_S
|
||||
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;
|
||||
if (match.empty()) return NULL;
|
||||
strncpy(outstr, match.c_str()+strlen(prefixp)+1, // +1 to skip the "+"
|
||||
VL_VALUE_STRING_MAX_WIDTH);
|
||||
outstr[VL_VALUE_STRING_MAX_WIDTH-1] = '\0';
|
||||
@@ -1617,10 +1645,10 @@ Verilated::ThreadLocal::ThreadLocal()
|
||||
Verilated::ThreadLocal::~ThreadLocal() {
|
||||
}
|
||||
|
||||
void Verilated::debug(int val) VL_MT_SAFE {
|
||||
void Verilated::debug(int level) VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
s_s.s_debug = val;
|
||||
if (val) {
|
||||
s_s.s_debug = level;
|
||||
if (level) {
|
||||
#ifdef VL_DEBUG
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("- Verilated::debug is on. Message prefix indicates {<thread>,<sequence_number>}.\n"););
|
||||
#else
|
||||
@@ -1648,6 +1676,20 @@ void Verilated::fatalOnVpiError(bool flag) VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
s_s.s_fatalOnVpiError = flag;
|
||||
}
|
||||
void Verilated::profThreadsStart(vluint64_t flag) VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
s_ns.s_profThreadsStart = flag;
|
||||
}
|
||||
void Verilated::profThreadsWindow(vluint64_t flag) VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
s_ns.s_profThreadsWindow = flag;
|
||||
}
|
||||
void Verilated::profThreadsFilenamep(const char* flagp) VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
if (s_ns.s_profThreadsFilenamep) free(const_cast<char*>(s_ns.s_profThreadsFilenamep));
|
||||
s_ns.s_profThreadsFilenamep = strdup(flagp);
|
||||
}
|
||||
|
||||
|
||||
const char* Verilated::catName(const char* n1, const char* n2) VL_MT_SAFE {
|
||||
// Returns new'ed data
|
||||
@@ -1683,8 +1725,15 @@ void Verilated::flushCall() VL_MT_SAFE {
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
const char* Verilated::productName() VL_PURE {
|
||||
return VERILATOR_PRODUCT;
|
||||
}
|
||||
const char* Verilated::productVersion() VL_PURE {
|
||||
return VERILATOR_VERSION;
|
||||
}
|
||||
|
||||
void Verilated::commandArgs(int argc, const char** argv) VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
VerilatedLockGuard lock(s_args.m_argMutex);
|
||||
s_args.argc = argc;
|
||||
s_args.argv = argv;
|
||||
VerilatedImp::commandArgs(argc,argv);
|
||||
@@ -1693,7 +1742,7 @@ void Verilated::commandArgs(int argc, const char** argv) VL_MT_SAFE {
|
||||
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 "";
|
||||
if (match.empty()) return "";
|
||||
strncpy(outstr, match.c_str(), VL_VALUE_STRING_MAX_WIDTH);
|
||||
outstr[VL_VALUE_STRING_MAX_WIDTH-1] = '\0';
|
||||
return outstr;
|
||||
@@ -1707,6 +1756,14 @@ void Verilated::overWidthError(const char* signame) VL_MT_SAFE {
|
||||
VL_FATAL_MT("unknown",0,"", msg.c_str());
|
||||
}
|
||||
|
||||
void Verilated::mkdir(const char* dirname) VL_MT_UNSAFE {
|
||||
#if defined(_WIN32) || defined(__MINGW32__)
|
||||
::mkdir(dirname);
|
||||
#else
|
||||
::mkdir(dirname, 0777);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Verilated::quiesce() VL_MT_SAFE {
|
||||
#ifdef VL_THREADED
|
||||
// Wait until all threads under this evaluation are quiet
|
||||
@@ -1780,9 +1837,61 @@ void VerilatedImp::commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(s
|
||||
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]);
|
||||
commandArgVl(argv[i]);
|
||||
}
|
||||
s_s.m_argVecLoaded = true; // Can't just test later for empty vector, no arguments is ok
|
||||
}
|
||||
void VerilatedImp::commandArgVl(const std::string& arg) {
|
||||
if (0 == strncmp(arg.c_str(), "+verilator+", strlen("+verilator+"))) {
|
||||
std::string value;
|
||||
if (0) {
|
||||
}
|
||||
else if (arg == "+verilator+debug") {
|
||||
Verilated::debug(4);
|
||||
}
|
||||
else if (commandArgVlValue(arg, "+verilator+debugi+", value/*ref*/)) {
|
||||
Verilated::debug(atoi(value.c_str()));
|
||||
}
|
||||
else if (arg == "+verilator+help") {
|
||||
versionDump();
|
||||
VL_PRINTF_MT("For help, please see 'verilator --help'\n");
|
||||
VL_FATAL_MT("COMMAND_LINE", 0, "", "Exiting due to command line argument (not an error)");
|
||||
}
|
||||
else if (commandArgVlValue(arg, "+verilator+prof+threads+start+", value/*ref*/)) {
|
||||
Verilated::profThreadsStart(atoll(value.c_str()));
|
||||
}
|
||||
else if (commandArgVlValue(arg, "+verilator+prof+threads+window+", value/*ref*/)) {
|
||||
Verilated::profThreadsWindow(atol(value.c_str()));
|
||||
}
|
||||
else if (commandArgVlValue(arg, "+verilator+prof+threads+file+", value/*ref*/)) {
|
||||
Verilated::profThreadsFilenamep(value.c_str());
|
||||
}
|
||||
else if (commandArgVlValue(arg, "+verilator+rand+reset+", value/*ref*/)) {
|
||||
Verilated::randReset(atoi(value.c_str()));
|
||||
}
|
||||
else if (arg == "+verilator+V") {
|
||||
versionDump(); // Someday more info too
|
||||
VL_FATAL_MT("COMMAND_LINE", 0, "", "Exiting due to command line argument (not an error)");
|
||||
}
|
||||
else if (arg == "+verilator+version") {
|
||||
versionDump();
|
||||
VL_FATAL_MT("COMMAND_LINE", 0, "", "Exiting due to command line argument (not an error)");
|
||||
}
|
||||
else {
|
||||
VL_PRINTF_MT("%%Warning: Unknown +verilator runtime argument: '%s'\n", arg.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
bool VerilatedImp::commandArgVlValue(const std::string& arg,
|
||||
const std::string& prefix, std::string& valuer) {
|
||||
size_t len = prefix.length();
|
||||
if (0==strncmp(prefix.c_str(), arg.c_str(), len)) {
|
||||
valuer = arg.substr(len);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// VerilatedSyms:: Methods
|
||||
@@ -1807,7 +1916,8 @@ VerilatedModule::VerilatedModule(const char* namep)
|
||||
|
||||
VerilatedModule::~VerilatedModule() {
|
||||
// Memory cleanup - not called during normal operation
|
||||
if (m_namep) { free((void*)m_namep); m_namep=NULL; }
|
||||
// NOLINTNEXTLINE(google-readability-casting)
|
||||
if (m_namep) { free((void*)(m_namep)); m_namep=NULL; }
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
@@ -1907,7 +2017,7 @@ void VerilatedScope::varInsert(int finalize, const char* namep, void* datap,
|
||||
if (!finalize) return;
|
||||
|
||||
if (!m_varsp) m_varsp = new VerilatedVarNameMap();
|
||||
VerilatedVar var (namep, datap, vltype, (VerilatedVarFlags)vlflags, dims);
|
||||
VerilatedVar var(namep, datap, vltype, static_cast<VerilatedVarFlags>(vlflags), dims);
|
||||
|
||||
va_list ap;
|
||||
va_start(ap,dims);
|
||||
|
||||
+130
-101
@@ -28,23 +28,22 @@
|
||||
#ifndef _VERILATED_H_
|
||||
#define _VERILATED_H_ 1 ///< Header Guard
|
||||
|
||||
#include "verilated_config.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
// <iostream> avoided to reduce compile time
|
||||
// <map> avoided and instead in verilated_heavy.h to reduce compile time
|
||||
// <string> avoided and instead in verilated_heavy.h to reduce compile time
|
||||
#ifdef VL_THREADED
|
||||
# include <atomic>
|
||||
# include <mutex>
|
||||
# include <thread>
|
||||
#endif
|
||||
// <iostream> avoided to reduce compile time
|
||||
// <map> avoided and instead in verilated_heavy.h to reduce compile time
|
||||
// <string> avoided and instead in verilated_heavy.h to reduce compile time
|
||||
|
||||
//=============================================================================
|
||||
// Switches
|
||||
@@ -79,6 +78,10 @@ class VerilatedVar;
|
||||
class VerilatedVarNameMap;
|
||||
class VerilatedVcd;
|
||||
class VerilatedVcdC;
|
||||
class VerilatedLxt2;
|
||||
class VerilatedLxt2C;
|
||||
class VerilatedFst;
|
||||
class VerilatedFstC;
|
||||
|
||||
enum VerilatedVarType {
|
||||
VLVT_UNKNOWN=0,
|
||||
@@ -92,15 +95,16 @@ enum VerilatedVarType {
|
||||
};
|
||||
|
||||
enum VerilatedVarFlags {
|
||||
VLVD_IN=1, // == vpiInput
|
||||
VLVD_OUT=2, // == vpiOutput
|
||||
VLVD_INOUT=3, // == vpiInOut
|
||||
VLVD_NODIR=5, // == vpiNoDirection
|
||||
VLVF_MASK_DIR=7, // Bit mask for above directions
|
||||
VLVD_0 = 0, // None
|
||||
VLVD_IN = 1, // == vpiInput
|
||||
VLVD_OUT = 2, // == vpiOutput
|
||||
VLVD_INOUT = 3, // == vpiInOut
|
||||
VLVD_NODIR = 5, // == vpiNoDirection
|
||||
VLVF_MASK_DIR = 7, // Bit mask for above directions
|
||||
// Flags
|
||||
VLVF_PUB_RD=(1<<8), // Public readable
|
||||
VLVF_PUB_RW=(1<<9), // Public writable
|
||||
VLVF_DPI_CLAY=(1<<10) // DPI compatible C standard layout
|
||||
VLVF_PUB_RD = (1<<8), // Public readable
|
||||
VLVF_PUB_RW = (1<<9), // Public writable
|
||||
VLVF_DPI_CLAY = (1<<10) // DPI compatible C standard layout
|
||||
};
|
||||
|
||||
//=========================================================================
|
||||
@@ -120,6 +124,7 @@ class VL_CAPABILITY("mutex") VerilatedMutex {
|
||||
public:
|
||||
VerilatedMutex() {}
|
||||
~VerilatedMutex() {}
|
||||
const VerilatedMutex& operator!() const { return *this; } // For -fthread_safety
|
||||
/// Acquire/lock mutex
|
||||
void lock() VL_ACQUIRE() {
|
||||
// Try to acquire the lock by spinning. If the wait is short,
|
||||
@@ -327,7 +332,7 @@ public: // But internals only - called from VerilatedModule's
|
||||
class Verilated {
|
||||
// MEMBERS
|
||||
// Slow path variables
|
||||
static VerilatedMutex m_mutex; ///< Mutex for all static members, when VL_THREADED
|
||||
static VerilatedMutex m_mutex; ///< Mutex for s_s/s_ns members, when VL_THREADED
|
||||
|
||||
static VerilatedVoidCb s_flushCb; ///< Flush callback function
|
||||
|
||||
@@ -344,9 +349,21 @@ class Verilated {
|
||||
~Serialized() {}
|
||||
} s_s;
|
||||
|
||||
static struct NonSerialized { // Non-serialized information
|
||||
// These are reloaded from on command-line settings, so do not need to persist
|
||||
// Fast path
|
||||
vluint64_t s_profThreadsStart; ///< +prof+threads starting time
|
||||
vluint32_t s_profThreadsWindow; ///< +prof+threads window size
|
||||
// Slow path
|
||||
const char* s_profThreadsFilenamep; ///< +prof+threads filename
|
||||
NonSerialized();
|
||||
~NonSerialized();
|
||||
} s_ns;
|
||||
|
||||
// no need to be save-restored (serialized) the
|
||||
// assumption is that the restore is allowed to pass different arguments
|
||||
static struct CommandArgValues {
|
||||
VerilatedMutex m_argMutex; ///< Mutex for s_args members, when VL_THREADED
|
||||
int argc;
|
||||
const char** argv;
|
||||
CommandArgValues() : argc(0), argv(NULL) {}
|
||||
@@ -408,6 +425,14 @@ public:
|
||||
/// Enable/disable vpi fatal
|
||||
static void fatalOnVpiError(bool flag) VL_MT_SAFE;
|
||||
static bool fatalOnVpiError() VL_MT_SAFE { return s_s.s_fatalOnVpiError; }
|
||||
/// --prof-threads related settings
|
||||
static void profThreadsStart(vluint64_t flag) VL_MT_SAFE;
|
||||
static vluint64_t profThreadsStart() VL_MT_SAFE { return s_ns.s_profThreadsStart; }
|
||||
static void profThreadsWindow(vluint64_t flag) VL_MT_SAFE;
|
||||
static vluint32_t profThreadsWindow() VL_MT_SAFE { return s_ns.s_profThreadsWindow; }
|
||||
static void profThreadsFilenamep(const char* flagp) VL_MT_SAFE;
|
||||
static const char* profThreadsFilenamep() VL_MT_SAFE { return s_ns.s_profThreadsFilenamep; }
|
||||
|
||||
/// Flush callback for VCD waves
|
||||
static void flushCb(VerilatedVoidCb cb) VL_MT_SAFE;
|
||||
static void flushCall() VL_MT_SAFE;
|
||||
@@ -422,8 +447,11 @@ public:
|
||||
static const char* commandArgsPlusMatch(const char* prefixp) VL_MT_SAFE;
|
||||
|
||||
/// Produce name & version for (at least) VPI
|
||||
static const char* productName() VL_PURE { return VERILATOR_PRODUCT; }
|
||||
static const char* productVersion() VL_PURE { return VERILATOR_VERSION; }
|
||||
static const char* productName() VL_PURE;
|
||||
static const char* productVersion() VL_PURE;
|
||||
|
||||
/// Convenience OS utilities
|
||||
static void mkdir(const char* dirname) VL_MT_UNSAFE;
|
||||
|
||||
/// When multithreaded, quiesce the model to prepare for trace/saves/coverage
|
||||
/// This may only be called when no locks are held.
|
||||
@@ -499,31 +527,31 @@ private:
|
||||
/// User code may wish to replace this function, to do so, define VL_USER_FINISH.
|
||||
/// This code does not have to be thread safe.
|
||||
/// Verilator internal code must call VL_FINISH_MT instead, which eventually calls this.
|
||||
extern void vl_finish (const char* filename, int linenum, const char* hier);
|
||||
extern void vl_finish(const char* filename, int linenum, const char* hier);
|
||||
|
||||
/// Routine to call for $stop
|
||||
/// User code may wish to replace this function, to do so, define VL_USER_STOP.
|
||||
/// This code does not have to be thread safe.
|
||||
/// Verilator internal code must call VL_FINISH_MT instead, which eventually calls this.
|
||||
extern void vl_stop (const char* filename, int linenum, const char* hier);
|
||||
extern void vl_stop(const char* filename, int linenum, const char* hier);
|
||||
|
||||
/// Routine to call for a couple of fatal messages
|
||||
/// User code may wish to replace this function, to do so, define VL_USER_FATAL.
|
||||
/// This code does not have to be thread safe.
|
||||
/// Verilator internal code must call VL_FINISH_MT instead, which eventually calls this.
|
||||
extern void vl_fatal (const char* filename, int linenum, const char* hier,
|
||||
const char* msg);
|
||||
extern void vl_fatal(const char* filename, int linenum, const char* hier,
|
||||
const char* msg);
|
||||
|
||||
//=========================================================================
|
||||
// Extern functions -- Slow path
|
||||
|
||||
/// Multithread safe wrapper for calls to $finish
|
||||
extern void VL_FINISH_MT (const char* filename, int linenum, const char* hier) VL_MT_SAFE;
|
||||
extern void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAFE;
|
||||
/// Multithread safe wrapper for calls to $stop
|
||||
extern void VL_STOP_MT (const char* filename, int linenum, const char* hier) VL_MT_SAFE;
|
||||
extern void VL_STOP_MT(const char* filename, int linenum, const char* hier) VL_MT_SAFE;
|
||||
/// Multithread safe wrapper to call for a couple of fatal messages
|
||||
extern void VL_FATAL_MT (const char* filename, int linenum, const char* hier,
|
||||
const char* msg) VL_MT_SAFE;
|
||||
extern void VL_FATAL_MT(const char* filename, int linenum, const char* hier,
|
||||
const char* msg) VL_MT_SAFE;
|
||||
|
||||
/// Print a string, multithread safe. Eventually VL_PRINTF will get called.
|
||||
#ifdef VL_THREADED
|
||||
@@ -555,29 +583,29 @@ extern WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP r
|
||||
/// File I/O
|
||||
extern IData VL_FGETS_IXI(int obits, void* destp, IData fpi);
|
||||
|
||||
extern IData VL_FOPEN_S(const char* filenamep, const char* mode);
|
||||
extern IData VL_FOPEN_WI(int fnwords, WDataInP ofilename, IData mode);
|
||||
extern IData VL_FOPEN_QI(QData ofilename, IData mode);
|
||||
inline IData VL_FOPEN_II(IData ofilename, IData mode) VL_MT_SAFE {
|
||||
return VL_FOPEN_QI(ofilename,mode); }
|
||||
extern IData VL_FOPEN_S(const char* filenamep, const char* modep);
|
||||
extern IData VL_FOPEN_WI(int fnwords, WDataInP filenamep, IData mode);
|
||||
extern IData VL_FOPEN_QI(QData filename, IData mode);
|
||||
inline IData VL_FOPEN_II(IData filename, IData mode) VL_MT_SAFE {
|
||||
return VL_FOPEN_QI(filename,mode); }
|
||||
|
||||
extern void VL_FCLOSE_I(IData fdi);
|
||||
|
||||
extern void VL_READMEM_W(bool hex, int width, int depth, int array_lsb, int fnwords,
|
||||
WDataInP ofilename, void* memp, IData start, IData end);
|
||||
WDataInP filenamep, void* memp, IData start, IData end);
|
||||
extern void VL_READMEM_Q(bool hex, int width, int depth, int array_lsb, int fnwords,
|
||||
QData ofilename, void* memp, IData start, IData end);
|
||||
QData filename, void* memp, IData start, IData end);
|
||||
inline void VL_READMEM_I(bool hex, int width, int depth, int array_lsb, int fnwords,
|
||||
IData ofilename, void* memp, IData start, IData end) VL_MT_SAFE {
|
||||
VL_READMEM_Q(hex, width,depth,array_lsb,fnwords, ofilename,memp,start,end); }
|
||||
IData filename, void* memp, IData start, IData end) VL_MT_SAFE {
|
||||
VL_READMEM_Q(hex, width,depth,array_lsb,fnwords, filename,memp,start,end); }
|
||||
|
||||
extern void VL_WRITEMEM_W(bool hex, int width, int depth, int array_lsb, int fnwords,
|
||||
WDataInP ofilename, const void* memp, IData start, IData end);
|
||||
WDataInP filenamep, const void* memp, IData start, IData end);
|
||||
extern void VL_WRITEMEM_Q(bool hex, int width, int depth, int array_lsb, int fnwords,
|
||||
QData ofilename, const void* memp, IData start, IData end);
|
||||
QData filename, const void* memp, IData start, IData end);
|
||||
inline void VL_WRITEMEM_I(bool hex, int width, int depth, int array_lsb, int fnwords,
|
||||
IData ofilename, const void* memp, IData start, IData end) VL_MT_SAFE {
|
||||
VL_WRITEMEM_Q(hex, width,depth,array_lsb,fnwords, ofilename,memp,start,end); }
|
||||
IData filename, const void* memp, IData start, IData end) VL_MT_SAFE {
|
||||
VL_WRITEMEM_Q(hex, width,depth,array_lsb,fnwords, filename,memp,start,end); }
|
||||
|
||||
extern void VL_WRITEF(const char* formatp, ...);
|
||||
extern void VL_FWRITEF(IData fpi, const char* formatp, ...);
|
||||
@@ -593,7 +621,7 @@ extern void VL_SFORMAT_X(int obits, IData& destr, const char* formatp, ...);
|
||||
extern void VL_SFORMAT_X(int obits, QData& destr, const char* formatp, ...);
|
||||
extern void VL_SFORMAT_X(int obits, void* destp, const char* formatp, ...);
|
||||
|
||||
extern IData VL_SYSTEM_IW(int lhsnwords, WDataInP lhs);
|
||||
extern IData VL_SYSTEM_IW(int lhswords, WDataInP lhsp);
|
||||
extern IData VL_SYSTEM_IQ(QData lhs);
|
||||
inline IData VL_SYSTEM_II(IData lhs) VL_MT_SAFE { return VL_SYSTEM_IQ(lhs); }
|
||||
|
||||
@@ -604,24 +632,24 @@ extern const char* vl_mc_scan_plusargs(const char* prefixp); // PLIish
|
||||
// Base macros
|
||||
|
||||
/// Return true if data[bit] set; not 0/1 return, but 0/non-zero return.
|
||||
#define VL_BITISSET_I(data,bit) (data & (VL_UL(1)<<VL_BITBIT_I(bit)))
|
||||
#define VL_BITISSET_Q(data,bit) (data & (VL_ULL(1)<<VL_BITBIT_Q(bit)))
|
||||
#define VL_BITISSET_W(data,bit) (data[VL_BITWORD_I(bit)] & (VL_UL(1)<<VL_BITBIT_I(bit)))
|
||||
#define VL_BITISSET_I(data,bit) ((data) & (VL_UL(1) << VL_BITBIT_I(bit)))
|
||||
#define VL_BITISSET_Q(data,bit) ((data) & (VL_ULL(1) << VL_BITBIT_Q(bit)))
|
||||
#define VL_BITISSET_W(data,bit) ((data)[VL_BITWORD_I(bit)] & (VL_UL(1) << VL_BITBIT_I(bit)))
|
||||
#define VL_BITISSETLIMIT_W(data,width,bit) \
|
||||
(((bit)<(width)) && data[VL_BITWORD_I(bit)] & (VL_UL(1)<<VL_BITBIT_I(bit)))
|
||||
(((bit)<(width)) && (data)[VL_BITWORD_I(bit)] & (VL_UL(1) << VL_BITBIT_I(bit)))
|
||||
|
||||
/// Shift appropriate word by bit. Does not account for wrapping between two words
|
||||
#define VL_BITRSHIFT_W(data,bit) (data[VL_BITWORD_I(bit)] >> VL_BITBIT_I(bit))
|
||||
#define VL_BITRSHIFT_W(data,bit) ((data)[VL_BITWORD_I(bit)] >> VL_BITBIT_I(bit))
|
||||
|
||||
/// Create two 32-bit words from quadword
|
||||
/// WData is always at least 2 words; does not clean upper bits
|
||||
#define VL_SET_WQ(owp,data) { owp[0]=static_cast<IData>(data); \
|
||||
owp[1]=static_cast<IData>((data)>>VL_WORDSIZE); }
|
||||
#define VL_SET_WI(owp,data) { owp[0]=static_cast<IData>(data); owp[1]=0; }
|
||||
#define VL_SET_WQ(owp,data) { (owp)[0] = static_cast<IData>(data); \
|
||||
(owp)[1] = static_cast<IData>((data)>>VL_WORDSIZE); }
|
||||
#define VL_SET_WI(owp,data) { (owp)[0] = static_cast<IData>(data); (owp)[1] = 0; }
|
||||
#define VL_SET_QW(lwp) \
|
||||
( (static_cast<QData>(lwp[0])) \
|
||||
| (static_cast<QData>(lwp[1])<<(static_cast<QData>(VL_WORDSIZE)) ))
|
||||
#define _VL_SET_QII(ld,rd) ( (static_cast<QData>(ld)<<VL_ULL(32)) | static_cast<QData>(rd) )
|
||||
( (static_cast<QData>((lwp)[0])) \
|
||||
| (static_cast<QData>((lwp)[1]) << (static_cast<QData>(VL_WORDSIZE)) ))
|
||||
#define _VL_SET_QII(ld,rd) ((static_cast<QData>(ld)<<VL_ULL(32)) | static_cast<QData>(rd))
|
||||
|
||||
/// Return FILE* from IData
|
||||
extern FILE* VL_CVT_I_FP(IData lhs);
|
||||
@@ -670,7 +698,7 @@ extern void _VL_DEBUG_PRINT_W(int lbits, WDataInP iwp);
|
||||
// Pli macros
|
||||
|
||||
#ifndef VL_TIME_PRECISION
|
||||
# define VL_TIME_PRECISION -12 ///< Timescale units only for for VPI return - picoseconds
|
||||
# define VL_TIME_PRECISION (-12) ///< Timescale units only for for VPI return - picoseconds
|
||||
#endif
|
||||
#ifndef VL_TIME_MULTIPLIER
|
||||
# define VL_TIME_MULTIPLIER 1
|
||||
@@ -800,31 +828,32 @@ static inline void VL_ASSIGNBIT_WO(int, int bit, WDataOutP owp, IData) VL_MT_SAF
|
||||
#define VL_ASSIGN_ISI(obits,vvar,svar) { (vvar) = VL_CLEAN_II((obits),(obits),(svar).read()); }
|
||||
#define VL_ASSIGN_QSQ(obits,vvar,svar) { (vvar) = VL_CLEAN_QQ((obits),(obits),(svar).read()); }
|
||||
|
||||
#define VL_ASSIGN_ISW(obits,od,svar) { \
|
||||
od = (svar.read().get_word(0)) & VL_MASK_I(obits); \
|
||||
}
|
||||
#define VL_ASSIGN_QSW(obits,od,svar) { \
|
||||
od = ((static_cast<QData>(svar.read().get_word(1)))<<VL_WORDSIZE | svar.read().get_word(0)) \
|
||||
& VL_MASK_Q(obits); \
|
||||
}
|
||||
#define VL_ASSIGN_WSW(obits,owp,svar) { \
|
||||
int words = VL_WORDS_I(obits); \
|
||||
for (int i=0; i < words; ++i) owp[i] = svar.read().get_word(i); \
|
||||
owp[words-1] &= VL_MASK_I(obits); \
|
||||
}
|
||||
#define VL_ASSIGN_ISW(obits,od,svar) { \
|
||||
(od) = ((svar).read().get_word(0)) & VL_MASK_I(obits); \
|
||||
}
|
||||
#define VL_ASSIGN_QSW(obits,od,svar) { \
|
||||
(od) = ((static_cast<QData>((svar).read().get_word(1)))<<VL_WORDSIZE \
|
||||
| (svar).read().get_word(0)) \
|
||||
& VL_MASK_Q(obits); \
|
||||
}
|
||||
#define VL_ASSIGN_WSW(obits,owp,svar) { \
|
||||
int words = VL_WORDS_I(obits); \
|
||||
for (int i=0; i < words; ++i) (owp)[i] = (svar).read().get_word(i); \
|
||||
(owp)[words-1] &= VL_MASK_I(obits); \
|
||||
}
|
||||
|
||||
#define VL_ASSIGN_ISU(obits,vvar,svar) { (vvar) = VL_CLEAN_II((obits),(obits),(svar).read().to_uint()); }
|
||||
#define VL_ASSIGN_QSU(obits,vvar,svar) { (vvar) = VL_CLEAN_QQ((obits),(obits),(svar).read().to_uint64()); }
|
||||
#define VL_ASSIGN_WSB(obits,owp,svar) { \
|
||||
int words = VL_WORDS_I(obits); \
|
||||
sc_biguint<obits> _butemp = (svar).read(); \
|
||||
for (int i=0; i < words; ++i) { \
|
||||
int msb = ((i+1)*VL_WORDSIZE) - 1; \
|
||||
msb = (msb >= obits) ? (obits-1) : msb; \
|
||||
owp[i] = _butemp.range(msb,i*VL_WORDSIZE).to_uint(); \
|
||||
} \
|
||||
owp[words-1] &= VL_MASK_I(obits); \
|
||||
}
|
||||
#define VL_ASSIGN_WSB(obits,owp,svar) { \
|
||||
int words = VL_WORDS_I(obits); \
|
||||
sc_biguint<(obits)> _butemp = (svar).read(); \
|
||||
for (int i=0; i < words; ++i) { \
|
||||
int msb = ((i+1)*VL_WORDSIZE) - 1; \
|
||||
msb = (msb >= (obits)) ? ((obits)-1) : msb; \
|
||||
(owp)[i] = _butemp.range(msb,i*VL_WORDSIZE).to_uint(); \
|
||||
} \
|
||||
(owp)[words-1] &= VL_MASK_I(obits); \
|
||||
}
|
||||
|
||||
// Copying verilog format from systemc integers and bit vectors.
|
||||
// Set a SystemC variable
|
||||
@@ -832,36 +861,36 @@ static inline void VL_ASSIGNBIT_WO(int, int bit, WDataOutP owp, IData) VL_MT_SAF
|
||||
#define VL_ASSIGN_SII(obits,svar,vvar) { (svar).write(vvar); }
|
||||
#define VL_ASSIGN_SQQ(obits,svar,vvar) { (svar).write(vvar); }
|
||||
|
||||
#define VL_ASSIGN_SWI(obits,svar,rd) { \
|
||||
sc_bv<obits> _bvtemp; \
|
||||
_bvtemp.set_word(0,(rd)); \
|
||||
svar.write(_bvtemp); \
|
||||
}
|
||||
#define VL_ASSIGN_SWQ(obits,svar,rd) { \
|
||||
sc_bv<obits> _bvtemp; \
|
||||
_bvtemp.set_word(0, static_cast<IData>(rd)); \
|
||||
_bvtemp.set_word(1, static_cast<IData>((rd)>>VL_WORDSIZE)); \
|
||||
svar.write(_bvtemp); \
|
||||
}
|
||||
#define VL_ASSIGN_SWW(obits,svar,rwp) { \
|
||||
sc_bv<obits> _bvtemp; \
|
||||
for (int i=0; i < VL_WORDS_I(obits); ++i) _bvtemp.set_word(i,rwp[i]); \
|
||||
svar.write(_bvtemp); \
|
||||
}
|
||||
#define VL_ASSIGN_SWI(obits,svar,rd) { \
|
||||
sc_bv<(obits)> _bvtemp; \
|
||||
_bvtemp.set_word(0, (rd)); \
|
||||
(svar).write(_bvtemp); \
|
||||
}
|
||||
#define VL_ASSIGN_SWQ(obits,svar,rd) { \
|
||||
sc_bv<(obits)> _bvtemp; \
|
||||
_bvtemp.set_word(0, static_cast<IData>(rd)); \
|
||||
_bvtemp.set_word(1, static_cast<IData>((rd)>>VL_WORDSIZE)); \
|
||||
(svar).write(_bvtemp); \
|
||||
}
|
||||
#define VL_ASSIGN_SWW(obits,svar,rwp) { \
|
||||
sc_bv<(obits)> _bvtemp; \
|
||||
for (int i=0; i < VL_WORDS_I(obits); ++i) _bvtemp.set_word(i, (rwp)[i]); \
|
||||
(svar).write(_bvtemp); \
|
||||
}
|
||||
|
||||
#define VL_ASSIGN_SUI(obits,svar,rd) { (svar).write(rd); }
|
||||
#define VL_ASSIGN_SUQ(obits,svar,rd) { (svar).write(rd); }
|
||||
#define VL_ASSIGN_SBI(obits,svar,rd) { (svar).write(rd); }
|
||||
#define VL_ASSIGN_SBQ(obits,svar,rd) { (svar).write(rd); }
|
||||
#define VL_ASSIGN_SBW(obits,svar,rwp) { \
|
||||
sc_biguint<obits> _butemp; \
|
||||
for (int i=0; i < VL_WORDS_I(obits); ++i) { \
|
||||
int msb = ((i+1)*VL_WORDSIZE) - 1; \
|
||||
msb = (msb >= obits) ? (obits-1) : msb; \
|
||||
_butemp.range(msb,i*VL_WORDSIZE) = rwp[i]; \
|
||||
} \
|
||||
svar.write(_butemp); \
|
||||
}
|
||||
#define VL_ASSIGN_SBW(obits,svar,rwp) { \
|
||||
sc_biguint<(obits)> _butemp; \
|
||||
for (int i=0; i < VL_WORDS_I(obits); ++i) { \
|
||||
int msb = ((i+1)*VL_WORDSIZE) - 1; \
|
||||
msb = (msb >= (obits)) ? ((obits)-1) : msb; \
|
||||
_butemp.range(msb, i*VL_WORDSIZE) = (rwp)[i]; \
|
||||
} \
|
||||
(svar).write(_butemp); \
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Extending sizes
|
||||
@@ -929,8 +958,8 @@ static inline WDataOutP VL_EXTENDS_WW(int obits, int lbits, WDataOutP owp, WData
|
||||
// REDUCTION OPERATORS
|
||||
|
||||
// EMIT_RULE: VL_REDAND: oclean=clean; lclean==clean; obits=1;
|
||||
#define VL_REDAND_II(obits,lbits,lhs) (lhs == VL_MASK_I(lbits))
|
||||
#define VL_REDAND_IQ(obits,lbits,lhs) (lhs == VL_MASK_Q(lbits))
|
||||
#define VL_REDAND_II(obits,lbits,lhs) ((lhs) == VL_MASK_I(lbits))
|
||||
#define VL_REDAND_IQ(obits,lbits,lhs) ((lhs) == VL_MASK_Q(lbits))
|
||||
static inline IData VL_REDAND_IW(int, int lbits, WDataInP lwp) VL_MT_SAFE {
|
||||
int words = VL_WORDS_I(lbits);
|
||||
IData combine=lwp[0];
|
||||
@@ -940,8 +969,8 @@ static inline IData VL_REDAND_IW(int, int lbits, WDataInP lwp) VL_MT_SAFE {
|
||||
}
|
||||
|
||||
// EMIT_RULE: VL_REDOR: oclean=clean; lclean==clean; obits=1;
|
||||
#define VL_REDOR_I(lhs) (lhs!=0)
|
||||
#define VL_REDOR_Q(lhs) (lhs!=0)
|
||||
#define VL_REDOR_I(lhs) ((lhs)!=0)
|
||||
#define VL_REDOR_Q(lhs) ((lhs)!=0)
|
||||
static inline IData VL_REDOR_W(int words, WDataInP lwp) VL_MT_SAFE {
|
||||
IData equal=0;
|
||||
for (int i=0; i < words; ++i) equal |= lwp[i];
|
||||
|
||||
@@ -24,6 +24,8 @@ CFG_CXXFLAGS_STD_OLDEST = @CFG_CXXFLAGS_STD_OLDEST@
|
||||
CFG_CXXFLAGS_NO_UNUSED = @CFG_CXXFLAGS_NO_UNUSED@
|
||||
# Compiler flags that turn on extra warnings
|
||||
CFG_CXXFLAGS_WEXTRA = @CFG_CXXFLAGS_WEXTRA@
|
||||
# Linker libraries for multithreading
|
||||
CFG_LDLIBS_THREADS = @CFG_LDLIBS_THREADS@
|
||||
|
||||
######################################################################
|
||||
# Programs
|
||||
@@ -116,6 +118,7 @@ ifneq ($(VM_THREADS),0)
|
||||
ifneq ($(VM_THREADS),)
|
||||
# Need C++11 at least, so always default to newest
|
||||
CPPFLAGS += -DVL_THREADED $(CFG_CXXFLAGS_STD_NEWEST)
|
||||
LDLIBS += $(CFG_LDLIBS_THREADS)
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
+26
-26
@@ -24,9 +24,9 @@
|
||||
#include "verilated_cov.h"
|
||||
#include "verilated_cov_key.h"
|
||||
|
||||
#include <map>
|
||||
#include <deque>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedCovImpBase
|
||||
@@ -77,7 +77,7 @@ public:
|
||||
virtual void zero() const { *m_countp = 0; }
|
||||
// CONSTRUCTORS
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
VerilatedCoverItemSpec(T* countp) : m_countp(countp) { zero(); }
|
||||
VerilatedCoverItemSpec(T* countp) : m_countp(countp) { *m_countp = 0; }
|
||||
virtual ~VerilatedCoverItemSpec() {}
|
||||
};
|
||||
|
||||
@@ -167,8 +167,8 @@ private:
|
||||
// (foo.a.x, foo.b.y) => foo.*
|
||||
// (foo.a.x, foo.b) => foo.*
|
||||
if (old == add) return add;
|
||||
if (old == "") return add;
|
||||
if (add == "") return old;
|
||||
if (old.empty()) return add;
|
||||
if (add.empty()) return old;
|
||||
|
||||
const char* a = old.c_str();
|
||||
const char* b = add.c_str();
|
||||
@@ -261,18 +261,18 @@ public:
|
||||
}
|
||||
|
||||
// We assume there's always call to i/f/p in that order
|
||||
void inserti (VerilatedCovImpItem* itemp) VL_EXCLUDES(m_mutex) {
|
||||
void inserti(VerilatedCovImpItem* itemp) VL_EXCLUDES(m_mutex) {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
assert(!m_insertp);
|
||||
m_insertp = itemp;
|
||||
}
|
||||
void insertf (const char* filenamep, int lineno) VL_EXCLUDES(m_mutex) {
|
||||
void insertf(const char* filenamep, int lineno) VL_EXCLUDES(m_mutex) {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
m_insertFilenamep = filenamep;
|
||||
m_insertLineno = lineno;
|
||||
}
|
||||
void insertp (const char* ckeyps[MAX_KEYS],
|
||||
const char* valps[MAX_KEYS]) VL_EXCLUDES(m_mutex) {
|
||||
void insertp(const char* ckeyps[MAX_KEYS],
|
||||
const char* valps[MAX_KEYS]) VL_EXCLUDES(m_mutex) {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
assert(m_insertp);
|
||||
// First two key/vals are filename
|
||||
@@ -296,7 +296,7 @@ public:
|
||||
}
|
||||
// Ignore empty keys
|
||||
for (int i=0; i<MAX_KEYS; ++i) {
|
||||
if (keys[i]!="") {
|
||||
if (!keys[i].empty()) {
|
||||
for (int j=i+1; j<MAX_KEYS; ++j) {
|
||||
if (keys[i] == keys[j]) { // Duplicate key. Keep the last one
|
||||
keys[i] = "";
|
||||
@@ -309,7 +309,7 @@ public:
|
||||
int addKeynum=0;
|
||||
for (int i=0; i<MAX_KEYS; ++i) {
|
||||
const std::string key = keys[i];
|
||||
if (keys[i]!="") {
|
||||
if (!keys[i].empty()) {
|
||||
const std::string val = valps[i];
|
||||
//cout<<" "<<__FUNCTION__<<" "<<key<<" = "<<val<<endl;
|
||||
m_insertp->m_keys[addKeynum] = valueIndex(key);
|
||||
@@ -334,7 +334,7 @@ public:
|
||||
#endif
|
||||
selftest();
|
||||
|
||||
std::ofstream os (filename);
|
||||
std::ofstream os(filename);
|
||||
if (os.fail()) {
|
||||
std::string msg = std::string("%Error: Can't write '")+filename+"'";
|
||||
VL_FATAL_MT("",0,"",msg.c_str());
|
||||
@@ -391,7 +391,7 @@ public:
|
||||
for (EventMap::const_iterator it=eventCounts.begin(); it!=eventCounts.end(); ++it) {
|
||||
os<<"C '"<<std::dec;
|
||||
os<<it->first;
|
||||
if (it->second.first != "") os<<keyValueFormatter(VL_CIK_HIER,it->second.first);
|
||||
if (!it->second.first.empty()) os<<keyValueFormatter(VL_CIK_HIER,it->second.first);
|
||||
os<<"' "<<it->second.second;
|
||||
os<<std::endl;
|
||||
}
|
||||
@@ -424,12 +424,12 @@ void VerilatedCov::_insertf(const char* filename, int lineno) VL_MT_SAFE {
|
||||
}
|
||||
|
||||
#define K(n) const char* key ## n
|
||||
#define A(n) const char* key ## n, const char* val ## n // Argument list
|
||||
#define C(n) key ## n, val ## n // Calling argument list
|
||||
#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)) VL_MT_SAFE {
|
||||
#define A(n) const char* key ## n, const char* valp ## n // Argument list
|
||||
#define C(n) key ## n, valp ## n // Calling argument list
|
||||
#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)) 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,
|
||||
@@ -437,27 +437,27 @@ void VerilatedCov::_insertp (A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9),
|
||||
key20,key21,key22,key23,key24,key25,key26,key27,key28,key29};
|
||||
const char* valps[VerilatedCovImpBase::MAX_KEYS]
|
||||
= {NULL,NULL,NULL, // filename,lineno,page
|
||||
val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,
|
||||
val10,val11,val12,val13,val14,val15,val16,val17,val18,val19,
|
||||
val20,val21,val22,val23,val24,val25,val26,val27,val28,val29};
|
||||
valp0,valp1,valp2,valp3,valp4,valp5,valp6,valp7,valp8,valp9,
|
||||
valp10,valp11,valp12,valp13,valp14,valp15,valp16,valp17,valp18,valp19,
|
||||
valp20,valp21,valp22,valp23,valp24,valp25,valp26,valp27,valp28,valp29};
|
||||
VerilatedCovImp::imp().insertp(keyps, valps);
|
||||
}
|
||||
|
||||
// And versions with fewer arguments (oh for a language with named parameters!)
|
||||
void VerilatedCov::_insertp (A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9)) VL_MT_SAFE {
|
||||
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)) VL_MT_SAFE {
|
||||
void VerilatedCov::_insertp(A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9),
|
||||
A(10),A(11),A(12),A(13),A(14),A(15),A(16),A(17),A(18),A(19)) VL_MT_SAFE {
|
||||
_insertp(C(0),C(1),C(2),C(3),C(4),C(5),C(6),C(7),C(8),C(9),
|
||||
C(10),C(11),C(12),C(13),C(14),C(15),C(16),C(17),C(18),C(19),
|
||||
N(20),N(21),N(22),N(23),N(24),N(25),N(26),N(27),N(28),N(29));
|
||||
}
|
||||
// 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)) VL_MT_SAFE {
|
||||
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)) VL_MT_SAFE {
|
||||
std::string val2str = vlCovCvtToStr(val2);
|
||||
std::string val3str = vlCovCvtToStr(val3);
|
||||
_insertp(C(0),C(1),
|
||||
|
||||
+14
-14
@@ -73,7 +73,7 @@
|
||||
//=============================================================================
|
||||
/// Convert VL_COVER_INSERT value arguments to strings
|
||||
|
||||
template< class T> std::string vlCovCvtToStr (const T& t) VL_PURE {
|
||||
template< class T> std::string vlCovCvtToStr(const T& t) VL_PURE {
|
||||
std::ostringstream os; os<<t; return os.str();
|
||||
}
|
||||
|
||||
@@ -91,16 +91,16 @@ public:
|
||||
/// Return default filename
|
||||
static const char* defaultFilename() VL_PURE { return "coverage.dat"; }
|
||||
/// Write all coverage data to a file
|
||||
static void write (const char* filenamep = defaultFilename()) VL_MT_SAFE;
|
||||
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) VL_MT_SAFE;
|
||||
static void _inserti (vluint64_t* itemp) VL_MT_SAFE;
|
||||
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) VL_MT_SAFE;
|
||||
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
|
||||
@@ -108,15 +108,15 @@ public:
|
||||
#define K(n) const char* key ## n
|
||||
#define A(n) const char* key ## n, const char* valp ## n // Argument list
|
||||
#define D(n) const char* key ## n = NULL, const char* valp ## n = NULL // Argument list
|
||||
static void _insertp (D(0),D(1),D(2),D(3),D(4),D(5),D(6),D(7),D(8),D(9));
|
||||
static void _insertp (A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9)
|
||||
,A(10),D(11),D(12),D(13),D(14),D(15),D(16),D(17),D(18),D(19));
|
||||
static void _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),D(21),D(22),D(23),D(24),D(25),D(26),D(27),D(28),D(29));
|
||||
static void _insertp(D(0),D(1),D(2),D(3),D(4),D(5),D(6),D(7),D(8),D(9));
|
||||
static void _insertp(A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9)
|
||||
,A(10),D(11),D(12),D(13),D(14),D(15),D(16),D(17),D(18),D(19));
|
||||
static void _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),D(21),D(22),D(23),D(24),D(25),D(26),D(27),D(28),D(29));
|
||||
// Backward compatibility for Verilator
|
||||
static void _insertp (A(0), A(1), K(2),int val2, K(3),int val3,
|
||||
K(4),const std::string& val4, A(5),A(6));
|
||||
static void _insertp(A(0), A(1), K(2),int val2, K(3),int val3,
|
||||
K(4),const std::string& val4, A(5),A(6));
|
||||
|
||||
#undef K
|
||||
#undef A
|
||||
@@ -124,7 +124,7 @@ public:
|
||||
/// Clear coverage points (and call delete on all items)
|
||||
static void clear() VL_MT_SAFE;
|
||||
/// Clear items not matching the provided string
|
||||
static void clearNonMatch (const char* matchp) VL_MT_SAFE;
|
||||
static void clearNonMatch(const char* matchp) VL_MT_SAFE;
|
||||
/// Zero coverage points
|
||||
static void zero() VL_MT_SAFE;
|
||||
};
|
||||
|
||||
+33
-33
@@ -104,39 +104,39 @@ VLCOVGEN_ITEM("name=>'weight', short=>'w', group=>0, default=>undef, descr
|
||||
class VerilatedCovKey {
|
||||
public:
|
||||
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;
|
||||
if (key == "col1") return VL_CIK_COL1;
|
||||
if (key == "col1_name") return VL_CIK_COL1_NAME;
|
||||
if (key == "col2") return VL_CIK_COL2;
|
||||
if (key == "col2_name") return VL_CIK_COL2_NAME;
|
||||
if (key == "col3") return VL_CIK_COL3;
|
||||
if (key == "col3_name") return VL_CIK_COL3_NAME;
|
||||
if (key == "column") return VL_CIK_COLUMN;
|
||||
if (key == "comment") return VL_CIK_COMMENT;
|
||||
if (key == "filename") return VL_CIK_FILENAME;
|
||||
if (key == "groupcmt") return VL_CIK_GROUPCMT;
|
||||
if (key == "groupdesc") return VL_CIK_GROUPDESC;
|
||||
if (key == "groupname") return VL_CIK_GROUPNAME;
|
||||
if (key == "hier") return VL_CIK_HIER;
|
||||
if (key == "limit") return VL_CIK_LIMIT;
|
||||
if (key == "lineno") return VL_CIK_LINENO;
|
||||
if (key == "per_instance") return VL_CIK_PER_INSTANCE;
|
||||
if (key == "row0") return VL_CIK_ROW0;
|
||||
if (key == "row0_name") return VL_CIK_ROW0_NAME;
|
||||
if (key == "row1") return VL_CIK_ROW1;
|
||||
if (key == "row1_name") return VL_CIK_ROW1_NAME;
|
||||
if (key == "row2") return VL_CIK_ROW2;
|
||||
if (key == "row2_name") return VL_CIK_ROW2_NAME;
|
||||
if (key == "row3") return VL_CIK_ROW3;
|
||||
if (key == "row3_name") return VL_CIK_ROW3_NAME;
|
||||
if (key == "table") return VL_CIK_TABLE;
|
||||
if (key == "thresh") return VL_CIK_THRESH;
|
||||
if (key == "type") return VL_CIK_TYPE;
|
||||
if (key == "weight") return VL_CIK_WEIGHT;
|
||||
// VLCOVGEN_SHORT_AUTO_EDIT_END
|
||||
return key;
|
||||
// VLCOVGEN_SHORT_AUTO_EDIT_BEGIN
|
||||
if (key == "col0") return VL_CIK_COL0;
|
||||
if (key == "col0_name") return VL_CIK_COL0_NAME;
|
||||
if (key == "col1") return VL_CIK_COL1;
|
||||
if (key == "col1_name") return VL_CIK_COL1_NAME;
|
||||
if (key == "col2") return VL_CIK_COL2;
|
||||
if (key == "col2_name") return VL_CIK_COL2_NAME;
|
||||
if (key == "col3") return VL_CIK_COL3;
|
||||
if (key == "col3_name") return VL_CIK_COL3_NAME;
|
||||
if (key == "column") return VL_CIK_COLUMN;
|
||||
if (key == "comment") return VL_CIK_COMMENT;
|
||||
if (key == "filename") return VL_CIK_FILENAME;
|
||||
if (key == "groupcmt") return VL_CIK_GROUPCMT;
|
||||
if (key == "groupdesc") return VL_CIK_GROUPDESC;
|
||||
if (key == "groupname") return VL_CIK_GROUPNAME;
|
||||
if (key == "hier") return VL_CIK_HIER;
|
||||
if (key == "limit") return VL_CIK_LIMIT;
|
||||
if (key == "lineno") return VL_CIK_LINENO;
|
||||
if (key == "per_instance") return VL_CIK_PER_INSTANCE;
|
||||
if (key == "row0") return VL_CIK_ROW0;
|
||||
if (key == "row0_name") return VL_CIK_ROW0_NAME;
|
||||
if (key == "row1") return VL_CIK_ROW1;
|
||||
if (key == "row1_name") return VL_CIK_ROW1_NAME;
|
||||
if (key == "row2") return VL_CIK_ROW2;
|
||||
if (key == "row2_name") return VL_CIK_ROW2_NAME;
|
||||
if (key == "row3") return VL_CIK_ROW3;
|
||||
if (key == "row3_name") return VL_CIK_ROW3_NAME;
|
||||
if (key == "table") return VL_CIK_TABLE;
|
||||
if (key == "thresh") return VL_CIK_THRESH;
|
||||
if (key == "type") return VL_CIK_TYPE;
|
||||
if (key == "weight") return VL_CIK_WEIGHT;
|
||||
// VLCOVGEN_SHORT_AUTO_EDIT_END
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
//=========================================================================
|
||||
|
||||
#define _VERILATED_DPI_CPP_
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated_dpi.h"
|
||||
#include "verilated_imp.h"
|
||||
@@ -720,14 +721,16 @@ void svPutLogicArrElem3(const svOpenArrayHandle d, svLogic value, int indx1, int
|
||||
|
||||
svScope svGetScope() {
|
||||
if (VL_UNLIKELY(!Verilated::dpiInContext())) { _VL_SVDPI_CONTEXT_WARN(); return NULL; }
|
||||
return (svScope)Verilated::dpiScope();
|
||||
// NOLINTNEXTLINE(google-readability-casting)
|
||||
return (svScope)(Verilated::dpiScope());
|
||||
}
|
||||
|
||||
svScope svSetScope(const svScope scope) {
|
||||
const VerilatedScope* prevScopep = Verilated::dpiScope();
|
||||
const VerilatedScope* vscopep = reinterpret_cast<const VerilatedScope*>(scope);
|
||||
Verilated::dpiScope(vscopep);
|
||||
return (svScope)prevScopep;
|
||||
// NOLINTNEXTLINE(google-readability-casting)
|
||||
return (svScope)(prevScopep);
|
||||
}
|
||||
|
||||
const char* svGetNameFromScope(const svScope scope) {
|
||||
@@ -736,7 +739,8 @@ const char* svGetNameFromScope(const svScope scope) {
|
||||
}
|
||||
|
||||
svScope svGetScopeFromName(const char* scopeName) {
|
||||
return (svScope)VerilatedImp::scopeFind(scopeName);
|
||||
// NOLINTNEXTLINE(google-readability-casting)
|
||||
return (svScope)(VerilatedImp::scopeFind(scopeName));
|
||||
}
|
||||
|
||||
int svPutUserData(const svScope scope, void* userKey, void* userData) {
|
||||
|
||||
@@ -28,8 +28,10 @@
|
||||
#ifndef _VERILATED_DPI_H_
|
||||
#define _VERILATED_DPI_H_ 1 ///< Header Guard
|
||||
|
||||
#include "verilated.h" // Also presumably included by caller
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h" // Also presumably included by caller
|
||||
#include "verilated_sym_props.h"
|
||||
|
||||
#include "svdpi.h"
|
||||
|
||||
//===================================================================
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//=============================================================================
|
||||
//
|
||||
// THIS MODULE IS PUBLICLY LICENSED
|
||||
//
|
||||
// Copyright 2001-2018 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.
|
||||
//
|
||||
// This is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
// for more details.
|
||||
//
|
||||
//=============================================================================
|
||||
///
|
||||
/// \file
|
||||
/// \brief C++ Tracing in FST Format
|
||||
///
|
||||
//=============================================================================
|
||||
// SPDIFF_OFF
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h"
|
||||
#include "verilated_fst_c.h"
|
||||
|
||||
// Include the GTKWave implementation directly
|
||||
#include "gtkwave/fastlz.c"
|
||||
#include "gtkwave/fstapi.c"
|
||||
#include "gtkwave/lz4.c"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <ctime>
|
||||
#include <fcntl.h>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#if defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
|
||||
# include <io.h>
|
||||
#else
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
|
||||
class VerilatedFstCallInfo {
|
||||
protected:
|
||||
friend class VerilatedFst;
|
||||
VerilatedFstCallback_t m_initcb; ///< Initialization Callback function
|
||||
VerilatedFstCallback_t m_fullcb; ///< Full Dumping Callback function
|
||||
VerilatedFstCallback_t m_changecb; ///< Incremental Dumping Callback function
|
||||
void* m_userthis; ///< Fake "this" for caller
|
||||
vluint32_t m_code; ///< Starting code number
|
||||
// CONSTRUCTORS
|
||||
VerilatedFstCallInfo(VerilatedFstCallback_t icb, VerilatedFstCallback_t fcb,
|
||||
VerilatedFstCallback_t changecb,
|
||||
void* ut, vluint32_t code)
|
||||
: m_initcb(icb), m_fullcb(fcb), m_changecb(changecb), m_userthis(ut), m_code(code) {};
|
||||
~VerilatedFstCallInfo() {}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFst
|
||||
|
||||
VerilatedFst::VerilatedFst(void* fst)
|
||||
: m_fst(fst),
|
||||
m_fullDump(true),
|
||||
m_scopeEscape('.') {}
|
||||
|
||||
void VerilatedFst::open(const char* filename) VL_MT_UNSAFE {
|
||||
m_assertOne.check();
|
||||
m_fst = fstWriterCreate(filename, 1);
|
||||
m_curScope.clear();
|
||||
|
||||
for (vluint32_t ent = 0; ent< m_callbacks.size(); ++ent) {
|
||||
VerilatedFstCallInfo* cip = m_callbacks[ent];
|
||||
cip->m_code = 1;
|
||||
(cip->m_initcb)(this, cip->m_userthis, cip->m_code);
|
||||
}
|
||||
|
||||
// Clear the scope stack
|
||||
std::list<std::string>::iterator it = m_curScope.begin();
|
||||
while (it != m_curScope.end()) {
|
||||
fstWriterSetUpscope(m_fst);
|
||||
it = m_curScope.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedFst::module(const std::string& name) {
|
||||
m_module = name;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Decl
|
||||
|
||||
void VerilatedFst::declDTypeEnum(int dtypenum, const char* name, vluint32_t elements,
|
||||
unsigned int minValbits,
|
||||
const char** itemNamesp, const char** itemValuesp) {
|
||||
fstEnumHandle enumNum = fstWriterCreateEnumTable(m_fst, name, elements,
|
||||
minValbits, itemNamesp, itemValuesp);
|
||||
m_local2fstdtype[dtypenum] = enumNum;
|
||||
}
|
||||
|
||||
void VerilatedFst::declSymbol(vluint32_t code, const char* name,
|
||||
int dtypenum, fstVarDir vardir, fstVarType vartype,
|
||||
int arraynum, vluint32_t len) {
|
||||
std::pair<Code2SymbolType::iterator, bool> p
|
||||
= m_code2symbol.insert(std::make_pair(code, static_cast<fstHandle>(NULL)));
|
||||
std::istringstream nameiss(name);
|
||||
std::istream_iterator<std::string> beg(nameiss), end;
|
||||
std::list<std::string> tokens(beg, end); // Split name
|
||||
std::string symbol_name(tokens.back());
|
||||
tokens.pop_back(); // Remove symbol name from hierarchy
|
||||
tokens.insert(tokens.begin(), m_module); // Add current module to the hierarchy
|
||||
|
||||
// Find point where current and new scope diverge
|
||||
std::list<std::string>::iterator cur_it = m_curScope.begin();
|
||||
std::list<std::string>::iterator new_it = tokens.begin();
|
||||
while (cur_it != m_curScope.end() && new_it != tokens.end()) {
|
||||
if (*cur_it != *new_it)
|
||||
break;
|
||||
++cur_it;
|
||||
++new_it;
|
||||
}
|
||||
|
||||
// Go back to the common point
|
||||
while (cur_it != m_curScope.end()) {
|
||||
fstWriterSetUpscope(m_fst);
|
||||
cur_it = m_curScope.erase(cur_it);
|
||||
}
|
||||
|
||||
// Follow the hierarchy of the new variable from the common scope point
|
||||
while (new_it != tokens.end()) {
|
||||
fstWriterSetScope(m_fst, FST_ST_VCD_SCOPE, new_it->c_str(), NULL);
|
||||
m_curScope.push_back(*new_it);
|
||||
new_it = tokens.erase(new_it);
|
||||
}
|
||||
|
||||
std::stringstream name_ss;
|
||||
name_ss << symbol_name;
|
||||
if (arraynum >= 0)
|
||||
name_ss << "(" << arraynum << ")";
|
||||
std::string name_str = name_ss.str();
|
||||
|
||||
if (dtypenum > 0) {
|
||||
fstEnumHandle enumNum = m_local2fstdtype[dtypenum];
|
||||
fstWriterEmitEnumTableRef(m_fst, enumNum);
|
||||
}
|
||||
if (p.second) { // New
|
||||
p.first->second = fstWriterCreateVar(m_fst, vartype, vardir, len, name_str.c_str(), 0);
|
||||
assert(p.first->second);
|
||||
} else { // Alias
|
||||
fstWriterCreateVar(m_fst, vartype, vardir, len, name_str.c_str(), p.first->second);
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Callbacks
|
||||
|
||||
void VerilatedFst::addCallback(
|
||||
VerilatedFstCallback_t initcb, VerilatedFstCallback_t fullcb,
|
||||
VerilatedFstCallback_t changecb, void* userthis) VL_MT_UNSAFE_ONE {
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(isOpen())) {
|
||||
std::string msg = std::string("Internal: ")+__FILE__+"::"+__FUNCTION__+" called with already open file";
|
||||
VL_FATAL_MT(__FILE__,__LINE__,"",msg.c_str());
|
||||
}
|
||||
VerilatedFstCallInfo* vci = new VerilatedFstCallInfo(initcb, fullcb, changecb, userthis, 1);
|
||||
m_callbacks.push_back(vci);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Dumping
|
||||
|
||||
void VerilatedFst::dump(vluint64_t timeui) {
|
||||
if (!isOpen()) return;
|
||||
if (VL_UNLIKELY(m_fullDump)) {
|
||||
m_fullDump = false; // No need for more full dumps
|
||||
for (vluint32_t ent = 0; ent< m_callbacks.size(); ++ent) {
|
||||
VerilatedFstCallInfo* cip = m_callbacks[ent];
|
||||
(cip->m_fullcb)(this, cip->m_userthis, cip->m_code);
|
||||
}
|
||||
return;
|
||||
}
|
||||
fstWriterEmitTimeChange(m_fst, timeui);
|
||||
for (vluint32_t ent = 0; ent< m_callbacks.size(); ++ent) {
|
||||
VerilatedFstCallInfo* cip = m_callbacks[ent];
|
||||
(cip->m_changecb)(this, cip->m_userthis, cip->m_code);
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Helpers
|
||||
|
||||
char* VerilatedFst::word2Str(vluint32_t newval, int bits) {
|
||||
m_valueStrBuffer.resize(bits+1);
|
||||
char* s = m_valueStrBuffer.data();
|
||||
for (int i = 0; i < bits; ++i) {
|
||||
*s = '0' + ((newval>>(bits-i-1))&1);
|
||||
++s;
|
||||
}
|
||||
*s = '\0';
|
||||
return m_valueStrBuffer.data();
|
||||
}
|
||||
|
||||
char* VerilatedFst::quad2Str(vluint64_t newval, int bits) {
|
||||
m_valueStrBuffer.resize(bits+1);
|
||||
char* s = m_valueStrBuffer.data();
|
||||
for (int i = 0; i < bits; ++i) {
|
||||
*s = '0' + ((newval>>(bits-i-1))&1);
|
||||
++s;
|
||||
}
|
||||
*s = '\0';
|
||||
return m_valueStrBuffer.data();
|
||||
}
|
||||
|
||||
char* VerilatedFst::array2Str(const vluint32_t* newval, int bits) {
|
||||
int bq = bits/32, br = bits%32;
|
||||
m_valueStrBuffer.resize(bits+1);
|
||||
char* s = m_valueStrBuffer.data();
|
||||
for (int w = bq-1; w >= 0; --w) {
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
*s = '0' + ((newval[w]>>(32-i-1))&1);
|
||||
++s;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < br; ++i) {
|
||||
*s = '0' + ((newval[bq]>>(br-i-1))&1);
|
||||
++s;
|
||||
}
|
||||
*s = '\0';
|
||||
return m_valueStrBuffer.data();
|
||||
}
|
||||
|
||||
//********************************************************************
|
||||
// Local Variables:
|
||||
// End:
|
||||
@@ -0,0 +1,230 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//=============================================================================
|
||||
//
|
||||
// THIS MODULE IS PUBLICLY LICENSED
|
||||
//
|
||||
// Copyright 2001-2018 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.
|
||||
//
|
||||
// This is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
// for more details.
|
||||
//
|
||||
//=============================================================================
|
||||
///
|
||||
/// \file
|
||||
/// \brief C++ Tracing in FST Format
|
||||
///
|
||||
//=============================================================================
|
||||
// SPDIFF_OFF
|
||||
|
||||
#ifndef _VERILATED_FST_C_H_
|
||||
#define _VERILATED_FST_C_H_ 1
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h"
|
||||
|
||||
#include "gtkwave/fstapi.h"
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class VerilatedFst;
|
||||
class VerilatedFstCallInfo;
|
||||
typedef void (*VerilatedFstCallback_t)(VerilatedFst* vcdp, void* userthis, vluint32_t code);
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFst
|
||||
/// Base class to create a Verilator FST dump
|
||||
/// This is an internally used class - see VerilatedFstC for what to call from applications
|
||||
|
||||
class VerilatedFst {
|
||||
typedef std::map<vluint32_t, fstHandle> Code2SymbolType;
|
||||
typedef std::map<int, fstEnumHandle> Local2FstDtype;
|
||||
typedef std::vector<VerilatedFstCallInfo*> CallbackVec;
|
||||
private:
|
||||
void* m_fst;
|
||||
VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread
|
||||
bool m_fullDump;
|
||||
char m_scopeEscape;
|
||||
std::string m_module;
|
||||
CallbackVec m_callbacks; ///< Routines to perform dumping
|
||||
Code2SymbolType m_code2symbol;
|
||||
Local2FstDtype m_local2fstdtype;
|
||||
std::list<std::string> m_curScope;
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedFst);
|
||||
void declSymbol(vluint32_t code, const char* name,
|
||||
int dtypenum, fstVarDir vardir, fstVarType vartype,
|
||||
int arraynum, vluint32_t len);
|
||||
// helpers
|
||||
std::vector<char> m_valueStrBuffer;
|
||||
char* word2Str(vluint32_t newval, int bits);
|
||||
char* quad2Str(vluint64_t newval, int bits);
|
||||
char* array2Str(const vluint32_t *newval, int bits);
|
||||
public:
|
||||
explicit VerilatedFst(void* fst=NULL);
|
||||
~VerilatedFst() { if (m_fst == NULL) { fstWriterClose(m_fst); } }
|
||||
bool isOpen() const { return m_fst != NULL; }
|
||||
void open(const char* filename) VL_MT_UNSAFE;
|
||||
void flush() VL_MT_UNSAFE { fstWriterFlushContext(m_fst); }
|
||||
void close() VL_MT_UNSAFE {
|
||||
m_assertOne.check();
|
||||
fstWriterClose(m_fst);
|
||||
m_fst = NULL;
|
||||
}
|
||||
// void set_time_unit(const char* unitp); ///< Set time units (s/ms, defaults to ns)
|
||||
// void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||
|
||||
// void set_time_resolution(const char* unitp); ///< Set time resolution (s/ms, defaults to ns)
|
||||
// void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||
|
||||
// double timescaleToDouble(const char* unitp);
|
||||
// std::string doubleToTimescale(double value);
|
||||
|
||||
/// Change character that splits scopes. Note whitespace are ALWAYS escapes.
|
||||
void scopeEscape(char flag) { m_scopeEscape = flag; }
|
||||
/// Is this an escape?
|
||||
bool isScopeEscape(char c) { return isspace(c) || c==m_scopeEscape; }
|
||||
/// Inside dumping routines, called each cycle to make the dump
|
||||
void dump(vluint64_t timeui);
|
||||
/// Inside dumping routines, declare callbacks for tracings
|
||||
void addCallback(VerilatedFstCallback_t initcb, VerilatedFstCallback_t fullcb,
|
||||
VerilatedFstCallback_t changecb,
|
||||
void* userthis) VL_MT_UNSAFE_ONE;
|
||||
|
||||
/// Inside dumping routines, declare a module
|
||||
void module(const std::string& name);
|
||||
/// Inside dumping routines, declare a data type
|
||||
void declDTypeEnum(int dtypenum, const char* name, vluint32_t elements,
|
||||
unsigned int minValbits,
|
||||
const char** itemNamesp, const char** itemValuesp);
|
||||
/// Inside dumping routines, declare a signal
|
||||
void declBit(vluint32_t code, const char* name,
|
||||
int dtypenum, fstVarDir vardir, fstVarType vartype,
|
||||
int arraynum) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, arraynum, 1);
|
||||
}
|
||||
void declBus(vluint32_t code, const char* name,
|
||||
int dtypenum, fstVarDir vardir, fstVarType vartype,
|
||||
int arraynum, int msb, int lsb) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, arraynum, msb - lsb + 1);
|
||||
}
|
||||
void declDouble(vluint32_t code, const char* name,
|
||||
int dtypenum, fstVarDir vardir, fstVarType vartype,
|
||||
int arraynum) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, arraynum, 2);
|
||||
}
|
||||
void declFloat(vluint32_t code, const char* name,
|
||||
int dtypenum, fstVarDir vardir, fstVarType vartype,
|
||||
int arraynum) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, arraynum, 1);
|
||||
}
|
||||
void declQuad(vluint32_t code, const char* name,
|
||||
int dtypenum, fstVarDir vardir, fstVarType vartype,
|
||||
int arraynum, int msb, int lsb) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, arraynum, msb - lsb + 1);
|
||||
}
|
||||
void declArray(vluint32_t code, const char* name,
|
||||
int dtypenum, fstVarDir vardir, fstVarType vartype,
|
||||
int arraynum, int msb, int lsb) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, arraynum, msb - lsb + 1);
|
||||
}
|
||||
|
||||
/// Inside dumping routines, dump one signal if it has changed
|
||||
void chgBit(vluint32_t code, const vluint32_t newval) {
|
||||
fstWriterEmitValueChange(m_fst, m_code2symbol[code], newval ? "1" : "0");
|
||||
}
|
||||
void chgBus(vluint32_t code, const vluint32_t newval, int bits) {
|
||||
fstWriterEmitValueChange(m_fst, m_code2symbol[code], word2Str(newval, bits));
|
||||
}
|
||||
void chgDouble(vluint32_t code, const double newval) {
|
||||
double val = newval;
|
||||
fstWriterEmitValueChange(m_fst, m_code2symbol[code], &val);
|
||||
}
|
||||
void chgFloat(vluint32_t code, const float newval) {
|
||||
double val = (double)newval;
|
||||
fstWriterEmitValueChange(m_fst, m_code2symbol[code], &val);
|
||||
}
|
||||
void chgQuad(vluint32_t code, const vluint64_t newval, int bits) {
|
||||
fstWriterEmitValueChange(m_fst, m_code2symbol[code], quad2Str(newval, bits));
|
||||
}
|
||||
void chgArray(vluint32_t code, const vluint32_t* newval, int bits) {
|
||||
fstWriterEmitValueChange(m_fst, m_code2symbol[code], array2Str(newval, bits));
|
||||
}
|
||||
|
||||
void fullBit(vluint32_t code, const vluint32_t newval) { chgBit(code, newval); }
|
||||
void fullBus(vluint32_t code, const vluint32_t newval, int bits) { chgBus(code, newval, bits); }
|
||||
void fullDouble(vluint32_t code, const double newval) { chgDouble(code, newval); }
|
||||
void fullFloat(vluint32_t code, const float newval) { chgFloat(code, newval); }
|
||||
void fullQuad(vluint32_t code, const vluint64_t newval, int bits) { chgQuad(code, newval, bits); }
|
||||
void fullArray(vluint32_t code, const vluint32_t* newval, int bits) { chgArray(code, newval, bits); }
|
||||
|
||||
void declTriBit (vluint32_t code, const char* name, int arraynum);
|
||||
void declTriBus (vluint32_t code, const char* name, int arraynum, int msb, int lsb);
|
||||
void declTriQuad (vluint32_t code, const char* name, int arraynum, int msb, int lsb);
|
||||
void declTriArray (vluint32_t code, const char* name, int arraynum, int msb, int lsb);
|
||||
void fullTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri);
|
||||
void fullTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits);
|
||||
void fullTriQuad(vluint32_t code, const vluint64_t newval, const vluint32_t newtri, int bits);
|
||||
void fullTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip, int bits);
|
||||
void fullBitX(vluint32_t code);
|
||||
void fullBusX(vluint32_t code, int bits);
|
||||
void fullQuadX(vluint32_t code, int bits);
|
||||
void fullArrayX(vluint32_t code, int bits);
|
||||
void chgTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri);
|
||||
void chgTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits);
|
||||
void chgTriQuad(vluint32_t code, const vluint64_t newval, const vluint32_t newtri, int bits);
|
||||
void chgTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip, int bits);
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFstC
|
||||
/// Create a FST 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 VerilatedFstC {
|
||||
VerilatedFst m_sptrace; ///< Trace file being created
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedFstC);
|
||||
public:
|
||||
explicit VerilatedFstC(void* filep=NULL) : m_sptrace(filep) {}
|
||||
~VerilatedFstC() {}
|
||||
public:
|
||||
// ACCESSORS
|
||||
/// Is file open?
|
||||
bool isOpen() const { return m_sptrace.isOpen(); }
|
||||
// METHODS
|
||||
/// Open a new FST file
|
||||
void open(const char* filename) VL_MT_UNSAFE_ONE { m_sptrace.open(filename); }
|
||||
/// Close dump
|
||||
void close() VL_MT_UNSAFE_ONE { m_sptrace.close(); }
|
||||
/// Flush dump
|
||||
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
|
||||
/// conversion warnings. It's better to use a vluint64_t time instead.
|
||||
void dump(double timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
void dump(vluint32_t timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
void dump(int timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
/// Set time units (s/ms, defaults to ns)
|
||||
/// See also VL_TIME_PRECISION, and VL_TIME_MULTIPLIER in verilated.h
|
||||
void set_time_unit(const char* unit) { /* TODO */ }
|
||||
void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||
/// Set time resolution (s/ms, defaults to ns)
|
||||
/// See also VL_TIME_PRECISION, and VL_TIME_MULTIPLIER in verilated.h
|
||||
void set_time_resolution(const char* unit) { /* TODO */ }
|
||||
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||
|
||||
/// Internal class access
|
||||
inline VerilatedFst* spTrace() { return &m_sptrace; };
|
||||
};
|
||||
|
||||
#endif // guard
|
||||
@@ -65,15 +65,15 @@ inline IData VL_LEN_IN(const std::string& ld) { return ld.length(); }
|
||||
|
||||
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,
|
||||
const std::string& ofilename,
|
||||
const std::string& filename,
|
||||
void* memp, IData start, IData end) VL_MT_SAFE;
|
||||
extern void VL_WRITEMEM_N(bool hex, int width, int depth, int array_lsb,
|
||||
const std::string& ofilename,
|
||||
const std::string& filename,
|
||||
const void* memp, IData start, IData end) VL_MT_SAFE;
|
||||
extern IData VL_SSCANF_INX(int lbits, const std::string& ld, const char* formatp, ...) VL_MT_SAFE;
|
||||
extern void VL_SFORMAT_X(int obits_ignored, std::string& output, const char* formatp, ...) VL_MT_SAFE;
|
||||
extern 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;
|
||||
extern IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_MT_SAFE;
|
||||
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, IData& rdr) VL_MT_SAFE {
|
||||
WData rwp[2]; // WData must always be at least 2
|
||||
IData got = VL_VALUEPLUSARGS_INW(rbits,ld,rwp);
|
||||
|
||||
@@ -33,11 +33,9 @@
|
||||
#include "verilated_heavy.h"
|
||||
#include "verilated_syms.h"
|
||||
|
||||
#include <map>
|
||||
#include <deque>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#ifdef VL_THREADED
|
||||
# include <functional>
|
||||
# include <queue>
|
||||
@@ -239,6 +237,9 @@ public:
|
||||
}
|
||||
private:
|
||||
static void commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(s_s.m_argMutex);
|
||||
static void commandArgVl(const std::string& arg);
|
||||
static bool commandArgVlValue(const std::string& arg,
|
||||
const std::string& prefix, std::string& valuer);
|
||||
|
||||
public:
|
||||
// METHODS - user scope tracking
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//=============================================================================
|
||||
//
|
||||
// THIS MODULE IS PUBLICLY LICENSED
|
||||
//
|
||||
// Copyright 2001-2018 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.
|
||||
//
|
||||
// This is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
// for more details.
|
||||
//
|
||||
//=============================================================================
|
||||
///
|
||||
/// \file
|
||||
/// \brief C++ Tracing in LXT2 Format
|
||||
///
|
||||
//=============================================================================
|
||||
// SPDIFF_OFF
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h"
|
||||
#include "verilated_lxt2_c.h"
|
||||
|
||||
// Include the GTKWave implementation directly
|
||||
#include "gtkwave/lxt2_write.cpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <ctime>
|
||||
#include <fcntl.h>
|
||||
#include <sstream>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#if defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
|
||||
# include <io.h>
|
||||
#else
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
|
||||
class VerilatedLxt2CallInfo {
|
||||
protected:
|
||||
friend class VerilatedLxt2;
|
||||
VerilatedLxt2Callback_t m_initcb; ///< Initialization Callback function
|
||||
VerilatedLxt2Callback_t m_fullcb; ///< Full Dumping Callback function
|
||||
VerilatedLxt2Callback_t m_changecb; ///< Incremental Dumping Callback function
|
||||
void* m_userthis; ///< Fake "this" for caller
|
||||
vluint32_t m_code; ///< Starting code number
|
||||
// CONSTRUCTORS
|
||||
VerilatedLxt2CallInfo (VerilatedLxt2Callback_t icb, VerilatedLxt2Callback_t fcb,
|
||||
VerilatedLxt2Callback_t changecb,
|
||||
void* ut, vluint32_t code)
|
||||
: m_initcb(icb), m_fullcb(fcb), m_changecb(changecb), m_userthis(ut), m_code(code) {};
|
||||
~VerilatedLxt2CallInfo() {}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedLxt2
|
||||
|
||||
VerilatedLxt2::VerilatedLxt2(lxt2_wr_trace* lxt2)
|
||||
: m_lxt2(lxt2),
|
||||
m_fullDump(true),
|
||||
m_scopeEscape('.') {}
|
||||
|
||||
void VerilatedLxt2::open(const char* filename) VL_MT_UNSAFE {
|
||||
m_assertOne.check();
|
||||
m_lxt2 = lxt2_wr_init(filename);
|
||||
for (vluint32_t ent = 0; ent< m_callbacks.size(); ent++) {
|
||||
VerilatedLxt2CallInfo* cip = m_callbacks[ent];
|
||||
cip->m_code = 1;
|
||||
(cip->m_initcb)(this, cip->m_userthis, cip->m_code);
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedLxt2::module(const std::string& name) {
|
||||
m_module = name;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Decl
|
||||
|
||||
void VerilatedLxt2::declSymbol(vluint32_t code, const char* name, int arraynum, int msb, int lsb, int flags) {
|
||||
if (msb == 0 && lsb == 0) {
|
||||
msb = lsb = -1;
|
||||
}
|
||||
std::pair<Code2SymbolType::iterator, bool> p
|
||||
= m_code2symbol.insert(std::make_pair(code, (lxt2_wr_symbol*)(NULL)));
|
||||
std::stringstream name_ss;
|
||||
name_ss <<m_module<<"."<<name;
|
||||
if (arraynum >= 0) {
|
||||
name_ss <<"("<<arraynum<<")";
|
||||
}
|
||||
std::string name_s = name_ss.str();
|
||||
for (std::string::iterator it = name_s.begin(); it != name_s.end(); ++it) {
|
||||
if (isScopeEscape(*it)) {
|
||||
*it = '.';
|
||||
}
|
||||
}
|
||||
if (p.second) { // New
|
||||
p.first->second = lxt2_wr_symbol_add(m_lxt2, name_s.c_str(), 0, msb, lsb, flags);
|
||||
assert(p.first->second);
|
||||
} else { // Alias
|
||||
lxt2_wr_symbol_alias(m_lxt2, p.first->second->name, name_s.c_str(), msb, lsb);
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Callbacks
|
||||
|
||||
void VerilatedLxt2::addCallback(
|
||||
VerilatedLxt2Callback_t initcb, VerilatedLxt2Callback_t fullcb,
|
||||
VerilatedLxt2Callback_t changecb, void* userthis) VL_MT_UNSAFE_ONE {
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(isOpen())) {
|
||||
std::string msg = std::string("Internal: ")+__FILE__+"::"+__FUNCTION__+" called with already open file";
|
||||
VL_FATAL_MT(__FILE__,__LINE__,"",msg.c_str());
|
||||
}
|
||||
VerilatedLxt2CallInfo* vci = new VerilatedLxt2CallInfo(initcb, fullcb, changecb, userthis, 1);
|
||||
m_callbacks.push_back(vci);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Dumping
|
||||
|
||||
void VerilatedLxt2::dump(vluint64_t timeui) {
|
||||
if (!isOpen()) return;
|
||||
if (VL_UNLIKELY(m_fullDump)) {
|
||||
m_fullDump = false; // No need for more full dumps
|
||||
for (vluint32_t ent = 0; ent< m_callbacks.size(); ent++) {
|
||||
VerilatedLxt2CallInfo* cip = m_callbacks[ent];
|
||||
(cip->m_fullcb)(this, cip->m_userthis, cip->m_code);
|
||||
}
|
||||
return;
|
||||
}
|
||||
lxt2_wr_set_time64(m_lxt2, timeui);
|
||||
for (vluint32_t ent = 0; ent< m_callbacks.size(); ++ent) {
|
||||
VerilatedLxt2CallInfo* cip = m_callbacks[ent];
|
||||
(cip->m_changecb)(this, cip->m_userthis, cip->m_code);
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Helpers
|
||||
|
||||
char* VerilatedLxt2::quad2Str(vluint64_t newval, int bits) {
|
||||
m_valueStrBuffer.resize(bits+1);
|
||||
char* s = m_valueStrBuffer.data();
|
||||
for (int i = 0; i < bits; ++i) {
|
||||
*s = '0' + ((newval>>(bits-i-1))&1);
|
||||
++s;
|
||||
}
|
||||
*s = '\0';
|
||||
return m_valueStrBuffer.data();
|
||||
}
|
||||
|
||||
char* VerilatedLxt2::array2Str(const vluint32_t* newval, int bits) {
|
||||
int bq = bits/32, br = bits%32;
|
||||
m_valueStrBuffer.resize(bits+1);
|
||||
char* s = m_valueStrBuffer.data();
|
||||
for (int w = bq-1; w >= 0; --w) {
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
*s = '0' + ((newval[w]>>(32-i-1))&1);
|
||||
++s;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < br; ++i) {
|
||||
*s = '0' + ((newval[bq]>>(br-i-1))&1);
|
||||
++s;
|
||||
}
|
||||
*s = '\0';
|
||||
return m_valueStrBuffer.data();
|
||||
}
|
||||
|
||||
//********************************************************************
|
||||
// Local Variables:
|
||||
// End:
|
||||
@@ -0,0 +1,205 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//=============================================================================
|
||||
//
|
||||
// THIS MODULE IS PUBLICLY LICENSED
|
||||
//
|
||||
// Copyright 2001-2018 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.
|
||||
//
|
||||
// This is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
// for more details.
|
||||
//
|
||||
//=============================================================================
|
||||
///
|
||||
/// \file
|
||||
/// \brief C++ Tracing in LXT2 Format
|
||||
///
|
||||
//=============================================================================
|
||||
// SPDIFF_OFF
|
||||
|
||||
#ifndef _VERILATED_LXT2_C_H_
|
||||
#define _VERILATED_LXT2_C_H_ 1
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h"
|
||||
|
||||
#include "gtkwave/lxt2_write.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class VerilatedLxt2;
|
||||
class VerilatedLxt2CallInfo;
|
||||
typedef void (*VerilatedLxt2Callback_t)(VerilatedLxt2* vcdp, void* userthis, vluint32_t code);
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedLxt2
|
||||
/// Base class to create a Verilator LXT2 dump
|
||||
/// This is an internally used class - see VerilatedLxt2C for what to call from applications
|
||||
|
||||
class VerilatedLxt2 {
|
||||
typedef std::map<vluint32_t, lxt2_wr_symbol*> Code2SymbolType;
|
||||
typedef std::vector<VerilatedLxt2CallInfo*> CallbackVec;
|
||||
private:
|
||||
lxt2_wr_trace* m_lxt2;
|
||||
VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread
|
||||
bool m_fullDump;
|
||||
char m_scopeEscape;
|
||||
std::string m_module;
|
||||
CallbackVec m_callbacks; ///< Routines to perform dumping
|
||||
Code2SymbolType m_code2symbol;
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedLxt2);
|
||||
void declSymbol(vluint32_t code, const char* name, int arraynum, int msb, int lsb, int flags);
|
||||
// helpers
|
||||
std::vector<char> m_valueStrBuffer;
|
||||
char* quad2Str(vluint64_t newval, int bits);
|
||||
char* array2Str(const vluint32_t *newval, int bits);
|
||||
public:
|
||||
explicit VerilatedLxt2(lxt2_wr_trace* lxt2=NULL);
|
||||
~VerilatedLxt2() { if (m_lxt2 == NULL) { lxt2_wr_close(m_lxt2); } }
|
||||
bool isOpen() const { return m_lxt2 != NULL; }
|
||||
void open(const char* filename) VL_MT_UNSAFE;
|
||||
void flush() VL_MT_UNSAFE { lxt2_wr_flush(m_lxt2); }
|
||||
void close() VL_MT_UNSAFE {
|
||||
m_assertOne.check();
|
||||
lxt2_wr_close(m_lxt2);
|
||||
m_lxt2 = NULL;
|
||||
}
|
||||
// void set_time_unit(const char* unitp); ///< Set time units (s/ms, defaults to ns)
|
||||
// void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||
|
||||
// void set_time_resolution(const char* unitp); ///< Set time resolution (s/ms, defaults to ns)
|
||||
// void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||
|
||||
// double timescaleToDouble(const char* unitp);
|
||||
// std::string doubleToTimescale(double value);
|
||||
|
||||
/// Change character that splits scopes. Note whitespace are ALWAYS escapes.
|
||||
void scopeEscape(char flag) { m_scopeEscape = flag; }
|
||||
/// Is this an escape?
|
||||
bool isScopeEscape(char c) { return isspace(c) || c==m_scopeEscape; }
|
||||
/// Inside dumping routines, called each cycle to make the dump
|
||||
void dump(vluint64_t timeui);
|
||||
/// Inside dumping routines, declare callbacks for tracings
|
||||
void addCallback(VerilatedLxt2Callback_t initcb, VerilatedLxt2Callback_t fullcb,
|
||||
VerilatedLxt2Callback_t changecb,
|
||||
void* userthis) VL_MT_UNSAFE_ONE;
|
||||
|
||||
/// Inside dumping routines, declare a module
|
||||
void module(const std::string& name);
|
||||
/// Inside dumping routines, declare a signal
|
||||
void declBit(vluint32_t code, const char* name, int arraynum) {
|
||||
declSymbol(code, name, arraynum, 0, 0, LXT2_WR_SYM_F_BITS);
|
||||
}
|
||||
void declBus(vluint32_t code, const char* name, int arraynum, int msb, int lsb) {
|
||||
declSymbol(code, name, arraynum, msb, lsb, LXT2_WR_SYM_F_BITS);
|
||||
}
|
||||
void declDouble(vluint32_t code, const char* name, int arraynum) {
|
||||
declSymbol(code, name, arraynum, 63, 0, LXT2_WR_SYM_F_DOUBLE);
|
||||
}
|
||||
void declFloat(vluint32_t code, const char* name, int arraynum) {
|
||||
declSymbol(code, name, arraynum, 63, 0, LXT2_WR_SYM_F_DOUBLE);
|
||||
}
|
||||
void declQuad(vluint32_t code, const char* name, int arraynum, int msb, int lsb) {
|
||||
declSymbol(code, name, arraynum, msb, lsb, LXT2_WR_SYM_F_BITS);
|
||||
}
|
||||
void declArray(vluint32_t code, const char* name, int arraynum, int msb, int lsb) {
|
||||
declSymbol(code, name, arraynum, msb, lsb, LXT2_WR_SYM_F_BITS);
|
||||
}
|
||||
|
||||
/// Inside dumping routines, dump one signal if it has changed
|
||||
void chgBit(vluint32_t code, const vluint32_t newval) {
|
||||
lxt2_wr_emit_value_int(m_lxt2, m_code2symbol[code], 0, newval);
|
||||
}
|
||||
void chgBus(vluint32_t code, const vluint32_t newval, int bits) {
|
||||
lxt2_wr_emit_value_int(m_lxt2, m_code2symbol[code], 0, newval);
|
||||
}
|
||||
void chgDouble(vluint32_t code, const double newval) {
|
||||
lxt2_wr_emit_value_double(m_lxt2, m_code2symbol[code], 0, newval);
|
||||
}
|
||||
void chgFloat(vluint32_t code, const float newval) {
|
||||
lxt2_wr_emit_value_double(m_lxt2, m_code2symbol[code], 0, newval);
|
||||
}
|
||||
void chgQuad(vluint32_t code, const vluint64_t newval, int bits) {
|
||||
lxt2_wr_emit_value_bit_string(m_lxt2, m_code2symbol[code], 0, quad2Str(newval, bits));
|
||||
}
|
||||
void chgArray(vluint32_t code, const vluint32_t* newval, int bits) {
|
||||
lxt2_wr_emit_value_bit_string(m_lxt2, m_code2symbol[code], 0, array2Str(newval, bits));
|
||||
}
|
||||
|
||||
void fullBit(vluint32_t code, const vluint32_t newval) { chgBit(code, newval); }
|
||||
void fullBus(vluint32_t code, const vluint32_t newval, int bits) { chgBus(code, newval, bits); }
|
||||
void fullDouble(vluint32_t code, const double newval) { chgDouble(code, newval); }
|
||||
void fullFloat(vluint32_t code, const float newval) { chgFloat(code, newval); }
|
||||
void fullQuad(vluint32_t code, const vluint64_t newval, int bits) { chgQuad(code, newval, bits); }
|
||||
void fullArray(vluint32_t code, const vluint32_t* newval, int bits) { chgArray(code, newval, bits); }
|
||||
|
||||
void declTriBit (vluint32_t code, const char* name, int arraynum);
|
||||
void declTriBus (vluint32_t code, const char* name, int arraynum, int msb, int lsb);
|
||||
void declTriQuad (vluint32_t code, const char* name, int arraynum, int msb, int lsb);
|
||||
void declTriArray (vluint32_t code, const char* name, int arraynum, int msb, int lsb);
|
||||
void fullTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri);
|
||||
void fullTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits);
|
||||
void fullTriQuad(vluint32_t code, const vluint64_t newval, const vluint32_t newtri, int bits);
|
||||
void fullTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip, int bits);
|
||||
void fullBitX(vluint32_t code);
|
||||
void fullBusX(vluint32_t code, int bits);
|
||||
void fullQuadX(vluint32_t code, int bits);
|
||||
void fullArrayX(vluint32_t code, int bits);
|
||||
void chgTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri);
|
||||
void chgTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits);
|
||||
void chgTriQuad(vluint32_t code, const vluint64_t newval, const vluint32_t newtri, int bits);
|
||||
void chgTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip, int bits);
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedLxt2C
|
||||
/// Create a LXT2 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 VerilatedLxt2C {
|
||||
VerilatedLxt2 m_sptrace; ///< Trace file being created
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedLxt2C);
|
||||
public:
|
||||
explicit VerilatedLxt2C(lxt2_wr_trace* filep=NULL) : m_sptrace(filep) {}
|
||||
~VerilatedLxt2C() {}
|
||||
public:
|
||||
// ACCESSORS
|
||||
/// Is file open?
|
||||
bool isOpen() const { return m_sptrace.isOpen(); }
|
||||
// METHODS
|
||||
/// Open a new LXT2 file
|
||||
void open(const char* filename) VL_MT_UNSAFE_ONE { m_sptrace.open(filename); }
|
||||
/// Close dump
|
||||
void close() VL_MT_UNSAFE_ONE { m_sptrace.close(); }
|
||||
/// Flush dump
|
||||
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
|
||||
/// conversion warnings. It's better to use a vluint64_t time instead.
|
||||
void dump(double timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
void dump(vluint32_t timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
void dump(int timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
/// Set time units (s/ms, defaults to ns)
|
||||
/// See also VL_TIME_PRECISION, and VL_TIME_MULTIPLIER in verilated.h
|
||||
void set_time_unit(const char* unit) { /* TODO */ }
|
||||
void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||
/// Set time resolution (s/ms, defaults to ns)
|
||||
/// See also VL_TIME_PRECISION, and VL_TIME_MULTIPLIER in verilated.h
|
||||
void set_time_resolution(const char* unit) { /* TODO */ }
|
||||
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||
|
||||
/// Internal class access
|
||||
inline VerilatedLxt2* spTrace() { return &m_sptrace; };
|
||||
};
|
||||
|
||||
#endif // guard
|
||||
+13
-10
@@ -23,8 +23,8 @@
|
||||
#include "verilated.h"
|
||||
#include "verilated_save.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <cerrno>
|
||||
#include <fcntl.h>
|
||||
|
||||
#if defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
|
||||
# include <io.h>
|
||||
@@ -38,6 +38,9 @@
|
||||
#ifndef O_NONBLOCK
|
||||
# define O_NONBLOCK 0
|
||||
#endif
|
||||
#ifndef O_CLOEXEC
|
||||
# define O_CLOEXEC 0
|
||||
#endif
|
||||
|
||||
// CONSTANTS
|
||||
static const char* const VLTSAVE_HEADER_STR = "verilatorsave01\n"; ///< Value of first bytes of each file
|
||||
@@ -48,9 +51,9 @@ static const char* const VLTSAVE_TRAILER_STR = "vltsaved"; ///< Value of last by
|
||||
//=============================================================================
|
||||
// Searalization
|
||||
|
||||
bool VerilatedDeserialize::readDiffers (const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
|
||||
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;
|
||||
const vluint8_t* __restrict dp = static_cast<const vluint8_t* __restrict>(datap);
|
||||
vluint8_t miss = 0;
|
||||
while (size--) {
|
||||
miss |= (*dp++ ^ *m_cp++);
|
||||
@@ -58,7 +61,7 @@ bool VerilatedDeserialize::readDiffers (const void* __restrict datap, size_t siz
|
||||
return (miss!=0);
|
||||
}
|
||||
|
||||
VerilatedDeserialize& VerilatedDeserialize::readAssert (const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
|
||||
VerilatedDeserialize& VerilatedDeserialize::readAssert(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
|
||||
if (VL_UNLIKELY(readDiffers(datap,size))) {
|
||||
std::string fn = filename();
|
||||
std::string msg = std::string("Can't deserialize save-restore file as was made from different model");
|
||||
@@ -119,8 +122,8 @@ void VerilatedSave::open(const char* filenamep) VL_MT_UNSAFE_ONE {
|
||||
assert(0); // Not supported yet.
|
||||
} else {
|
||||
// cppcheck-suppress duplicateExpression
|
||||
m_fd = ::open (filenamep, O_CREAT|O_WRONLY|O_TRUNC|O_LARGEFILE|O_NONBLOCK
|
||||
, 0666);
|
||||
m_fd = ::open(filenamep, O_CREAT|O_WRONLY|O_TRUNC|O_LARGEFILE|O_NONBLOCK|O_CLOEXEC
|
||||
, 0666);
|
||||
if (m_fd<0) {
|
||||
// User code can check isOpen()
|
||||
m_isOpen = false;
|
||||
@@ -142,8 +145,8 @@ void VerilatedRestore::open(const char* filenamep) VL_MT_UNSAFE_ONE {
|
||||
assert(0); // Not supported yet.
|
||||
} else {
|
||||
// cppcheck-suppress duplicateExpression
|
||||
m_fd = ::open (filenamep, O_CREAT|O_RDONLY|O_LARGEFILE
|
||||
, 0666);
|
||||
m_fd = ::open(filenamep, O_CREAT|O_RDONLY|O_LARGEFILE|O_CLOEXEC
|
||||
, 0666);
|
||||
if (m_fd<0) {
|
||||
// User code can check isOpen()
|
||||
m_isOpen = false;
|
||||
@@ -184,7 +187,7 @@ void VerilatedSave::flush() VL_MT_UNSAFE_ONE {
|
||||
ssize_t remaining = (m_cp - wp);
|
||||
if (remaining==0) break;
|
||||
errno = 0;
|
||||
ssize_t got = ::write (m_fd, wp, remaining);
|
||||
ssize_t got = ::write(m_fd, wp, remaining);
|
||||
if (got>0) {
|
||||
wp += got;
|
||||
} else if (got < 0) {
|
||||
@@ -213,7 +216,7 @@ void VerilatedRestore::fill() VL_MT_UNSAFE_ONE {
|
||||
ssize_t remaining = (m_bufp+bufferSize() - m_endp);
|
||||
if (remaining==0) break;
|
||||
errno = 0;
|
||||
ssize_t got = ::read (m_fd, m_endp, remaining);
|
||||
ssize_t got = ::read(m_fd, m_endp, remaining);
|
||||
if (got>0) {
|
||||
m_endp += got;
|
||||
} else if (got < 0) {
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#define _VERILATED_SC_H_ 1 ///< Header Guard
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "systemc.h"
|
||||
|
||||
//=============================================================================
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
#ifndef _VERILATED_SYM_PROPS_H_
|
||||
#define _VERILATED_SYM_PROPS_H_ 1 ///< Header Guard
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
//===========================================================================
|
||||
/// Verilator range
|
||||
/// Thread safety: Assume is constructed only with model, then any number of readers
|
||||
|
||||
@@ -31,9 +31,10 @@
|
||||
#ifndef _VERILATED_SYMS_H_
|
||||
#define _VERILATED_SYMS_H_ 1 ///< Header Guard
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated_heavy.h"
|
||||
#include "verilated_sym_props.h"
|
||||
|
||||
#include "verilated_heavy.h"
|
||||
#include <map>
|
||||
|
||||
//======================================================================
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//=============================================================================
|
||||
//
|
||||
// THIS MODULE IS PUBLICLY LICENSED
|
||||
//
|
||||
// Copyright 2012-2018 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.
|
||||
//
|
||||
// This is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
// for more details.
|
||||
//
|
||||
//=============================================================================
|
||||
///
|
||||
/// \file
|
||||
/// \brief Thread pool for verilated modules
|
||||
///
|
||||
//=============================================================================
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated_threads.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
std::atomic<vluint64_t> VlNotification::s_yields;
|
||||
|
||||
VL_THREAD_LOCAL VlThreadPool::ProfileTrace* VlThreadPool::t_profilep = NULL;
|
||||
|
||||
//=============================================================================
|
||||
// VlMTaskVertex
|
||||
|
||||
VlMTaskVertex::VlMTaskVertex(vluint32_t upstreamDepCount)
|
||||
: m_upstreamDepsDone(0),
|
||||
m_upstreamDepCount(upstreamDepCount) {
|
||||
assert(atomic_is_lock_free(&m_upstreamDepsDone));
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VlWorkerThread
|
||||
|
||||
VlWorkerThread::VlWorkerThread(VlThreadPool* poolp, bool profiling)
|
||||
: m_poolp(poolp)
|
||||
, m_profiling(profiling)
|
||||
, m_exiting(false)
|
||||
// Must init this last -- after setting up fields that it might read:
|
||||
, m_cthread(startWorker, this) {}
|
||||
|
||||
VlWorkerThread::~VlWorkerThread() {
|
||||
m_exiting.store(true, std::memory_order_release);
|
||||
{
|
||||
VerilatedLockGuard lk(m_mutex);
|
||||
if (sleeping()) {
|
||||
wakeUp();
|
||||
}
|
||||
}
|
||||
// The thread should exit; join it.
|
||||
m_cthread.join();
|
||||
}
|
||||
|
||||
void VlWorkerThread::workerLoop() {
|
||||
if (VL_UNLIKELY(m_profiling)) {
|
||||
m_poolp->setupProfilingClientThread();
|
||||
}
|
||||
|
||||
VlNotification alarm;
|
||||
ExecRec work;
|
||||
work.m_fnp = NULL;
|
||||
|
||||
while (1) {
|
||||
bool sleep = false;
|
||||
if (VL_UNLIKELY(!work.m_fnp)) {
|
||||
// Look for work
|
||||
VerilatedLockGuard lk(m_mutex);
|
||||
if (VL_LIKELY(!m_ready.empty())) {
|
||||
dequeWork(&work);
|
||||
} else {
|
||||
// No work available, prepare to sleep. Pass alarm/work
|
||||
// into m_sleepAlarm so wakeUp will tall this function.
|
||||
//
|
||||
// Must modify m_sleepAlarm in the same critical section as
|
||||
// the check for ready work, otherwise we could race with
|
||||
// another thread enqueueing work and never be awoken.
|
||||
m_sleepAlarm.first = &alarm;
|
||||
m_sleepAlarm.second = &work;
|
||||
sleep = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Do this here, not above, to avoid a race with the destructor.
|
||||
if (VL_UNLIKELY(m_exiting.load(std::memory_order_acquire)))
|
||||
break;
|
||||
|
||||
if (VL_UNLIKELY(sleep)) {
|
||||
alarm.waitForNotification(); // ZZZzzzzz
|
||||
alarm.reset();
|
||||
}
|
||||
if (VL_LIKELY(work.m_fnp)) {
|
||||
work.m_fnp(work.m_evenCycle, work.m_sym);
|
||||
work.m_fnp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (VL_UNLIKELY(m_profiling)) {
|
||||
m_poolp->tearDownProfilingClientThread();
|
||||
}
|
||||
}
|
||||
|
||||
void VlWorkerThread::startWorker(VlWorkerThread* workerp) {
|
||||
workerp->workerLoop();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VlThreadPool
|
||||
|
||||
VlThreadPool::VlThreadPool(int nThreads, bool profiling)
|
||||
: m_profiling(profiling) {
|
||||
// --threads N passes nThreads=N-1, as the "main" threads counts as 1
|
||||
unsigned cpus = std::thread::hardware_concurrency();
|
||||
if (cpus < nThreads+1) {
|
||||
VL_PRINTF_MT("%%Warning: System has %u CPUs but model Verilated with"
|
||||
" --threads %d; may run slow.\n", cpus, nThreads+1);
|
||||
}
|
||||
// Create'em
|
||||
for (int i=0; i<nThreads; ++i) {
|
||||
m_workers.push_back(new VlWorkerThread(this, profiling));
|
||||
}
|
||||
// Set up a profile buffer for the current thread too -- on the
|
||||
// assumption that it's the same thread that calls eval and may be
|
||||
// donated to run mtasks during the eval.
|
||||
if (VL_UNLIKELY(m_profiling)) {
|
||||
setupProfilingClientThread();
|
||||
}
|
||||
}
|
||||
|
||||
VlThreadPool::~VlThreadPool() {
|
||||
for (int i = 0; i < m_workers.size(); ++i) {
|
||||
// Each ~WorkerThread will wait for its thread to exit.
|
||||
delete m_workers[i];
|
||||
}
|
||||
if (VL_UNLIKELY(m_profiling)) {
|
||||
tearDownProfilingClientThread();
|
||||
}
|
||||
}
|
||||
|
||||
void VlThreadPool::tearDownProfilingClientThread() {
|
||||
assert(t_profilep);
|
||||
delete t_profilep;
|
||||
t_profilep = NULL;
|
||||
}
|
||||
|
||||
void VlThreadPool::setupProfilingClientThread() {
|
||||
assert(!t_profilep);
|
||||
t_profilep = new ProfileTrace;
|
||||
// Reserve some space in the thread-local profiling buffer;
|
||||
// try not to malloc while collecting profiling.
|
||||
t_profilep->reserve(4096);
|
||||
{
|
||||
VerilatedLockGuard lk(m_mutex);
|
||||
m_allProfiles.insert(t_profilep);
|
||||
}
|
||||
}
|
||||
|
||||
void VlThreadPool::profileAppendAll(const VlProfileRec& rec) {
|
||||
VerilatedLockGuard lk(m_mutex);
|
||||
for (ProfileSet::iterator it = m_allProfiles.begin();
|
||||
it != m_allProfiles.end(); ++it) {
|
||||
// Every thread's profile trace gets a copy of rec.
|
||||
(*it)->emplace_back(rec);
|
||||
}
|
||||
}
|
||||
|
||||
void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed) {
|
||||
VerilatedLockGuard lk(m_mutex);
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("+prof+threads writing to '%s'\n", filenamep););
|
||||
|
||||
FILE* fp = fopen(filenamep, "w");
|
||||
if (VL_UNLIKELY(!fp)) {
|
||||
VL_FATAL_MT(filenamep, 0, "", "+prof+threads+file file not writable");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO Perhaps merge with verilated_coverage output format, so can
|
||||
// have a common merging and reporting tool, etc.
|
||||
fprintf(fp, "VLPROFTHREAD 1.0 # Verilator thread profile dump version 1.0\n");
|
||||
fprintf(fp, "VLPROF arg --threads %" VL_PRI64 "u\n",
|
||||
vluint64_t(m_workers.size()+1));
|
||||
fprintf(fp, "VLPROF arg +verilator+prof+threads+start+%" VL_PRI64 "u\n",
|
||||
Verilated::profThreadsStart());
|
||||
fprintf(fp, "VLPROF arg +verilator+prof+threads+window+%u\n",
|
||||
Verilated::profThreadsWindow());
|
||||
fprintf(fp, "VLPROF stat yields %" VL_PRI64 "u\n",
|
||||
VlNotification::yields());
|
||||
|
||||
vluint32_t thread_id = 0;
|
||||
for (ProfileSet::iterator pit = m_allProfiles.begin();
|
||||
pit != m_allProfiles.end(); ++pit) {
|
||||
++thread_id;
|
||||
|
||||
bool printing = false; // False while in warmup phase
|
||||
for (ProfileTrace::iterator eit = (*pit)->begin();
|
||||
eit != (*pit)->end(); ++eit) {
|
||||
switch (eit->m_type) {
|
||||
case VlProfileRec::TYPE_BARRIER:
|
||||
printing = true;
|
||||
break;
|
||||
case VlProfileRec::TYPE_MTASK_RUN:
|
||||
if (!printing) break;
|
||||
fprintf(fp, "VLPROF mtask %d"
|
||||
" start %" VL_PRI64"u end %" VL_PRI64"u elapsed %" VL_PRI64 "u"
|
||||
" predict_time %u cpu %u on thread %u\n",
|
||||
eit->m_mtaskId,
|
||||
eit->m_startTime,
|
||||
eit->m_endTime,
|
||||
(eit->m_endTime - eit->m_startTime),
|
||||
eit->m_predictTime,
|
||||
eit->m_cpu,
|
||||
thread_id);
|
||||
break;
|
||||
default: assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(fp, "VLPROF stat ticks %" VL_PRI64 "u\n",
|
||||
ticksElapsed);
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
@@ -0,0 +1,330 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//=============================================================================
|
||||
//
|
||||
// THIS MODULE IS PUBLICLY LICENSED
|
||||
//
|
||||
// Copyright 2012-2018 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.
|
||||
//
|
||||
// This is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
// for more details.
|
||||
//
|
||||
//=============================================================================
|
||||
///
|
||||
/// \file
|
||||
/// \brief Thread pool and profiling for Verilated modules
|
||||
///
|
||||
//=============================================================================
|
||||
|
||||
#ifndef _VERILATED_THREADS_H_
|
||||
#define _VERILATED_THREADS_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h" // for VerilatedMutex and clang annotations
|
||||
|
||||
#include <sched.h> // For sched_getcpu()
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#if defined(__APPLE__)
|
||||
# include <cpuid.h> // For __cpuid_count()
|
||||
#endif
|
||||
|
||||
// VlMTaskVertex and VlThreadpool will work with multiple symbol table types.
|
||||
// Since the type is opaque to VlMTaskVertex and VlThreadPool, represent it
|
||||
// as a void* here.
|
||||
typedef void* VlThrSymTab;
|
||||
|
||||
class VlNotification {
|
||||
// MEMBERS
|
||||
std::atomic<bool> m_notified; // Notification pending
|
||||
static std::atomic<vluint64_t> s_yields; // Statistics
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlNotification()
|
||||
: m_notified(false) {
|
||||
assert(atomic_is_lock_free(&m_notified));
|
||||
}
|
||||
~VlNotification() {}
|
||||
|
||||
// METHODS
|
||||
static vluint64_t yields() { return s_yields; }
|
||||
|
||||
// Block until notify() has occurred, then return.
|
||||
// If notify() has already occurred, return immediately.
|
||||
//
|
||||
// This is logically const: the object will remain in notified state
|
||||
// after WaitForNotification() returns, so you could notify more than
|
||||
// one thread of the same event.
|
||||
inline void waitForNotification() {
|
||||
unsigned ct = 0;
|
||||
while (VL_UNLIKELY(!notified())) {
|
||||
VL_CPU_RELAX();
|
||||
ct++;
|
||||
if (VL_UNLIKELY(ct > VL_LOCK_SPINS)) {
|
||||
ct = 0;
|
||||
++s_yields; // Statistics
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The 'inline' keyword here means nothing to the compiler, it's
|
||||
// implicit on methods defined within the class body anyway.
|
||||
//
|
||||
// 'inline' is attached the this method, and others in this file,
|
||||
// to remind humans that some routines in this file are called many
|
||||
// times per cycle in threaded mode. Such routines should be
|
||||
// inlinable; that's why they're declared in the .h and not the .cpp.
|
||||
inline bool notified() {
|
||||
return m_notified.load(std::memory_order_acquire);
|
||||
}
|
||||
// Set notified state. If state is already notified,
|
||||
// it remains so.
|
||||
inline void notify() {
|
||||
m_notified.store(true, std::memory_order_release);
|
||||
}
|
||||
// Reset the state to un-notified state, which is also the
|
||||
// state of a new Notification object.
|
||||
inline void reset() {
|
||||
m_notified.store(false, std::memory_order_relaxed);
|
||||
}
|
||||
};
|
||||
|
||||
typedef void (*VlExecFnp)(bool, VlThrSymTab);
|
||||
|
||||
/// Track dependencies for a single MTask.
|
||||
class VlMTaskVertex {
|
||||
// MEMBERS
|
||||
|
||||
// On even cycles, _upstreamDepsDone increases as upstream
|
||||
// dependencies complete. When it reaches _upstreamDepCount,
|
||||
// this MTaskVertex is ready.
|
||||
//
|
||||
// On odd cycles, _upstreamDepsDone decreases as upstream
|
||||
// dependencies complete, and when it reaches zero this MTaskVertex
|
||||
// is ready.
|
||||
//
|
||||
// An atomic is smaller than a mutex, and lock-free.
|
||||
//
|
||||
// (Why does the size of this class matter? If an mtask has many
|
||||
// downstream mtasks to notify, we hope these will pack into a
|
||||
// small number of cache lines to reduce the cost of pointer chasing
|
||||
// during done-notification. Nobody's quantified that cost though.
|
||||
// If we were really serious about shrinking this class, we could
|
||||
// use 16-bit types here...)
|
||||
std::atomic<vluint32_t> m_upstreamDepsDone;
|
||||
const vluint32_t m_upstreamDepCount;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
|
||||
// 'upstreamDepCount' is the number of upstream MTaskVertex's
|
||||
// that must notify this MTaskVertex before it will become ready
|
||||
// to run.
|
||||
explicit VlMTaskVertex(vluint32_t upstreamDepCount);
|
||||
~VlMTaskVertex() {}
|
||||
|
||||
// Upstream mtasks must call this when they complete.
|
||||
// Returns true when the current MTaskVertex becomes ready to execute,
|
||||
// false while it's still waiting on more dependencies.
|
||||
inline bool signalUpstreamDone(bool evenCycle) {
|
||||
if (evenCycle) {
|
||||
vluint32_t upstreamDepsDone
|
||||
= 1 + m_upstreamDepsDone.fetch_add(1, std::memory_order_release);
|
||||
assert(upstreamDepsDone <= m_upstreamDepCount);
|
||||
return (upstreamDepsDone == m_upstreamDepCount);
|
||||
} else {
|
||||
vluint32_t upstreamDepsDone_prev
|
||||
= m_upstreamDepsDone.fetch_sub(1, std::memory_order_release);
|
||||
assert(upstreamDepsDone_prev > 0);
|
||||
return (upstreamDepsDone_prev == 1);
|
||||
}
|
||||
}
|
||||
inline bool areUpstreamDepsDone(bool evenCycle) const {
|
||||
vluint32_t target = evenCycle ? m_upstreamDepCount : 0;
|
||||
return m_upstreamDepsDone.load(std::memory_order_acquire) == target;
|
||||
}
|
||||
inline void waitUntilUpstreamDone(bool evenCycle) const {
|
||||
while (VL_UNLIKELY(!areUpstreamDepsDone(evenCycle))) {
|
||||
VL_CPU_RELAX();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Profiling support
|
||||
class VlProfileRec {
|
||||
protected:
|
||||
friend class VlThreadPool;
|
||||
enum VlProfileE {
|
||||
TYPE_MTASK_RUN,
|
||||
TYPE_BARRIER
|
||||
};
|
||||
VlProfileE m_type; // Record type
|
||||
vluint32_t m_mtaskId; // Mtask we're logging
|
||||
vluint32_t m_predictTime; // How long scheduler predicted would take
|
||||
vluint64_t m_startTime; // Tick at start of execution
|
||||
vluint64_t m_endTime; // Tick at end of execution
|
||||
unsigned m_cpu; // Execution CPU number (at start anyways)
|
||||
public:
|
||||
class Barrier {};
|
||||
VlProfileRec() {}
|
||||
explicit VlProfileRec(Barrier) {
|
||||
m_type = TYPE_BARRIER;
|
||||
m_mtaskId = 0;
|
||||
m_predictTime = 0;
|
||||
m_startTime = 0;
|
||||
m_cpu = getcpu();
|
||||
}
|
||||
void startRecord(vluint64_t time, uint32_t mtask, uint32_t predict) {
|
||||
m_type = VlProfileRec::TYPE_MTASK_RUN;
|
||||
m_mtaskId = mtask;
|
||||
m_predictTime = predict;
|
||||
m_startTime = time;
|
||||
m_cpu = getcpu();
|
||||
}
|
||||
void endRecord(vluint64_t time) {
|
||||
m_endTime = time;
|
||||
}
|
||||
static int getcpu() { // Return current executing CPU
|
||||
#if defined(__linux)
|
||||
return sched_getcpu();
|
||||
#elif defined(__APPLE__)
|
||||
vluint32_t info[4];
|
||||
__cpuid_count(1, 0, info[0], info[1], info[2], info[3]);
|
||||
/* info[1] is EBX, bits 24-31 are APIC ID */
|
||||
if ((info[3] & (1 << 9)) == 0) {
|
||||
return -1; /* no APIC on chip */
|
||||
} else {
|
||||
return (unsigned)info[1] >> 24;
|
||||
}
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class VlThreadPool;
|
||||
|
||||
class VlWorkerThread {
|
||||
private:
|
||||
// TYPES
|
||||
struct ExecRec {
|
||||
VlExecFnp m_fnp; // Function to execute
|
||||
VlThrSymTab m_sym; // Symbol table to execute
|
||||
bool m_evenCycle; // Even/odd for flag alternation
|
||||
ExecRec() : m_fnp(NULL), m_sym(NULL), m_evenCycle(false) {}
|
||||
ExecRec(VlExecFnp fnp, bool evenCycle, VlThrSymTab sym)
|
||||
: m_fnp(fnp), m_sym(sym), m_evenCycle(evenCycle) {}
|
||||
};
|
||||
|
||||
// MEMBERS
|
||||
VerilatedMutex m_mutex;
|
||||
|
||||
// Why a vector? We expect the pending list to be very short, typically
|
||||
// 0 or 1 or 2, so popping from the front shouldn't be
|
||||
// expensive. Revisit if we ever have longer queues...
|
||||
std::vector<ExecRec> m_ready VL_GUARDED_BY(m_mutex);
|
||||
|
||||
VlThreadPool* m_poolp; // Our associated thread pool
|
||||
|
||||
// If values stored are non-NULL, the thread is asleep pending new
|
||||
// work. If the thread is not asleep, both parts of m_sleepAlarm must
|
||||
// be NULL.
|
||||
std::pair<VlNotification*, ExecRec*> m_sleepAlarm VL_GUARDED_BY(m_mutex);
|
||||
|
||||
bool m_profiling; // Is profiling enabled?
|
||||
std::atomic<bool> m_exiting; // Worker thread should exit
|
||||
std::thread m_cthread; // Underlying C++ thread record
|
||||
|
||||
VL_UNCOPYABLE(VlWorkerThread);
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit VlWorkerThread(VlThreadPool* poolp, bool profiling);
|
||||
~VlWorkerThread();
|
||||
|
||||
// METHODS
|
||||
inline void dequeWork(ExecRec* workp) VL_REQUIRES(m_mutex) {
|
||||
// As noted above this is inefficient if our ready list is ever
|
||||
// long (but it shouldn't be)
|
||||
*workp = m_ready.front();
|
||||
m_ready.erase(m_ready.begin());
|
||||
}
|
||||
inline void wakeUp() VL_REQUIRES(m_mutex) {
|
||||
VlNotification* notifyp = m_sleepAlarm.first;
|
||||
m_sleepAlarm.first = NULL; // NULL+NULL means wake
|
||||
m_sleepAlarm.second = NULL;
|
||||
notifyp->notify();
|
||||
}
|
||||
inline bool sleeping() VL_REQUIRES(m_mutex) {
|
||||
return (m_sleepAlarm.first != NULL);
|
||||
}
|
||||
inline void addTask(VlExecFnp fnp, bool evenCycle, VlThrSymTab sym) {
|
||||
VerilatedLockGuard lk(m_mutex);
|
||||
m_ready.emplace_back(fnp, evenCycle, sym);
|
||||
if (VL_LIKELY(sleeping())) { // Generally queue is waiting for work
|
||||
// Awaken thread
|
||||
dequeWork(m_sleepAlarm.second);
|
||||
wakeUp();
|
||||
}
|
||||
}
|
||||
void workerLoop();
|
||||
static void startWorker(VlWorkerThread* workerp);
|
||||
};
|
||||
|
||||
class VlThreadPool {
|
||||
// TYPES
|
||||
typedef std::vector<VlProfileRec> ProfileTrace;
|
||||
typedef std::set<ProfileTrace*> ProfileSet;
|
||||
|
||||
// MEMBERS
|
||||
std::vector<VlWorkerThread*> m_workers; // our workers
|
||||
bool m_profiling; // is profiling enabled?
|
||||
|
||||
// Support profiling -- we can append records of profiling events
|
||||
// to this vector with very low overhead, and then dump them out
|
||||
// later. This prevents the overhead of printf/malloc/IO from
|
||||
// corrupting the profiling data. It's super cheap to append
|
||||
// a VlProfileRec struct on the end of a pre-allocated vector;
|
||||
// this is the only cost we pay in real-time during a profiling cycle.
|
||||
static VL_THREAD_LOCAL ProfileTrace* t_profilep;
|
||||
ProfileSet m_allProfiles VL_GUARDED_BY(m_mutex);
|
||||
VerilatedMutex m_mutex;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
// Construct a thread pool with 'nThreads' dedicated threads. The thread
|
||||
// pool will create these threads and make them available to execute tasks
|
||||
// via this->workerp(index)->addTask(...)
|
||||
VlThreadPool(int nThreads, bool profiling);
|
||||
~VlThreadPool();
|
||||
|
||||
// METHODS
|
||||
inline int numThreads() const {
|
||||
return m_workers.size();
|
||||
}
|
||||
inline VlWorkerThread* workerp(int index) {
|
||||
assert(index >= 0);
|
||||
assert(index < m_workers.size());
|
||||
return m_workers[index];
|
||||
}
|
||||
inline VlProfileRec* profileAppend() {
|
||||
t_profilep->emplace_back();
|
||||
return &(t_profilep->back());
|
||||
}
|
||||
void profileAppendAll(const VlProfileRec& rec);
|
||||
void profileDump(const char* filenamep, vluint64_t ticksElapsed);
|
||||
// In profiling mode, each executing thread must call
|
||||
// this once to setup profiling state:
|
||||
void setupProfilingClientThread();
|
||||
void tearDownProfilingClientThread();
|
||||
private:
|
||||
VL_UNCOPYABLE(VlThreadPool);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,547 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: pre-C++11 replacements for std::unordered_set
|
||||
// and std::unordered_map.
|
||||
//
|
||||
// Code available from: http://www.veripool.org/verilator
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2018 by Wilson Snyder. This program is free software; you can
|
||||
// redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
//
|
||||
// Verilator is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
|
||||
//*************************************************************************
|
||||
// This file has clones of the std::unordered_set and std::unordered_map
|
||||
// hash table types. They are here so that Verilator can use hash tables
|
||||
// in pre-C++11 compilers, and the same client code can link against the
|
||||
// std:: types when they are available.
|
||||
//
|
||||
// The implementations in this file do not implement the complete APIs
|
||||
// of the std:: types. Nor are they correct in every detail,
|
||||
// notably, the const_iterators do not enforce constness. We can extend
|
||||
// these implementations to cover more of the std API as needed.
|
||||
//
|
||||
// TODO: In the future, when Verilator requires C++11 to compile,
|
||||
// remove this entire file and switch to the std:: types.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef _V3_UNORDERED_SET_MAP_H_
|
||||
#define _V3_UNORDERED_SET_MAP_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include <list>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
// Abstract 'vl_hash' and 'vl_equal_to' templates.
|
||||
template <typename T> struct vl_hash {
|
||||
size_t operator()(const T& k) const;
|
||||
};
|
||||
|
||||
template <typename T> struct vl_equal_to {
|
||||
bool operator()(const T& a, const T& b) const;
|
||||
};
|
||||
|
||||
// Specializations of 'vl_hash' and 'vl_equal_to'.
|
||||
inline size_t vl_hash_bytes(const void* vbufp, size_t nbytes) {
|
||||
const vluint8_t* bufp = static_cast<const vluint8_t*>(vbufp);
|
||||
size_t hash = 0;
|
||||
for (size_t i = 0; i < nbytes; i++) {
|
||||
hash = bufp[i] + 31u * hash; // the K&R classic!
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
template <> inline size_t
|
||||
vl_hash<unsigned int>::operator()(const unsigned int& k) const {
|
||||
return k;
|
||||
}
|
||||
|
||||
template <> inline bool
|
||||
vl_equal_to<unsigned int>::operator()(const unsigned int& a,
|
||||
const unsigned int& b) const {
|
||||
return a == b;
|
||||
}
|
||||
|
||||
template <> inline size_t
|
||||
vl_hash<std::string>::operator()(const std::string& k) const {
|
||||
return vl_hash_bytes(k.data(), k.size());
|
||||
}
|
||||
|
||||
template <> inline bool
|
||||
vl_equal_to<std::string>::operator()(const std::string& a,
|
||||
const std::string& b) const {
|
||||
// Don't scan the strings if the sizes are different.
|
||||
if (a.size() != b.size()) {
|
||||
return false;
|
||||
}
|
||||
return (0 == a.compare(b)); // Must scan.
|
||||
}
|
||||
|
||||
template <typename T> struct vl_hash<T*> {
|
||||
size_t operator()(T* kp) const {
|
||||
return ((sizeof(size_t) == sizeof(kp))
|
||||
? reinterpret_cast<size_t>(kp)
|
||||
: vl_hash_bytes(&kp, sizeof(kp)));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct vl_equal_to<T*> {
|
||||
bool operator()(T* ap, T* bp) const {
|
||||
return ap == bp;
|
||||
}
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
//
|
||||
/// Functional clone of the std::unordered_set hash table.
|
||||
template <class T_Key,
|
||||
class T_Hash = vl_hash<T_Key>,
|
||||
class T_Equal = vl_equal_to<T_Key> >
|
||||
class vl_unordered_set {
|
||||
public:
|
||||
// TYPES
|
||||
typedef std::list<T_Key> Bucket;
|
||||
enum RehashType {GROW, SHRINK};
|
||||
|
||||
template <class KK, class VV,
|
||||
class HH, class EQ> friend class vl_unordered_map;
|
||||
|
||||
class iterator {
|
||||
protected:
|
||||
// MEMBERS
|
||||
size_t m_bucketIdx; // Bucket this iterator points into.
|
||||
typename Bucket::iterator m_bit; // Bucket-local iterator.
|
||||
const vl_unordered_set* m_setp; // The containing set.
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
iterator(size_t bucketIdx, typename Bucket::iterator bit,
|
||||
const vl_unordered_set* setp)
|
||||
: m_bucketIdx(bucketIdx), m_bit(bit), m_setp(setp) {}
|
||||
|
||||
// METHODS
|
||||
const T_Key& operator*() const {
|
||||
return *m_bit;
|
||||
}
|
||||
// This should really be 'const T_Key*' type for unordered_set,
|
||||
// however this iterator is shared with unordered_map whose
|
||||
// operator-> returns a non-const ValueType*, so keep this
|
||||
// non-const to avoid having to define a whole separate iterator
|
||||
// for unordered_map.
|
||||
T_Key* operator->() const {
|
||||
return &(*m_bit);
|
||||
}
|
||||
bool operator==(const iterator& other) const {
|
||||
return ((m_bucketIdx == other.m_bucketIdx)
|
||||
&& (m_bit == other.m_bit));
|
||||
}
|
||||
bool operator!=(const iterator& other) const {
|
||||
return (!this->operator==(other));
|
||||
}
|
||||
void advanceUntilValid() {
|
||||
while (1) {
|
||||
if (m_bit != m_setp->m_bucketsp[m_bucketIdx].end()) {
|
||||
// Valid iterator in this bucket; we're done.
|
||||
return;
|
||||
}
|
||||
|
||||
// Try the next bucket?
|
||||
m_bucketIdx++;
|
||||
if (m_bucketIdx == m_setp->numBuckets()) {
|
||||
// Ran past the end of buckets, set to end().
|
||||
*this = m_setp->end();
|
||||
return;
|
||||
}
|
||||
m_bit = m_setp->m_bucketsp[m_bucketIdx].begin();
|
||||
}
|
||||
}
|
||||
void operator++() {
|
||||
++m_bit;
|
||||
advanceUntilValid();
|
||||
}
|
||||
|
||||
typename Bucket::iterator bit() const { return m_bit; }
|
||||
};
|
||||
|
||||
// TODO: there's no real const enforcement on the 'const_iterator'.
|
||||
typedef iterator const_iterator;
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
size_t m_numElements; // Number of entries present.
|
||||
size_t m_log2Buckets; // Log-base-2 of the number of buckets.
|
||||
mutable Bucket* m_bucketsp; // Hash table buckets. May be NULL;
|
||||
// // we'll allocate it on the fly when
|
||||
// // the first entries are created.
|
||||
Bucket m_emptyBucket; // A fake bucket, used to construct end().
|
||||
T_Hash m_hash; // Hash function provider.
|
||||
T_Equal m_equal; // Equal-to function provider.
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
vl_unordered_set()
|
||||
: m_numElements(0)
|
||||
, m_log2Buckets(initLog2Buckets())
|
||||
, m_bucketsp(NULL)
|
||||
, m_hash()
|
||||
, m_equal() { }
|
||||
|
||||
vl_unordered_set(const vl_unordered_set& other)
|
||||
: m_numElements(other.m_numElements)
|
||||
, m_log2Buckets(other.m_log2Buckets)
|
||||
, m_bucketsp(NULL)
|
||||
, m_hash()
|
||||
, m_equal() {
|
||||
if (other.m_bucketsp) {
|
||||
m_bucketsp = new Bucket[numBuckets()];
|
||||
for (size_t i = 0; i < numBuckets(); i++) {
|
||||
m_bucketsp[i] = other.m_bucketsp[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
~vl_unordered_set() {
|
||||
delete [] m_bucketsp; VL_DANGLING(m_bucketsp);
|
||||
}
|
||||
|
||||
vl_unordered_set& operator=(const vl_unordered_set& other) {
|
||||
if (this != &other) {
|
||||
clear();
|
||||
delete [] m_bucketsp;
|
||||
m_numElements = other.m_numElements;
|
||||
m_log2Buckets = other.m_log2Buckets;
|
||||
if (other.m_bucketsp) {
|
||||
m_bucketsp = new Bucket[numBuckets()];
|
||||
for (size_t i = 0; i < numBuckets(); i++) {
|
||||
m_bucketsp[i] = other.m_bucketsp[i];
|
||||
}
|
||||
} else {
|
||||
m_bucketsp = NULL;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// METHODS
|
||||
static size_t initLog2Buckets() { return 4; }
|
||||
|
||||
iterator begin() {
|
||||
if (m_numElements) {
|
||||
initBuckets();
|
||||
iterator result = iterator(0, m_bucketsp[0].begin(), this);
|
||||
result.advanceUntilValid();
|
||||
return result;
|
||||
}
|
||||
return end();
|
||||
}
|
||||
const_iterator begin() const {
|
||||
if (m_numElements) {
|
||||
initBuckets();
|
||||
const_iterator result = iterator(0, m_bucketsp[0].begin(), this);
|
||||
result.advanceUntilValid();
|
||||
return result;
|
||||
}
|
||||
return end();
|
||||
}
|
||||
const_iterator end() const {
|
||||
return iterator(VL_ULL(0xFFFFFFFFFFFFFFFF),
|
||||
const_cast<Bucket&>(m_emptyBucket).begin(), this);
|
||||
}
|
||||
|
||||
bool empty() const { return m_numElements == 0; }
|
||||
|
||||
size_t size() const { return m_numElements; }
|
||||
|
||||
size_t count(const T_Key& key) const {
|
||||
return (find(key) == end()) ? 0 : 1;
|
||||
}
|
||||
|
||||
size_t hashToBucket(size_t hashVal) const {
|
||||
return hashToBucket(hashVal, m_log2Buckets);
|
||||
}
|
||||
static size_t hashToBucket(size_t hashVal, unsigned log2Buckets) {
|
||||
// Fibonacci hashing
|
||||
// See https://probablydance.com/2018/06/16/fibonacci-hashing-the-optimization-that-the-world-forgot-or-a-better-alternative-to-integer-modulo/
|
||||
//
|
||||
// * The magic numbers below are UINT_MAX/phi where phi is the
|
||||
// golden ratio number (1.618...) for either 64- or 32-bit
|
||||
// values of UINT_MAX.
|
||||
//
|
||||
// * Fibonacci hashing mixes the result of the client's hash
|
||||
// function further. This permits the use of very fast client
|
||||
// hash funcs (like just returning the int or pointer value as
|
||||
// is!) and tolerates crappy client hash functions pretty well.
|
||||
size_t mult = hashVal * ((sizeof(size_t) == 8)
|
||||
? VL_ULL(11400714819323198485)
|
||||
: 2654435769lu);
|
||||
size_t result = (mult >> (((sizeof(size_t) == 8)
|
||||
? 64 : 32) - log2Buckets));
|
||||
return result;
|
||||
}
|
||||
|
||||
iterator find_internal(const T_Key& key, size_t& bucketIdxOut) {
|
||||
size_t hash = m_hash.operator()(key);
|
||||
bucketIdxOut = hashToBucket(hash);
|
||||
initBuckets();
|
||||
Bucket* bucketp = &m_bucketsp[bucketIdxOut];
|
||||
|
||||
for (typename Bucket::iterator it = bucketp->begin();
|
||||
it != bucketp->end(); ++it) {
|
||||
if (m_equal.operator()(*it, key)) {
|
||||
return iterator(bucketIdxOut, it, this);
|
||||
}
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
const_iterator find(const T_Key& key) const {
|
||||
size_t bucketIdx;
|
||||
return const_cast<vl_unordered_set*>(this)->find_internal(key,
|
||||
bucketIdx);
|
||||
}
|
||||
|
||||
iterator find(const T_Key& key) {
|
||||
size_t bucketIdx;
|
||||
return find_internal(key, bucketIdx);
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const T_Key &val) {
|
||||
size_t bucketIdx;
|
||||
iterator existIt = find_internal(val, bucketIdx);
|
||||
if (existIt != end()) {
|
||||
// Collision with existing element.
|
||||
//
|
||||
// An element may be inserted only if it is not
|
||||
// equal to an existing element. So fail.
|
||||
return std::pair<iterator, bool>(end(), false);
|
||||
}
|
||||
|
||||
// No collision, so insert it.
|
||||
m_numElements++;
|
||||
|
||||
m_bucketsp[bucketIdx].push_front(val);
|
||||
|
||||
// Compute result iterator. This pointer will be valid
|
||||
// if we don't rehash:
|
||||
iterator result_it(bucketIdx, m_bucketsp[bucketIdx].begin(), this);
|
||||
|
||||
if (needToRehash(GROW)) {
|
||||
rehash(GROW);
|
||||
// ... since we rehashed, do a lookup to get the result iterator.
|
||||
result_it = find(val);
|
||||
}
|
||||
|
||||
return std::pair<iterator, bool>(result_it, true);
|
||||
}
|
||||
|
||||
iterator erase(iterator it) {
|
||||
iterator next_it = it;
|
||||
++next_it;
|
||||
erase(*it);
|
||||
return next_it;
|
||||
}
|
||||
|
||||
size_t erase(const T_Key &key) {
|
||||
size_t bucketIdx;
|
||||
iterator it = find_internal(key, bucketIdx);
|
||||
if (it != end()) {
|
||||
m_bucketsp[bucketIdx].erase(it.bit());
|
||||
m_numElements--;
|
||||
// Rehashing to handle a shrinking data set is important
|
||||
// for the Scoreboard in V3Partition, which begins tracking
|
||||
// a huge number of vertices and then tracks a successively
|
||||
// smaller number over time.
|
||||
if (needToRehash(SHRINK)) {
|
||||
rehash(SHRINK);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
if (m_bucketsp) {
|
||||
delete [] m_bucketsp;
|
||||
m_bucketsp = NULL;
|
||||
}
|
||||
m_numElements = 0;
|
||||
m_log2Buckets = initLog2Buckets();
|
||||
}
|
||||
|
||||
private:
|
||||
size_t numBuckets() const { return (VL_ULL(1) << m_log2Buckets); }
|
||||
|
||||
Bucket* getBucket(size_t idx) {
|
||||
initBuckets();
|
||||
return &m_bucketsp[idx];
|
||||
}
|
||||
|
||||
void initBuckets() const {
|
||||
if (!m_bucketsp) m_bucketsp = new Bucket[numBuckets()];
|
||||
}
|
||||
|
||||
bool needToRehash(RehashType rt) const {
|
||||
if (rt == GROW) {
|
||||
return ((4 * numBuckets()) < m_numElements);
|
||||
} else {
|
||||
return (numBuckets() > (4 * m_numElements));
|
||||
}
|
||||
}
|
||||
|
||||
void rehash(RehashType rt) {
|
||||
size_t new_log2Buckets;
|
||||
if (rt == GROW) {
|
||||
new_log2Buckets = m_log2Buckets + 2;
|
||||
} else {
|
||||
if (m_log2Buckets <= 4) {
|
||||
// On shrink, saturate m_log2Buckets at its
|
||||
// initial size of 2^4 == 16 buckets. Don't risk
|
||||
// underflowing!
|
||||
return;
|
||||
}
|
||||
new_log2Buckets = m_log2Buckets - 2;
|
||||
}
|
||||
|
||||
size_t new_num_buckets = VL_ULL(1) << new_log2Buckets;
|
||||
Bucket* new_bucketsp = new Bucket[new_num_buckets];
|
||||
|
||||
for (size_t i=0; i<numBuckets(); i++) {
|
||||
while (!m_bucketsp[i].empty()) {
|
||||
typename Bucket::iterator bit = m_bucketsp[i].begin();
|
||||
size_t hash = m_hash.operator()(*bit);
|
||||
size_t new_idx = hashToBucket(hash, new_log2Buckets);
|
||||
// Avoid mallocing one list elem and freeing another;
|
||||
// splice just moves it over.
|
||||
new_bucketsp[new_idx].splice(new_bucketsp[new_idx].begin(),
|
||||
m_bucketsp[i], bit);
|
||||
}
|
||||
}
|
||||
|
||||
delete[] m_bucketsp;
|
||||
m_bucketsp = new_bucketsp;
|
||||
m_log2Buckets = new_log2Buckets;
|
||||
}
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
//
|
||||
/// Functional clone of the std::unordered_map hash table.
|
||||
template <class T_Key,
|
||||
class T_Value,
|
||||
class T_Hash = vl_hash<T_Key>,
|
||||
class T_Equal = vl_equal_to<T_Key> >
|
||||
class vl_unordered_map {
|
||||
private:
|
||||
// TYPES
|
||||
typedef std::pair<T_Key, T_Value> KeyValPair;
|
||||
|
||||
class KeyHash {
|
||||
private:
|
||||
T_Hash key_hash;
|
||||
public:
|
||||
KeyHash() {}
|
||||
size_t operator()(const KeyValPair& kv_pair) const {
|
||||
return key_hash.operator()(kv_pair.first);
|
||||
}
|
||||
};
|
||||
|
||||
class KeyEqual {
|
||||
private:
|
||||
T_Equal key_eq;
|
||||
public:
|
||||
KeyEqual() {}
|
||||
bool operator()(const KeyValPair& kv_a, const KeyValPair& kv_b) const {
|
||||
return key_eq.operator()(kv_a.first, kv_b.first);
|
||||
}
|
||||
};
|
||||
|
||||
// MEMBERS
|
||||
typedef vl_unordered_set<KeyValPair, KeyHash, KeyEqual> MapSet;
|
||||
MapSet m_set; // Wrap this vl_unordered_set which holds all state.
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
vl_unordered_map() {}
|
||||
~vl_unordered_map() {}
|
||||
|
||||
typedef typename MapSet::iterator iterator;
|
||||
typedef typename MapSet::const_iterator const_iterator;
|
||||
|
||||
// METHODS
|
||||
iterator begin() { return m_set.begin(); }
|
||||
const_iterator begin() const { return m_set.begin(); }
|
||||
const_iterator end() const { return m_set.end(); }
|
||||
bool empty() const { return m_set.empty(); }
|
||||
iterator find(const T_Key& k) {
|
||||
// We can't assume that T_Value() is defined.
|
||||
// ie, this does not work:
|
||||
// return m_set.find(std::make_pair(k, T_Value()));
|
||||
|
||||
// So, do this instead:
|
||||
T_Hash mapHash;
|
||||
T_Equal mapEq;
|
||||
size_t hash = mapHash.operator()(k);
|
||||
size_t bucketIdxOut = m_set.hashToBucket(hash);
|
||||
typename MapSet::Bucket* bucketp = m_set.getBucket(bucketIdxOut);
|
||||
|
||||
for (typename MapSet::Bucket::iterator it = bucketp->begin();
|
||||
it != bucketp->end(); ++it) {
|
||||
if (mapEq.operator()(it->first, k)) {
|
||||
return iterator(bucketIdxOut, it, &m_set);
|
||||
}
|
||||
}
|
||||
return end();
|
||||
}
|
||||
const_iterator find(const T_Key& k) const {
|
||||
return const_cast<vl_unordered_map*>(this)->find(k);
|
||||
}
|
||||
std::pair<iterator, bool> insert(const KeyValPair& val) {
|
||||
return m_set.insert(val);
|
||||
}
|
||||
iterator erase(iterator it) { return m_set.erase(it); }
|
||||
size_t erase(const T_Key& k) {
|
||||
iterator it = find(k);
|
||||
if (it == end()) { return 0; }
|
||||
m_set.erase(it);
|
||||
return 1;
|
||||
}
|
||||
T_Value& operator[](const T_Key& k) {
|
||||
// Here we can assume T_Value() is defined, as
|
||||
// std::unordered_map::operator[] relies on it too.
|
||||
KeyValPair dummy = std::make_pair(k, T_Value());
|
||||
iterator it = m_set.find(dummy);
|
||||
if (it == m_set.end()) {
|
||||
it = m_set.insert(dummy).first;
|
||||
}
|
||||
// For the 'set', it's generally not safe to modify
|
||||
// the value after deref. For the 'map' though, we know
|
||||
// it's safe to modify the value field and we can allow it:
|
||||
return it->second;
|
||||
}
|
||||
T_Value& at(const T_Key& k) {
|
||||
iterator it = find(k);
|
||||
if (it == end()) { throw std::out_of_range("sorry"); }
|
||||
return it->second;
|
||||
}
|
||||
const T_Value& at(const T_Key& k) const {
|
||||
iterator it = find(k);
|
||||
if (it == end()) { throw std::out_of_range("sorry"); }
|
||||
return it->second;
|
||||
}
|
||||
void clear() { m_set.clear(); }
|
||||
size_t size() const { return m_set.size(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
+273
-269
@@ -24,12 +24,11 @@
|
||||
#include "verilated.h"
|
||||
#include "verilated_vcd_c.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <ctime>
|
||||
#include <algorithm>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#if defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
|
||||
# include <io.h>
|
||||
@@ -39,12 +38,15 @@
|
||||
|
||||
// SPDIFF_ON
|
||||
|
||||
#ifndef O_LARGEFILE // For example on WIN32
|
||||
#ifndef O_LARGEFILE // For example on WIN32
|
||||
# define O_LARGEFILE 0
|
||||
#endif
|
||||
#ifndef O_NONBLOCK
|
||||
# define O_NONBLOCK 0
|
||||
#endif
|
||||
#ifndef O_CLOEXEC
|
||||
# define O_CLOEXEC 0
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcdImp
|
||||
@@ -58,25 +60,25 @@ private:
|
||||
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; }
|
||||
static Singleton& singleton() { static Singleton s; return s; }
|
||||
public:
|
||||
static void pushVcd(VerilatedVcd* vcdp) VL_EXCLUDES(singleton().s_vcdMutex) {
|
||||
VerilatedLockGuard lock(singleton().s_vcdMutex);
|
||||
singleton().s_vcdVecp.push_back(vcdp);
|
||||
VerilatedLockGuard lock(singleton().s_vcdMutex);
|
||||
singleton().s_vcdVecp.push_back(vcdp);
|
||||
}
|
||||
static void removeVcd(const VerilatedVcd* vcdp) VL_EXCLUDES(singleton().s_vcdMutex) {
|
||||
VerilatedLockGuard lock(singleton().s_vcdMutex);
|
||||
VcdVec::iterator pos = find(singleton().s_vcdVecp.begin(), singleton().s_vcdVecp.end(), vcdp);
|
||||
if (pos != singleton().s_vcdVecp.end()) { singleton().s_vcdVecp.erase(pos); }
|
||||
VerilatedLockGuard lock(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() 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 lock(singleton().s_vcdMutex);
|
||||
for (VcdVec::const_iterator it=singleton().s_vcdVecp.begin(); it!=singleton().s_vcdVecp.end(); ++it) {
|
||||
VerilatedVcd* vcdp = *it;
|
||||
vcdp->flush();
|
||||
}
|
||||
// Thread safety: Although this function is protected by a mutex so perhaps
|
||||
// in the future we can allow tracing in separate threads, vcdp->flush() assumes call from single thread
|
||||
VerilatedLockGuard lock(singleton().s_vcdMutex);
|
||||
for (VcdVec::const_iterator it=singleton().s_vcdVecp.begin(); it!=singleton().s_vcdVecp.end(); ++it) {
|
||||
VerilatedVcd* vcdp = *it;
|
||||
vcdp->flush();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -91,16 +93,16 @@ public:
|
||||
class VerilatedVcdCallInfo {
|
||||
protected:
|
||||
friend class VerilatedVcd;
|
||||
VerilatedVcdCallback_t m_initcb; ///< Initialization Callback function
|
||||
VerilatedVcdCallback_t m_fullcb; ///< Full Dumping Callback function
|
||||
VerilatedVcdCallback_t m_changecb; ///< Incremental Dumping Callback function
|
||||
void* m_userthis; ///< Fake "this" for caller
|
||||
vluint32_t m_code; ///< Starting code number
|
||||
VerilatedVcdCallback_t m_initcb; ///< Initialization Callback function
|
||||
VerilatedVcdCallback_t m_fullcb; ///< Full Dumping Callback function
|
||||
VerilatedVcdCallback_t m_changecb; ///< Incremental Dumping Callback function
|
||||
void* m_userthis; ///< Fake "this" for caller
|
||||
vluint32_t m_code; ///< Starting code number
|
||||
// CONSTRUCTORS
|
||||
VerilatedVcdCallInfo (VerilatedVcdCallback_t icb, VerilatedVcdCallback_t fcb,
|
||||
VerilatedVcdCallback_t changecb,
|
||||
void* ut, vluint32_t code)
|
||||
: m_initcb(icb), m_fullcb(fcb), m_changecb(changecb), m_userthis(ut), m_code(code) {};
|
||||
VerilatedVcdCallInfo(VerilatedVcdCallback_t icb, VerilatedVcdCallback_t fcb,
|
||||
VerilatedVcdCallback_t changecb,
|
||||
void* ut, vluint32_t code)
|
||||
: m_initcb(icb), m_fullcb(fcb), m_changecb(changecb), m_userthis(ut), m_code(code) {};
|
||||
~VerilatedVcdCallInfo() {}
|
||||
};
|
||||
|
||||
@@ -110,7 +112,7 @@ protected:
|
||||
// VerilatedVcdFile
|
||||
|
||||
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);
|
||||
m_fd = ::open(name.c_str(), O_CREAT|O_WRONLY|O_TRUNC|O_LARGEFILE|O_NONBLOCK|O_CLOEXEC, 0666);
|
||||
return (m_fd>=0);
|
||||
}
|
||||
|
||||
@@ -146,7 +148,7 @@ VerilatedVcd::VerilatedVcd(VerilatedVcdFile* filep)
|
||||
m_wroteBytes = 0;
|
||||
}
|
||||
|
||||
void VerilatedVcd::open (const char* filename) {
|
||||
void VerilatedVcd::open(const char* filename) {
|
||||
m_assertOne.check();
|
||||
if (isOpen()) return;
|
||||
|
||||
@@ -159,64 +161,64 @@ void VerilatedVcd::open (const char* filename) {
|
||||
Verilated::flushCb(&flush_all);
|
||||
|
||||
// SPDIFF_ON
|
||||
openNext (m_rolloverMB!=0);
|
||||
openNext(m_rolloverMB!=0);
|
||||
if (!isOpen()) return;
|
||||
|
||||
dumpHeader();
|
||||
|
||||
// Allocate space now we know the number of codes
|
||||
if (!m_sigs_oldvalp) {
|
||||
m_sigs_oldvalp = new vluint32_t [m_nextCode+10];
|
||||
m_sigs_oldvalp = new vluint32_t [m_nextCode+10];
|
||||
}
|
||||
|
||||
if (m_rolloverMB) {
|
||||
openNext(true);
|
||||
if (!isOpen()) return;
|
||||
openNext(true);
|
||||
if (!isOpen()) return;
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedVcd::openNext (bool incFilename) {
|
||||
void VerilatedVcd::openNext(bool incFilename) {
|
||||
// Open next filename in concat sequence, mangle filename if
|
||||
// incFilename is true.
|
||||
m_assertOne.check();
|
||||
closePrev(); // Close existing
|
||||
closePrev(); // Close existing
|
||||
if (incFilename) {
|
||||
// Find _0000.{ext} in filename
|
||||
std::string name = m_filename;
|
||||
size_t pos=name.rfind(".");
|
||||
if (pos>8 && 0==strncmp("_cat",name.c_str()+pos-8,4)
|
||||
&& isdigit(name.c_str()[pos-4])
|
||||
&& isdigit(name.c_str()[pos-3])
|
||||
&& isdigit(name.c_str()[pos-2])
|
||||
&& isdigit(name.c_str()[pos-1])) {
|
||||
// Increment code.
|
||||
if ((++(name[pos-1])) > '9') {
|
||||
name[pos-1] = '0';
|
||||
if ((++(name[pos-2])) > '9') {
|
||||
name[pos-2] = '0';
|
||||
if ((++(name[pos-3])) > '9') {
|
||||
name[pos-3] = '0';
|
||||
if ((++(name[pos-4])) > '9') {
|
||||
name[pos-4] = '0';
|
||||
}}}}
|
||||
} else {
|
||||
// Append _cat0000
|
||||
name.insert(pos,"_cat0000");
|
||||
}
|
||||
m_filename = name;
|
||||
// Find _0000.{ext} in filename
|
||||
std::string name = m_filename;
|
||||
size_t pos = name.rfind('.');
|
||||
if (pos>8 && 0==strncmp("_cat",name.c_str()+pos-8,4)
|
||||
&& isdigit(name.c_str()[pos-4])
|
||||
&& isdigit(name.c_str()[pos-3])
|
||||
&& isdigit(name.c_str()[pos-2])
|
||||
&& isdigit(name.c_str()[pos-1])) {
|
||||
// Increment code.
|
||||
if ((++(name[pos-1])) > '9') {
|
||||
name[pos-1] = '0';
|
||||
if ((++(name[pos-2])) > '9') {
|
||||
name[pos-2] = '0';
|
||||
if ((++(name[pos-3])) > '9') {
|
||||
name[pos-3] = '0';
|
||||
if ((++(name[pos-4])) > '9') {
|
||||
name[pos-4] = '0';
|
||||
}}}}
|
||||
} else {
|
||||
// Append _cat0000
|
||||
name.insert(pos,"_cat0000");
|
||||
}
|
||||
m_filename = name;
|
||||
}
|
||||
if (m_filename[0]=='|') {
|
||||
assert(0); // Not supported yet.
|
||||
assert(0); // Not supported yet.
|
||||
} else {
|
||||
// cppcheck-suppress duplicateExpression
|
||||
if (!m_filep->open(m_filename)) {
|
||||
// User code can check isOpen()
|
||||
m_isOpen = false;
|
||||
return;
|
||||
}
|
||||
// cppcheck-suppress duplicateExpression
|
||||
if (!m_filep->open(m_filename)) {
|
||||
// User code can check isOpen()
|
||||
m_isOpen = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_isOpen = true;
|
||||
m_fullDump = true; // First dump must be full
|
||||
m_fullDump = true; // First dump must be full
|
||||
m_wroteBytes = 0;
|
||||
}
|
||||
|
||||
@@ -226,9 +228,9 @@ void VerilatedVcd::makeNameMap() {
|
||||
m_nextCode = 1;
|
||||
m_namemapp = new NameMap;
|
||||
for (vluint32_t ent = 0; ent< m_callbacks.size(); ent++) {
|
||||
VerilatedVcdCallInfo *cip = m_callbacks[ent];
|
||||
cip->m_code = m_nextCode;
|
||||
(cip->m_initcb) (this, cip->m_userthis, cip->m_code);
|
||||
VerilatedVcdCallInfo* cip = m_callbacks[ent];
|
||||
cip->m_code = m_nextCode;
|
||||
(cip->m_initcb)(this, cip->m_userthis, cip->m_code);
|
||||
}
|
||||
|
||||
// Though not speced, it's illegal to generate a vcd with signals
|
||||
@@ -237,21 +239,21 @@ void VerilatedVcd::makeNameMap() {
|
||||
// This comes from user instantiations with no name - IE Vtop("").
|
||||
bool nullScope = false;
|
||||
for (NameMap::const_iterator it=m_namemapp->begin(); it!=m_namemapp->end(); ++it) {
|
||||
const std::string& hiername = it->first;
|
||||
if (hiername.size() >= 1 && hiername[0] == '\t') nullScope=true;
|
||||
const std::string& hiername = it->first;
|
||||
if (!hiername.empty() && hiername[0] == '\t') nullScope=true;
|
||||
}
|
||||
if (nullScope) {
|
||||
NameMap* newmapp = new NameMap;
|
||||
for (NameMap::const_iterator it=m_namemapp->begin(); it!=m_namemapp->end(); ++it) {
|
||||
const std::string& hiername = it->first;
|
||||
const std::string& decl = it->second;
|
||||
std::string newname = std::string("top");
|
||||
if (hiername[0] != '\t') newname += ' ';
|
||||
newname += hiername;
|
||||
newmapp->insert(std::make_pair(newname,decl));
|
||||
}
|
||||
deleteNameMap();
|
||||
m_namemapp = newmapp;
|
||||
NameMap* newmapp = new NameMap;
|
||||
for (NameMap::const_iterator it=m_namemapp->begin(); it!=m_namemapp->end(); ++it) {
|
||||
const std::string& hiername = it->first;
|
||||
const std::string& decl = it->second;
|
||||
std::string newname = std::string("top");
|
||||
if (hiername[0] != '\t') newname += ' ';
|
||||
newname += hiername;
|
||||
newmapp->insert(std::make_pair(newname,decl));
|
||||
}
|
||||
deleteNameMap();
|
||||
m_namemapp = newmapp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,13 +268,13 @@ VerilatedVcd::~VerilatedVcd() {
|
||||
deleteNameMap();
|
||||
if (m_filep && m_fileNewed) { delete m_filep; m_filep = NULL; }
|
||||
for (CallbackVec::const_iterator it=m_callbacks.begin(); it!=m_callbacks.end(); ++it) {
|
||||
delete (*it);
|
||||
delete (*it);
|
||||
}
|
||||
m_callbacks.clear();
|
||||
VerilatedVcdSingleton::removeVcd(this);
|
||||
}
|
||||
|
||||
void VerilatedVcd::closePrev () {
|
||||
void VerilatedVcd::closePrev() {
|
||||
// This function is on the flush() call path
|
||||
if (!isOpen()) return;
|
||||
|
||||
@@ -281,7 +283,7 @@ void VerilatedVcd::closePrev () {
|
||||
m_filep->close();
|
||||
}
|
||||
|
||||
void VerilatedVcd::closeErr () {
|
||||
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.
|
||||
@@ -297,37 +299,37 @@ void VerilatedVcd::close() {
|
||||
m_assertOne.check();
|
||||
if (!isOpen()) return;
|
||||
if (m_evcd) {
|
||||
printStr("$vcdclose ");
|
||||
printTime(m_timeLastDump);
|
||||
printStr(" $end\n");
|
||||
printStr("$vcdclose ");
|
||||
printTime(m_timeLastDump);
|
||||
printStr(" $end\n");
|
||||
}
|
||||
closePrev();
|
||||
}
|
||||
|
||||
void VerilatedVcd::printStr (const char* str) {
|
||||
void VerilatedVcd::printStr(const char* str) {
|
||||
// Not fast...
|
||||
while (*str) {
|
||||
*m_writep++ = *str++;
|
||||
bufferCheck();
|
||||
*m_writep++ = *str++;
|
||||
bufferCheck();
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedVcd::printQuad (vluint64_t n) {
|
||||
void VerilatedVcd::printQuad(vluint64_t n) {
|
||||
char buf [100];
|
||||
sprintf(buf,"%" VL_PRI64 "u", n);
|
||||
printStr(buf);
|
||||
}
|
||||
|
||||
void VerilatedVcd::printTime (vluint64_t timeui) {
|
||||
void VerilatedVcd::printTime(vluint64_t timeui) {
|
||||
// VCD file format specification does not allow non-integers for timestamps
|
||||
// Dinotrace doesn't mind, but Cadence vvision seems to choke
|
||||
if (VL_UNLIKELY(timeui < m_timeLastDump)) {
|
||||
timeui = m_timeLastDump;
|
||||
static VL_THREAD_LOCAL bool backTime = false;
|
||||
if (!backTime) {
|
||||
backTime = true;
|
||||
VL_PRINTF_MT("%%Warning: VCD time is moving backwards, wave file may be incorrect.\n");
|
||||
}
|
||||
timeui = m_timeLastDump;
|
||||
static VL_THREAD_LOCAL bool backTime = false;
|
||||
if (!backTime) {
|
||||
backTime = true;
|
||||
VL_PRINTF_MT("%%Warning: VCD time is moving backwards, wave file may be incorrect.\n");
|
||||
}
|
||||
}
|
||||
m_timeLastDump = timeui;
|
||||
printQuad(timeui);
|
||||
@@ -337,17 +339,17 @@ void VerilatedVcd::bufferResize(vluint64_t minsize) {
|
||||
// minsize is size of largest write. We buffer at least 8 times as much data,
|
||||
// writing when we are 3/4 full (with thus 2*minsize remaining free)
|
||||
if (VL_UNLIKELY(minsize > m_wrChunkSize)) {
|
||||
char* oldbufp = m_wrBufp;
|
||||
m_wrChunkSize = minsize*2;
|
||||
m_wrBufp = new char [m_wrChunkSize * 8];
|
||||
memcpy(m_wrBufp, oldbufp, m_writep - oldbufp);
|
||||
char* oldbufp = m_wrBufp;
|
||||
m_wrChunkSize = minsize*2;
|
||||
m_wrBufp = new char [m_wrChunkSize * 8];
|
||||
memcpy(m_wrBufp, oldbufp, m_writep - oldbufp);
|
||||
m_writep = m_wrBufp + (m_writep - oldbufp);
|
||||
m_wrFlushp = m_wrBufp + m_wrChunkSize * 6;
|
||||
delete [] oldbufp; oldbufp=NULL;
|
||||
m_wrFlushp = m_wrBufp + m_wrChunkSize * 6;
|
||||
delete [] oldbufp; oldbufp=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedVcd::bufferFlush () VL_MT_UNSAFE_ONE {
|
||||
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()
|
||||
@@ -356,22 +358,22 @@ void VerilatedVcd::bufferFlush () VL_MT_UNSAFE_ONE {
|
||||
if (VL_UNLIKELY(!isOpen())) return;
|
||||
char* wp = m_wrBufp;
|
||||
while (1) {
|
||||
ssize_t remaining = (m_writep - wp);
|
||||
if (remaining==0) break;
|
||||
errno = 0;
|
||||
ssize_t got = m_filep->write(wp, remaining);
|
||||
if (got>0) {
|
||||
wp += got;
|
||||
m_wroteBytes += got;
|
||||
} else if (got < 0) {
|
||||
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_MT("",0,"",msg.c_str());
|
||||
closeErr();
|
||||
break;
|
||||
}
|
||||
}
|
||||
ssize_t remaining = (m_writep - wp);
|
||||
if (remaining==0) break;
|
||||
errno = 0;
|
||||
ssize_t got = m_filep->write(wp, remaining);
|
||||
if (got>0) {
|
||||
wp += got;
|
||||
m_wroteBytes += got;
|
||||
} else if (got < 0) {
|
||||
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_MT("",0,"",msg.c_str());
|
||||
closeErr();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset buffer
|
||||
@@ -381,19 +383,19 @@ void VerilatedVcd::bufferFlush () VL_MT_UNSAFE_ONE {
|
||||
//=============================================================================
|
||||
// Simple methods
|
||||
|
||||
void VerilatedVcd::set_time_unit (const char* unitp) {
|
||||
//cout<<" set_time_unit ("<<unitp<<") == "<<timescaleToDouble(unitp)
|
||||
void VerilatedVcd::set_time_unit(const char* unitp) {
|
||||
//cout<<" set_time_unit("<<unitp<<") == "<<timescaleToDouble(unitp)
|
||||
// <<" == "<<doubleToTimescale(timescaleToDouble(unitp))<<endl;
|
||||
m_timeUnit = timescaleToDouble(unitp);
|
||||
}
|
||||
|
||||
void VerilatedVcd::set_time_resolution (const char* unitp) {
|
||||
//cout<<"set_time_resolution ("<<unitp<<") == "<<timescaleToDouble(unitp)
|
||||
void VerilatedVcd::set_time_resolution(const char* unitp) {
|
||||
//cout<<"set_time_resolution("<<unitp<<") == "<<timescaleToDouble(unitp)
|
||||
// <<" == "<<doubleToTimescale(timescaleToDouble(unitp))<<endl;
|
||||
m_timeRes = timescaleToDouble(unitp);
|
||||
}
|
||||
|
||||
double VerilatedVcd::timescaleToDouble (const char* unitp) {
|
||||
double VerilatedVcd::timescaleToDouble(const char* unitp) {
|
||||
char* endp;
|
||||
double value = strtod(unitp, &endp);
|
||||
if (value==0.0 && endp==unitp) value=1; // On error so we allow just "ns" to return 1e-9.
|
||||
@@ -411,9 +413,9 @@ double VerilatedVcd::timescaleToDouble (const char* unitp) {
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string VerilatedVcd::doubleToTimescale (double value) {
|
||||
std::string VerilatedVcd::doubleToTimescale(double value) {
|
||||
const char* suffixp = "s";
|
||||
if (value>=1e0) { suffixp="s"; value *= 1e0; }
|
||||
if (value>=1e0) { suffixp="s"; value *= 1e0; }
|
||||
else if (value>=1e-3 ) { suffixp="ms"; value *= 1e3; }
|
||||
else if (value>=1e-6 ) { suffixp="us"; value *= 1e6; }
|
||||
else if (value>=1e-9 ) { suffixp="ns"; value *= 1e9; }
|
||||
@@ -427,14 +429,14 @@ std::string VerilatedVcd::doubleToTimescale (double value) {
|
||||
//=============================================================================
|
||||
// Definitions
|
||||
|
||||
void VerilatedVcd::printIndent (int level_change) {
|
||||
void VerilatedVcd::printIndent(int level_change) {
|
||||
if (level_change<0) m_modDepth += level_change;
|
||||
assert(m_modDepth>=0);
|
||||
for (int i=0; i<m_modDepth; i++) printStr(" ");
|
||||
if (level_change>0) m_modDepth += level_change;
|
||||
}
|
||||
|
||||
void VerilatedVcd::dumpHeader () {
|
||||
void VerilatedVcd::dumpHeader() {
|
||||
printStr("$version Generated by VerilatedVcd $end\n");
|
||||
time_t time_str = time(NULL);
|
||||
printStr("$date "); printStr(ctime(&time_str)); printStr(" $end\n");
|
||||
@@ -447,7 +449,7 @@ void VerilatedVcd::dumpHeader () {
|
||||
makeNameMap();
|
||||
|
||||
// Signal header
|
||||
assert (m_modDepth==0);
|
||||
assert(m_modDepth==0);
|
||||
printIndent(1);
|
||||
printStr("\n");
|
||||
|
||||
@@ -459,78 +461,78 @@ void VerilatedVcd::dumpHeader () {
|
||||
// Print the signal names
|
||||
const char* lastName = "";
|
||||
for (NameMap::const_iterator it=m_namemapp->begin(); it!=m_namemapp->end(); ++it) {
|
||||
const std::string& hiernamestr = it->first;
|
||||
const std::string& decl = it->second;
|
||||
const std::string& hiernamestr = it->first;
|
||||
const std::string& decl = it->second;
|
||||
|
||||
// Determine difference between the old and new names
|
||||
const char* hiername = hiernamestr.c_str();
|
||||
const char* lp = lastName;
|
||||
const char* np = hiername;
|
||||
lastName = hiername;
|
||||
// Determine difference between the old and new names
|
||||
const char* hiername = hiernamestr.c_str();
|
||||
const char* lp = lastName;
|
||||
const char* np = hiername;
|
||||
lastName = hiername;
|
||||
|
||||
// Skip common prefix, it must break at a space or tab
|
||||
for (; *np && (*np == *lp); np++, lp++) {}
|
||||
while (np!=hiername && *np && *np!=' ' && *np!='\t') { np--; lp--; }
|
||||
//printf("hier %s\n lp=%s\n np=%s\n",hiername,lp,np);
|
||||
// Skip common prefix, it must break at a space or tab
|
||||
for (; *np && (*np == *lp); np++, lp++) {}
|
||||
while (np!=hiername && *np && *np!=' ' && *np!='\t') { np--; lp--; }
|
||||
//printf("hier %s\n lp=%s\n np=%s\n",hiername,lp,np);
|
||||
|
||||
// Any extra spaces in last name are scope ups we need to do
|
||||
bool first = true;
|
||||
for (; *lp; lp++) {
|
||||
if (*lp==' ' || (first && *lp!='\t')) {
|
||||
printIndent(-1);
|
||||
printStr("$upscope $end\n");
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
// Any extra spaces in last name are scope ups we need to do
|
||||
bool first = true;
|
||||
for (; *lp; lp++) {
|
||||
if (*lp==' ' || (first && *lp!='\t')) {
|
||||
printIndent(-1);
|
||||
printStr("$upscope $end\n");
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
|
||||
// Any new spaces are scope downs we need to do
|
||||
while (*np) {
|
||||
if (*np==' ') np++;
|
||||
if (*np=='\t') break; // tab means signal name starts
|
||||
printIndent(1);
|
||||
printStr("$scope module ");
|
||||
for (; *np && *np!=' ' && *np!='\t'; np++) {
|
||||
if (*np=='[') printStr("(");
|
||||
else if (*np==']') printStr(")");
|
||||
else *m_writep++=*np;
|
||||
}
|
||||
printStr(" $end\n");
|
||||
}
|
||||
// Any new spaces are scope downs we need to do
|
||||
while (*np) {
|
||||
if (*np==' ') np++;
|
||||
if (*np=='\t') break; // tab means signal name starts
|
||||
printIndent(1);
|
||||
printStr("$scope module ");
|
||||
for (; *np && *np!=' ' && *np!='\t'; np++) {
|
||||
if (*np=='[') printStr("(");
|
||||
else if (*np==']') printStr(")");
|
||||
else *m_writep++=*np;
|
||||
}
|
||||
printStr(" $end\n");
|
||||
}
|
||||
|
||||
printIndent(0);
|
||||
printStr(decl.c_str());
|
||||
printIndent(0);
|
||||
printStr(decl.c_str());
|
||||
}
|
||||
|
||||
while (m_modDepth>1) {
|
||||
printIndent(-1);
|
||||
printStr("$upscope $end\n");
|
||||
printIndent(-1);
|
||||
printStr("$upscope $end\n");
|
||||
}
|
||||
|
||||
printIndent(-1);
|
||||
printStr("$enddefinitions $end\n\n\n");
|
||||
assert (m_modDepth==0);
|
||||
assert(m_modDepth==0);
|
||||
|
||||
// Reclaim storage
|
||||
deleteNameMap();
|
||||
}
|
||||
|
||||
void VerilatedVcd::module (const std::string& name) {
|
||||
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) {
|
||||
void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep,
|
||||
int arraynum, bool tri, bool bussed, int msb, int lsb) {
|
||||
if (!code) { VL_FATAL_MT(__FILE__,__LINE__,"","Internal: internal trace problem, code 0 is illegal"); }
|
||||
|
||||
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
|
||||
if (tri) codesNeeded *= 2; // Space in change array for __en signals
|
||||
|
||||
// Make sure array is large enough
|
||||
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
|
||||
m_sigs.reserve(m_nextCode*2); // Power-of-2 allocation speeds things up
|
||||
}
|
||||
|
||||
// Make sure write buffer is large enough (one character per bit), plus header
|
||||
@@ -547,18 +549,20 @@ void VerilatedVcd::declare (vluint32_t code, const char* name, const char* wirep
|
||||
// Tab sorts before spaces, so signals nicely will print before scopes
|
||||
// Note the hiername may be nothing, if so we'll add "\t{name}"
|
||||
std::string nameasstr = name;
|
||||
if (m_modName!="") { nameasstr = m_modName+m_scopeEscape+nameasstr; } // Optional ->module prefix
|
||||
if (!m_modName.empty()) {
|
||||
nameasstr = m_modName+m_scopeEscape+nameasstr; // Optional ->module prefix
|
||||
}
|
||||
std::string hiername;
|
||||
std::string basename;
|
||||
for (const char* cp=nameasstr.c_str(); *cp; cp++) {
|
||||
if (isScopeEscape(*cp)) {
|
||||
// Ahh, we've just read a scope, not a basename
|
||||
if (hiername!="") hiername += " ";
|
||||
hiername += basename;
|
||||
basename = "";
|
||||
} else {
|
||||
basename += *cp;
|
||||
}
|
||||
if (isScopeEscape(*cp)) {
|
||||
// Ahh, we've just read a scope, not a basename
|
||||
if (!hiername.empty()) hiername += " ";
|
||||
hiername += basename;
|
||||
basename = "";
|
||||
} else {
|
||||
basename += *cp;
|
||||
}
|
||||
}
|
||||
hiername += "\t"+basename;
|
||||
|
||||
@@ -569,50 +573,50 @@ void VerilatedVcd::declare (vluint32_t code, const char* name, const char* wirep
|
||||
sprintf(buf, " %2d ", bits);
|
||||
decl += buf;
|
||||
if (m_evcd) {
|
||||
sprintf(buf, "<%d", code);
|
||||
decl += buf;
|
||||
sprintf(buf, "<%u", code);
|
||||
decl += buf;
|
||||
} else {
|
||||
decl += stringCode(code);
|
||||
decl += stringCode(code);
|
||||
}
|
||||
decl += " ";
|
||||
decl += basename;
|
||||
if (arraynum>=0) {
|
||||
sprintf(buf, "(%d)", arraynum);
|
||||
decl += buf;
|
||||
hiername += buf;
|
||||
sprintf(buf, "(%d)", arraynum);
|
||||
decl += buf;
|
||||
hiername += buf;
|
||||
}
|
||||
if (bussed) {
|
||||
sprintf(buf, " [%d:%d]", msb, lsb);
|
||||
decl += buf;
|
||||
sprintf(buf, " [%d:%d]", msb, lsb);
|
||||
decl += buf;
|
||||
}
|
||||
decl += " $end\n";
|
||||
m_namemapp->insert(std::make_pair(hiername,decl));
|
||||
}
|
||||
|
||||
void VerilatedVcd::declBit (vluint32_t code, const char* name, int arraynum)
|
||||
{ declare (code, name, "wire", arraynum, false, false, 0, 0); }
|
||||
{ declare(code, name, "wire", arraynum, false, false, 0, 0); }
|
||||
void VerilatedVcd::declBus (vluint32_t code, const char* name, int arraynum, int msb, int lsb)
|
||||
{ declare (code, name, "wire", arraynum, false, true, msb, lsb); }
|
||||
{ declare(code, name, "wire", arraynum, false, true, msb, lsb); }
|
||||
void VerilatedVcd::declQuad (vluint32_t code, const char* name, int arraynum, int msb, int lsb)
|
||||
{ declare (code, name, "wire", arraynum, false, true, msb, lsb); }
|
||||
{ declare(code, name, "wire", arraynum, false, true, msb, lsb); }
|
||||
void VerilatedVcd::declArray (vluint32_t code, const char* name, int arraynum, int msb, int lsb)
|
||||
{ declare (code, name, "wire", arraynum, false, true, msb, lsb); }
|
||||
{ declare(code, name, "wire", arraynum, false, true, msb, lsb); }
|
||||
void VerilatedVcd::declTriBit (vluint32_t code, const char* name, int arraynum)
|
||||
{ declare (code, name, "wire", arraynum, true, false, 0, 0); }
|
||||
{ declare(code, name, "wire", arraynum, true, false, 0, 0); }
|
||||
void VerilatedVcd::declTriBus (vluint32_t code, const char* name, int arraynum, int msb, int lsb)
|
||||
{ declare (code, name, "wire", arraynum, true, true, msb, lsb); }
|
||||
{ declare(code, name, "wire", arraynum, true, true, msb, lsb); }
|
||||
void VerilatedVcd::declTriQuad (vluint32_t code, const char* name, int arraynum, int msb, int lsb)
|
||||
{ declare (code, name, "wire", arraynum, true, true, msb, lsb); }
|
||||
{ declare(code, name, "wire", arraynum, true, true, msb, lsb); }
|
||||
void VerilatedVcd::declTriArray (vluint32_t code, const char* name, int arraynum, int msb, int lsb)
|
||||
{ declare (code, name, "wire", arraynum, true, true, msb, lsb); }
|
||||
{ declare(code, name, "wire", arraynum, true, true, msb, lsb); }
|
||||
void VerilatedVcd::declFloat (vluint32_t code, const char* name, int arraynum)
|
||||
{ declare (code, name, "real", arraynum, false, false, 31, 0); }
|
||||
{ declare(code, name, "real", arraynum, false, false, 31, 0); }
|
||||
void VerilatedVcd::declDouble (vluint32_t code, const char* name, int arraynum)
|
||||
{ declare (code, name, "real", arraynum, false, false, 63, 0); }
|
||||
{ declare(code, name, "real", arraynum, false, false, 63, 0); }
|
||||
|
||||
//=============================================================================
|
||||
|
||||
void VerilatedVcd::fullDouble (vluint32_t code, const double newval) {
|
||||
void VerilatedVcd::fullDouble(vluint32_t code, const double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
(*(reinterpret_cast<double*>(&m_sigs_oldvalp[code]))) = newval;
|
||||
// Buffer can't overflow before sprintf; we sized during declaration
|
||||
@@ -621,7 +625,7 @@ void VerilatedVcd::fullDouble (vluint32_t code, const double newval) {
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullFloat (vluint32_t code, const float newval) {
|
||||
void VerilatedVcd::fullFloat(vluint32_t code, const float newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
(*(reinterpret_cast<float*>(&m_sigs_oldvalp[code]))) = newval;
|
||||
// Buffer can't overflow before sprintf; we sized during declaration
|
||||
@@ -634,14 +638,14 @@ void VerilatedVcd::fullFloat (vluint32_t code, const float newval) {
|
||||
//=============================================================================
|
||||
// Callbacks
|
||||
|
||||
void VerilatedVcd::addCallback VL_MT_UNSAFE_ONE (
|
||||
void VerilatedVcd::addCallback(
|
||||
VerilatedVcdCallback_t initcb, VerilatedVcdCallback_t fullcb, VerilatedVcdCallback_t changecb,
|
||||
void* userthis)
|
||||
void* userthis) VL_MT_UNSAFE_ONE
|
||||
{
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(isOpen())) {
|
||||
std::string msg = std::string("Internal: ")+__FILE__+"::"+__FUNCTION__+" called with already open file";
|
||||
VL_FATAL_MT(__FILE__,__LINE__,"",msg.c_str());
|
||||
std::string msg = std::string("Internal: ")+__FILE__+"::"+__FUNCTION__+" called with already open file";
|
||||
VL_FATAL_MT(__FILE__,__LINE__,"",msg.c_str());
|
||||
}
|
||||
VerilatedVcdCallInfo* vci = new VerilatedVcdCallInfo(initcb, fullcb, changecb, userthis, m_nextCode);
|
||||
m_callbacks.push_back(vci);
|
||||
@@ -650,37 +654,37 @@ void VerilatedVcd::addCallback VL_MT_UNSAFE_ONE (
|
||||
//=============================================================================
|
||||
// Dumping
|
||||
|
||||
void VerilatedVcd::dumpFull (vluint64_t timeui) {
|
||||
void VerilatedVcd::dumpFull(vluint64_t timeui) {
|
||||
m_assertOne.check();
|
||||
dumpPrep (timeui);
|
||||
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);
|
||||
VerilatedVcdCallInfo* cip = m_callbacks[ent];
|
||||
(cip->m_fullcb)(this, cip->m_userthis, cip->m_code);
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedVcd::dump (vluint64_t timeui) {
|
||||
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
|
||||
dumpFull(timeui);
|
||||
return;
|
||||
m_fullDump = false; // No need for more full dumps
|
||||
dumpFull(timeui);
|
||||
return;
|
||||
}
|
||||
if (VL_UNLIKELY(m_rolloverMB && m_wroteBytes > this->m_rolloverMB)) {
|
||||
openNext(true);
|
||||
if (!isOpen()) return;
|
||||
openNext(true);
|
||||
if (!isOpen()) return;
|
||||
}
|
||||
dumpPrep (timeui);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedVcd::dumpPrep (vluint64_t timeui) {
|
||||
void VerilatedVcd::dumpPrep(vluint64_t timeui) {
|
||||
printStr("#");
|
||||
printTime(timeui);
|
||||
printStr("\n");
|
||||
@@ -705,40 +709,40 @@ vluint8_t ch;
|
||||
vluint64_t timestamp = 1;
|
||||
double doub = 0;
|
||||
|
||||
void vcdInit (VerilatedVcd* vcdp, void* userthis, vluint32_t code) {
|
||||
void vcdInit(VerilatedVcd* vcdp, void* userthis, vluint32_t code) {
|
||||
vcdp->scopeEscape('.');
|
||||
vcdp->module ("top");
|
||||
vcdp->declBus (0x2, "v1",-1,5,1);
|
||||
vcdp->declBus (0x3, "v2",-1,6,0);
|
||||
vcdp->module ("top.sub1");
|
||||
vcdp->declBit (0x4, "s1",-1);
|
||||
vcdp->declBit (0x5, "ch",-1);
|
||||
vcdp->module ("top.sub2");
|
||||
vcdp->declArray (0x6, "s2",-1, 40,3);
|
||||
vcdp->module("top");
|
||||
vcdp->declBus(0x2, "v1",-1,5,1);
|
||||
vcdp->declBus(0x3, "v2",-1,6,0);
|
||||
vcdp->module("top.sub1");
|
||||
vcdp->declBit(0x4, "s1",-1);
|
||||
vcdp->declBit(0x5, "ch",-1);
|
||||
vcdp->module("top.sub2");
|
||||
vcdp->declArray(0x6, "s2",-1, 40,3);
|
||||
// Note need to add 3 for next code.
|
||||
vcdp->module ("top2");
|
||||
vcdp->declBus (0x2, "t2v1",-1,4,1);
|
||||
vcdp->declTriBit (0x10, "io1", -1);
|
||||
vcdp->declTriBus (0x12, "io5", -1,4,0);
|
||||
vcdp->declTriArray (0x16, "io96",-1,95,0);
|
||||
vcdp->module("top2");
|
||||
vcdp->declBus(0x2, "t2v1",-1,4,1);
|
||||
vcdp->declTriBit (0x10, "io1", -1);
|
||||
vcdp->declTriBus (0x12, "io5", -1,4,0);
|
||||
vcdp->declTriArray(0x16, "io96",-1,95,0);
|
||||
// Note need to add 6 for next code.
|
||||
vcdp->declDouble (0x1c, "doub",-1);
|
||||
vcdp->declDouble (0x1c, "doub",-1);
|
||||
// Note need to add 2 for next code.
|
||||
}
|
||||
|
||||
void vcdFull (VerilatedVcd* vcdp, void* userthis, vluint32_t code) {
|
||||
void vcdFull(VerilatedVcd* vcdp, void* userthis, vluint32_t code) {
|
||||
vcdp->fullBus (0x2, v1,5);
|
||||
vcdp->fullBus (0x3, v2,7);
|
||||
vcdp->fullBit (0x4, s1);
|
||||
vcdp->fullBus (0x5, ch,2);
|
||||
vcdp->fullArray(0x6, &s2[0], 38);
|
||||
vcdp->fullTriBit (0x10, tri96[0]&1, tri96__tri[0]&1);
|
||||
vcdp->fullTriBus (0x12, tri96[0]&0x1f, tri96__tri[0]&0x1f, 5);
|
||||
vcdp->fullTriArray (0x16, tri96, tri96__tri, 96);
|
||||
vcdp->fullTriBit (0x10, tri96[0]&1, tri96__tri[0]&1);
|
||||
vcdp->fullTriBus (0x12, tri96[0]&0x1f, tri96__tri[0]&0x1f, 5);
|
||||
vcdp->fullTriArray(0x16, tri96, tri96__tri, 96);
|
||||
vcdp->fullDouble(0x1c, doub);
|
||||
}
|
||||
|
||||
void vcdChange (VerilatedVcd* vcdp, void* userthis, vluint32_t code) {
|
||||
void vcdChange(VerilatedVcd* vcdp, void* userthis, vluint32_t code) {
|
||||
vcdp->chgBus (0x2, v1,5);
|
||||
vcdp->chgBus (0x3, v2,7);
|
||||
vcdp->chgBit (0x4, s1);
|
||||
@@ -760,34 +764,34 @@ main() {
|
||||
ch = 0;
|
||||
doub = 0;
|
||||
{
|
||||
VerilatedVcdC* vcdp = new VerilatedVcdC;
|
||||
vcdp->spTrace()->addCallback (&vcdInit, &vcdFull, &vcdChange, 0);
|
||||
vcdp->open ("test.vcd");
|
||||
// Dumping
|
||||
vcdp->dump(timestamp++);
|
||||
v1 = 0xfff;
|
||||
tri96[2] = 4; tri96[1] = 2; tri96[0] = 1;
|
||||
tri96__tri[2] = tri96__tri[1] = tri96__tri[0] = ~0; // Still tri
|
||||
doub = 1.5;
|
||||
vcdp->dump(timestamp++);
|
||||
v2 = 0x1;
|
||||
s2[1] = 2;
|
||||
tri96__tri[2] = tri96__tri[1] = tri96__tri[0] = 0; // enable w/o data change
|
||||
doub = -1.66e13;
|
||||
vcdp->dump(timestamp++);
|
||||
ch = 2;
|
||||
tri96[2] = ~4; tri96[1] = ~2; tri96[0] = ~1;
|
||||
doub = -3.33e-13;
|
||||
vcdp->dump(timestamp++);
|
||||
vcdp->dump(timestamp++);
|
||||
VerilatedVcdC* vcdp = new VerilatedVcdC;
|
||||
vcdp->spTrace()->addCallback(&vcdInit, &vcdFull, &vcdChange, 0);
|
||||
vcdp->open("test.vcd");
|
||||
// Dumping
|
||||
vcdp->dump(timestamp++);
|
||||
v1 = 0xfff;
|
||||
tri96[2] = 4; tri96[1] = 2; tri96[0] = 1;
|
||||
tri96__tri[2] = tri96__tri[1] = tri96__tri[0] = ~0; // Still tri
|
||||
doub = 1.5;
|
||||
vcdp->dump(timestamp++);
|
||||
v2 = 0x1;
|
||||
s2[1] = 2;
|
||||
tri96__tri[2] = tri96__tri[1] = tri96__tri[0] = 0; // enable w/o data change
|
||||
doub = -1.66e13;
|
||||
vcdp->dump(timestamp++);
|
||||
ch = 2;
|
||||
tri96[2] = ~4; tri96[1] = ~2; tri96[0] = ~1;
|
||||
doub = -3.33e-13;
|
||||
vcdp->dump(timestamp++);
|
||||
vcdp->dump(timestamp++);
|
||||
# ifdef VERILATED_VCD_TEST_64BIT
|
||||
vluint64_t bytesPerDump = 15ULL;
|
||||
for (vluint64_t i=0; i<((1ULL<<32) / bytesPerDump); i++) {
|
||||
v1 = i;
|
||||
vcdp->dump(timestamp++);
|
||||
}
|
||||
vluint64_t bytesPerDump = 15ULL;
|
||||
for (vluint64_t i=0; i<((1ULL<<32) / bytesPerDump); i++) {
|
||||
v1 = i;
|
||||
vcdp->dump(timestamp++);
|
||||
}
|
||||
# endif
|
||||
vcdp->close();
|
||||
vcdp->close();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
+236
-236
@@ -26,9 +26,9 @@
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
class VerilatedVcd;
|
||||
class VerilatedVcdCallInfo;
|
||||
@@ -40,7 +40,7 @@ class VerilatedVcdCallInfo;
|
||||
|
||||
class VerilatedVcdFile {
|
||||
private:
|
||||
int m_fd; ///< File descriptor we're writing to
|
||||
int m_fd; ///< File descriptor we're writing to
|
||||
public:
|
||||
// METHODS
|
||||
VerilatedVcdFile() : m_fd(0) {}
|
||||
@@ -57,10 +57,10 @@ public:
|
||||
class VerilatedVcdSig {
|
||||
protected:
|
||||
friend class VerilatedVcd;
|
||||
vluint32_t m_code; ///< VCD file code number
|
||||
int m_bits; ///< Size of value in bits
|
||||
VerilatedVcdSig (vluint32_t code, int bits)
|
||||
: m_code(code), m_bits(bits) {}
|
||||
vluint32_t m_code; ///< VCD file code number
|
||||
int m_bits; ///< Size of value in bits
|
||||
VerilatedVcdSig(vluint32_t code, int bits)
|
||||
: m_code(code), m_bits(bits) {}
|
||||
public:
|
||||
~VerilatedVcdSig() {}
|
||||
};
|
||||
@@ -76,75 +76,75 @@ typedef void (*VerilatedVcdCallback_t)(VerilatedVcd* vcdp, void* userthis, vluin
|
||||
|
||||
class VerilatedVcd {
|
||||
private:
|
||||
VerilatedVcdFile* m_filep; ///< File we're writing to
|
||||
bool m_fileNewed; ///< m_filep needs destruction
|
||||
bool m_isOpen; ///< True indicates open file
|
||||
bool m_evcd; ///< True for evcd format
|
||||
std::string m_filename; ///< Filename we're writing to (if open)
|
||||
vluint64_t m_rolloverMB; ///< MB of file size to rollover at
|
||||
char m_scopeEscape; ///< Character to separate scope components
|
||||
int m_modDepth; ///< Depth of module hierarchy
|
||||
bool m_fullDump; ///< True indicates dump ignoring if changed
|
||||
vluint32_t m_nextCode; ///< Next code number to assign
|
||||
std::string m_modName; ///< Module name being traced now
|
||||
double m_timeRes; ///< Time resolution (ns/ms etc)
|
||||
double m_timeUnit; ///< Time units (ns/ms etc)
|
||||
vluint64_t m_timeLastDump; ///< Last time we did a dump
|
||||
VerilatedVcdFile* m_filep; ///< File we're writing to
|
||||
bool m_fileNewed; ///< m_filep needs destruction
|
||||
bool m_isOpen; ///< True indicates open file
|
||||
bool m_evcd; ///< True for evcd format
|
||||
std::string m_filename; ///< Filename we're writing to (if open)
|
||||
vluint64_t m_rolloverMB; ///< MB of file size to rollover at
|
||||
char m_scopeEscape; ///< Character to separate scope components
|
||||
int m_modDepth; ///< Depth of module hierarchy
|
||||
bool m_fullDump; ///< True indicates dump ignoring if changed
|
||||
vluint32_t m_nextCode; ///< Next code number to assign
|
||||
std::string m_modName; ///< Module name being traced now
|
||||
double m_timeRes; ///< Time resolution (ns/ms etc)
|
||||
double m_timeUnit; ///< Time units (ns/ms etc)
|
||||
vluint64_t m_timeLastDump; ///< Last time we did a dump
|
||||
|
||||
char* m_wrBufp; ///< Output buffer
|
||||
char* m_wrFlushp; ///< Output buffer flush trigger location
|
||||
char* m_writep; ///< Write pointer into output buffer
|
||||
vluint64_t m_wrChunkSize; ///< Output buffer size
|
||||
vluint64_t m_wroteBytes; ///< Number of bytes written to this file
|
||||
char* m_wrBufp; ///< Output buffer
|
||||
char* m_wrFlushp; ///< Output buffer flush trigger location
|
||||
char* m_writep; ///< Write pointer into output buffer
|
||||
vluint64_t m_wrChunkSize; ///< Output buffer size
|
||||
vluint64_t m_wroteBytes; ///< Number of bytes written to this file
|
||||
|
||||
vluint32_t* m_sigs_oldvalp; ///< Pointer to old signal values
|
||||
vluint32_t* m_sigs_oldvalp; ///< Pointer to old signal values
|
||||
typedef std::vector<VerilatedVcdSig> SigVec;
|
||||
SigVec m_sigs; ///< Pointer to signal information
|
||||
SigVec m_sigs; ///< Pointer to signal information
|
||||
typedef std::vector<VerilatedVcdCallInfo*> CallbackVec;
|
||||
CallbackVec m_callbacks; ///< Routines to perform dumping
|
||||
CallbackVec m_callbacks; ///< Routines to perform dumping
|
||||
typedef std::map<std::string,std::string> NameMap;
|
||||
NameMap* m_namemapp; ///< List of names for the header
|
||||
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() 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
|
||||
if (VL_UNLIKELY(m_writep > m_wrFlushp)) {
|
||||
bufferFlush();
|
||||
}
|
||||
// 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_writep > m_wrFlushp)) {
|
||||
bufferFlush();
|
||||
}
|
||||
}
|
||||
void closePrev();
|
||||
void closeErr();
|
||||
void openNext();
|
||||
void makeNameMap();
|
||||
void deleteNameMap();
|
||||
void printIndent (int levelchange);
|
||||
void printStr (const char* str);
|
||||
void printQuad (vluint64_t n);
|
||||
void printTime (vluint64_t timeui);
|
||||
void declare (vluint32_t code, const char* name, const char* wirep,
|
||||
int arraynum, bool tri, bool bussed, int msb, int lsb);
|
||||
void printIndent(int level_change);
|
||||
void printStr(const char* str);
|
||||
void printQuad(vluint64_t n);
|
||||
void printTime(vluint64_t timeui);
|
||||
void declare(vluint32_t code, const char* name, const char* wirep,
|
||||
int arraynum, bool tri, bool bussed, int msb, int lsb);
|
||||
|
||||
void dumpHeader();
|
||||
void dumpPrep (vluint64_t timeui);
|
||||
void dumpFull (vluint64_t timeui);
|
||||
void dumpPrep(vluint64_t timeui);
|
||||
void dumpFull(vluint64_t timeui);
|
||||
// cppcheck-suppress functionConst
|
||||
void dumpDone ();
|
||||
inline void printCode (vluint32_t code) {
|
||||
if (code>=(94*94*94)) *m_writep++ = static_cast<char>((code/94/94/94)%94+33);
|
||||
if (code>=(94*94)) *m_writep++ = static_cast<char>((code/94/94)%94+33);
|
||||
if (code>=(94)) *m_writep++ = static_cast<char>((code/94)%94+33);
|
||||
*m_writep++ = static_cast<char>((code)%94+33);
|
||||
void dumpDone();
|
||||
inline void printCode(vluint32_t code) {
|
||||
if (code>=(94*94*94)) *m_writep++ = static_cast<char>((code/94/94/94)%94+33);
|
||||
if (code>=(94*94)) *m_writep++ = static_cast<char>((code/94/94)%94+33);
|
||||
if (code>=(94)) *m_writep++ = static_cast<char>((code/94)%94+33);
|
||||
*m_writep++ = static_cast<char>((code)%94+33);
|
||||
}
|
||||
static std::string stringCode (vluint32_t code) VL_PURE {
|
||||
std::string out;
|
||||
if (code>=(94*94*94)) out += static_cast<char>((code/94/94/94)%94+33);
|
||||
if (code>=(94*94)) out += static_cast<char>((code/94/94)%94+33);
|
||||
if (code>=(94)) out += static_cast<char>((code/94)%94+33);
|
||||
return out + static_cast<char>((code)%94+33);
|
||||
static std::string stringCode(vluint32_t code) VL_PURE {
|
||||
std::string out;
|
||||
if (code>=(94*94*94)) out += static_cast<char>((code/94/94/94)%94+33);
|
||||
if (code>=(94*94)) out += static_cast<char>((code/94/94)%94+33);
|
||||
if (code>=(94)) out += static_cast<char>((code/94)%94+33);
|
||||
return out + static_cast<char>((code)%94+33);
|
||||
}
|
||||
|
||||
// CONSTRUCTORS
|
||||
@@ -165,34 +165,34 @@ public:
|
||||
|
||||
// METHODS
|
||||
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 openNext(bool incFilename); ///< Open next data-only 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()); }
|
||||
void set_time_unit(const char* unitp); ///< Set time units (s/ms, defaults to ns)
|
||||
void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||
|
||||
void set_time_resolution (const char* unit); ///< Set time resolution (s/ms, defaults to ns)
|
||||
void set_time_resolution (const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||
void set_time_resolution(const char* unitp); ///< Set time resolution (s/ms, defaults to ns)
|
||||
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||
|
||||
double timescaleToDouble (const char* unitp);
|
||||
std::string doubleToTimescale (double value);
|
||||
double timescaleToDouble(const char* unitp);
|
||||
std::string doubleToTimescale(double value);
|
||||
|
||||
/// Inside dumping routines, called each cycle to make the dump
|
||||
void dump (vluint64_t timeui);
|
||||
void dump(vluint64_t timeui);
|
||||
/// Call dump with a absolute unscaled time in seconds
|
||||
void dumpSeconds (double secs) { dump(static_cast<vluint64_t>(secs * m_timeRes)); }
|
||||
void dumpSeconds(double secs) { dump(static_cast<vluint64_t>(secs * m_timeRes)); }
|
||||
|
||||
/// Inside dumping routines, declare callbacks for tracings
|
||||
void addCallback (VerilatedVcdCallback_t init, VerilatedVcdCallback_t full,
|
||||
VerilatedVcdCallback_t change,
|
||||
void* userthis) VL_MT_UNSAFE_ONE;
|
||||
void addCallback(VerilatedVcdCallback_t initcb, VerilatedVcdCallback_t fullcb,
|
||||
VerilatedVcdCallback_t changecb,
|
||||
void* userthis) VL_MT_UNSAFE_ONE;
|
||||
|
||||
/// Inside dumping routines, declare a module
|
||||
void module (const std::string& name);
|
||||
void module(const std::string& name);
|
||||
/// Inside dumping routines, declare a signal
|
||||
void declBit (vluint32_t code, const char* name, int arraynum);
|
||||
void declBus (vluint32_t code, const char* name, int arraynum, int msb, int lsb);
|
||||
@@ -204,192 +204,192 @@ public:
|
||||
void declTriArray (vluint32_t code, const char* name, int arraynum, int msb, int lsb);
|
||||
void declDouble (vluint32_t code, const char* name, int arraynum);
|
||||
void declFloat (vluint32_t code, const char* name, int arraynum);
|
||||
// ... other module_start for submodules (based on cell name)
|
||||
// ... other module_start for submodules (based on cell name)
|
||||
|
||||
/// Inside dumping routines, dump one signal
|
||||
void fullBit (vluint32_t code, const vluint32_t newval) {
|
||||
// Note the &1, so we don't require clean input -- makes more common no change case faster
|
||||
m_sigs_oldvalp[code] = newval;
|
||||
*m_writep++=('0'+static_cast<char>(newval&1)); printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
void fullBit(vluint32_t code, const vluint32_t newval) {
|
||||
// Note the &1, so we don't require clean input -- makes more common no change case faster
|
||||
m_sigs_oldvalp[code] = newval;
|
||||
*m_writep++=('0'+static_cast<char>(newval&1)); printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void fullBus (vluint32_t code, const vluint32_t newval, int bits) {
|
||||
m_sigs_oldvalp[code] = newval;
|
||||
*m_writep++='b';
|
||||
for (int bit=bits-1; bit>=0; --bit) {
|
||||
*m_writep++=((newval&(1L<<bit))?'1':'0');
|
||||
}
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
void fullBus(vluint32_t code, const vluint32_t newval, int bits) {
|
||||
m_sigs_oldvalp[code] = newval;
|
||||
*m_writep++='b';
|
||||
for (int bit=bits-1; bit>=0; --bit) {
|
||||
*m_writep++=((newval&(1L<<bit))?'1':'0');
|
||||
}
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void fullQuad (vluint32_t code, const vluint64_t newval, int bits) {
|
||||
(*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) = newval;
|
||||
*m_writep++='b';
|
||||
for (int bit=bits-1; bit>=0; --bit) {
|
||||
*m_writep++=((newval&(1ULL<<bit))?'1':'0');
|
||||
}
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
void fullQuad(vluint32_t code, const vluint64_t newval, int bits) {
|
||||
(*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) = newval;
|
||||
*m_writep++='b';
|
||||
for (int bit=bits-1; bit>=0; --bit) {
|
||||
*m_writep++=((newval&(1ULL<<bit))?'1':'0');
|
||||
}
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void fullArray (vluint32_t code, const vluint32_t* newval, int bits) {
|
||||
for (int word=0; word<(((bits-1)/32)+1); ++word) {
|
||||
m_sigs_oldvalp[code+word] = newval[word];
|
||||
}
|
||||
*m_writep++='b';
|
||||
for (int bit=bits-1; bit>=0; --bit) {
|
||||
*m_writep++=((newval[(bit/32)]&(1L<<(bit&0x1f)))?'1':'0');
|
||||
}
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
void fullArray(vluint32_t code, const vluint32_t* newval, int bits) {
|
||||
for (int word=0; word<(((bits-1)/32)+1); ++word) {
|
||||
m_sigs_oldvalp[code+word] = newval[word];
|
||||
}
|
||||
*m_writep++='b';
|
||||
for (int bit=bits-1; bit>=0; --bit) {
|
||||
*m_writep++=((newval[(bit/32)]&(1L<<(bit&0x1f)))?'1':'0');
|
||||
}
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void fullTriBit (vluint32_t code, const vluint32_t newval, const vluint32_t newtri) {
|
||||
m_sigs_oldvalp[code] = newval;
|
||||
m_sigs_oldvalp[code+1] = newtri;
|
||||
*m_writep++ = "01zz"[m_sigs_oldvalp[code]
|
||||
| (m_sigs_oldvalp[code+1]<<1)];
|
||||
printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
void fullTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri) {
|
||||
m_sigs_oldvalp[code] = newval;
|
||||
m_sigs_oldvalp[code+1] = newtri;
|
||||
*m_writep++ = "01zz"[m_sigs_oldvalp[code]
|
||||
| (m_sigs_oldvalp[code+1]<<1)];
|
||||
printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void fullTriBus (vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits) {
|
||||
m_sigs_oldvalp[code] = newval;
|
||||
m_sigs_oldvalp[code+1] = newtri;
|
||||
*m_writep++='b';
|
||||
for (int bit=bits-1; bit>=0; --bit) {
|
||||
*m_writep++ = "01zz"[((newval >> bit)&1)
|
||||
| (((newtri >> bit)&1)<<1)];
|
||||
}
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
void fullTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits) {
|
||||
m_sigs_oldvalp[code] = newval;
|
||||
m_sigs_oldvalp[code+1] = newtri;
|
||||
*m_writep++='b';
|
||||
for (int bit=bits-1; bit>=0; --bit) {
|
||||
*m_writep++ = "01zz"[((newval >> bit)&1)
|
||||
| (((newtri >> bit)&1)<<1)];
|
||||
}
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void fullTriQuad (vluint32_t code, const vluint64_t newval, const vluint32_t newtri, int bits) {
|
||||
(*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) = newval;
|
||||
(*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code+1]))) = newtri;
|
||||
*m_writep++='b';
|
||||
for (int bit=bits-1; bit>=0; --bit) {
|
||||
*m_writep++ = "01zz"[((newval >> bit)&1ULL)
|
||||
| (((newtri >> bit)&1ULL)<<1ULL)];
|
||||
}
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
void fullTriQuad(vluint32_t code, const vluint64_t newval, const vluint32_t newtri, int bits) {
|
||||
(*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) = newval;
|
||||
(*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code+1]))) = newtri;
|
||||
*m_writep++='b';
|
||||
for (int bit=bits-1; bit>=0; --bit) {
|
||||
*m_writep++ = "01zz"[((newval >> bit)&1ULL)
|
||||
| (((newtri >> bit)&1ULL)<<1ULL)];
|
||||
}
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void fullTriArray (vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip, int bits) {
|
||||
for (int word=0; word<(((bits-1)/32)+1); ++word) {
|
||||
m_sigs_oldvalp[code+word*2] = newvalp[word];
|
||||
m_sigs_oldvalp[code+word*2+1] = newtrip[word];
|
||||
}
|
||||
*m_writep++='b';
|
||||
for (int bit=bits-1; bit>=0; --bit) {
|
||||
vluint32_t valbit = (newvalp[(bit/32)]>>(bit&0x1f)) & 1;
|
||||
vluint32_t tribit = (newtrip[(bit/32)]>>(bit&0x1f)) & 1;
|
||||
*m_writep++ = "01zz"[valbit | (tribit<<1)];
|
||||
}
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
void fullTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip, int bits) {
|
||||
for (int word=0; word<(((bits-1)/32)+1); ++word) {
|
||||
m_sigs_oldvalp[code+word*2] = newvalp[word];
|
||||
m_sigs_oldvalp[code+word*2+1] = newtrip[word];
|
||||
}
|
||||
*m_writep++='b';
|
||||
for (int bit=bits-1; bit>=0; --bit) {
|
||||
vluint32_t valbit = (newvalp[(bit/32)]>>(bit&0x1f)) & 1;
|
||||
vluint32_t tribit = (newtrip[(bit/32)]>>(bit&0x1f)) & 1;
|
||||
*m_writep++ = "01zz"[valbit | (tribit<<1)];
|
||||
}
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void fullDouble (vluint32_t code, const double newval);
|
||||
void fullFloat (vluint32_t code, const float newval);
|
||||
void fullDouble(vluint32_t code, const double newval);
|
||||
void fullFloat(vluint32_t code, const float newval);
|
||||
|
||||
/// Inside dumping routines, dump one signal as unknowns
|
||||
/// Presently this code doesn't change the oldval vector.
|
||||
/// Thus this is for special standalone applications that after calling
|
||||
/// fullBitX, must when then value goes non-X call fullBit.
|
||||
inline void fullBitX (vluint32_t code) {
|
||||
*m_writep++='x'; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
inline void fullBitX(vluint32_t code) {
|
||||
*m_writep++='x'; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
}
|
||||
inline void fullBusX (vluint32_t code, int bits) {
|
||||
*m_writep++='b';
|
||||
for (int bit=bits-1; bit>=0; --bit) {
|
||||
*m_writep++='x';
|
||||
}
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
inline void fullBusX(vluint32_t code, int bits) {
|
||||
*m_writep++='b';
|
||||
for (int bit=bits-1; bit>=0; --bit) {
|
||||
*m_writep++='x';
|
||||
}
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
}
|
||||
inline void fullQuadX (vluint32_t code, int bits) { fullBusX (code, bits); }
|
||||
inline void fullArrayX (vluint32_t code, int bits) { fullBusX (code, bits); }
|
||||
inline void fullQuadX(vluint32_t code, int bits) { fullBusX(code, bits); }
|
||||
inline void fullArrayX(vluint32_t code, int bits) { fullBusX(code, bits); }
|
||||
|
||||
/// Inside dumping routines, dump one signal if it has changed
|
||||
inline void chgBit (vluint32_t code, const vluint32_t newval) {
|
||||
vluint32_t diff = m_sigs_oldvalp[code] ^ newval;
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
// Verilator 3.510 and newer provide clean input, so the below is only for back compatibility
|
||||
if (VL_UNLIKELY(diff & 1)) { // Change after clean?
|
||||
fullBit (code, newval);
|
||||
}
|
||||
}
|
||||
inline void chgBit(vluint32_t code, const vluint32_t newval) {
|
||||
vluint32_t diff = m_sigs_oldvalp[code] ^ newval;
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
// Verilator 3.510 and newer provide clean input, so the below is only for back compatibility
|
||||
if (VL_UNLIKELY(diff & 1)) { // Change after clean?
|
||||
fullBit(code, newval);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgBus (vluint32_t code, const vluint32_t newval, int bits) {
|
||||
vluint32_t diff = m_sigs_oldvalp[code] ^ newval;
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits==32 || (diff & ((1U<<bits)-1) ))) {
|
||||
fullBus (code, newval, bits);
|
||||
}
|
||||
}
|
||||
inline void chgBus(vluint32_t code, const vluint32_t newval, int bits) {
|
||||
vluint32_t diff = m_sigs_oldvalp[code] ^ newval;
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits==32 || (diff & ((1U<<bits)-1) ))) {
|
||||
fullBus(code, newval, bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgQuad (vluint32_t code, const vluint64_t newval, int bits) {
|
||||
vluint64_t diff = (*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) ^ newval;
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits==64 || (diff & ((1ULL<<bits)-1) ))) {
|
||||
fullQuad(code, newval, bits);
|
||||
}
|
||||
}
|
||||
inline void chgQuad(vluint32_t code, const vluint64_t newval, int bits) {
|
||||
vluint64_t diff = (*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) ^ newval;
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits==64 || (diff & ((1ULL<<bits)-1) ))) {
|
||||
fullQuad(code, newval, bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgArray (vluint32_t code, const vluint32_t* newval, int bits) {
|
||||
for (int word=0; word<(((bits-1)/32)+1); ++word) {
|
||||
if (VL_UNLIKELY(m_sigs_oldvalp[code+word] ^ newval[word])) {
|
||||
fullArray (code,newval,bits);
|
||||
return;
|
||||
}
|
||||
}
|
||||
inline void chgArray(vluint32_t code, const vluint32_t* newval, int bits) {
|
||||
for (int word=0; word<(((bits-1)/32)+1); ++word) {
|
||||
if (VL_UNLIKELY(m_sigs_oldvalp[code+word] ^ newval[word])) {
|
||||
fullArray(code,newval,bits);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriBit (vluint32_t code, const vluint32_t newval, const vluint32_t newtri) {
|
||||
vluint32_t diff = ((m_sigs_oldvalp[code] ^ newval)
|
||||
| (m_sigs_oldvalp[code+1] ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
// Verilator 3.510 and newer provide clean input, so the below is only for back compatibility
|
||||
if (VL_UNLIKELY(diff & 1)) { // Change after clean?
|
||||
fullTriBit (code, newval, newtri);
|
||||
}
|
||||
}
|
||||
inline void chgTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri) {
|
||||
vluint32_t diff = ((m_sigs_oldvalp[code] ^ newval)
|
||||
| (m_sigs_oldvalp[code+1] ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
// Verilator 3.510 and newer provide clean input, so the below is only for back compatibility
|
||||
if (VL_UNLIKELY(diff & 1)) { // Change after clean?
|
||||
fullTriBit(code, newval, newtri);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriBus (vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits) {
|
||||
vluint32_t diff = ((m_sigs_oldvalp[code] ^ newval)
|
||||
| (m_sigs_oldvalp[code+1] ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits==32 || (diff & ((1U<<bits)-1) ))) {
|
||||
fullTriBus (code, newval, newtri, bits);
|
||||
}
|
||||
}
|
||||
inline void chgTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits) {
|
||||
vluint32_t diff = ((m_sigs_oldvalp[code] ^ newval)
|
||||
| (m_sigs_oldvalp[code+1] ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits==32 || (diff & ((1U<<bits)-1) ))) {
|
||||
fullTriBus(code, newval, newtri, bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriQuad (vluint32_t code, const vluint64_t newval, const vluint32_t newtri, int bits) {
|
||||
vluint64_t diff = ( ((*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) ^ newval)
|
||||
| ((*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code+1]))) ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits==64 || (diff & ((1ULL<<bits)-1) ))) {
|
||||
fullTriQuad(code, newval, newtri, bits);
|
||||
}
|
||||
}
|
||||
inline void chgTriQuad(vluint32_t code, const vluint64_t newval, const vluint32_t newtri, int bits) {
|
||||
vluint64_t diff = ( ((*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) ^ newval)
|
||||
| ((*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code+1]))) ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits==64 || (diff & ((1ULL<<bits)-1) ))) {
|
||||
fullTriQuad(code, newval, newtri, bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriArray (vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip, int bits) {
|
||||
for (int word=0; word<(((bits-1)/32)+1); ++word) {
|
||||
if (VL_UNLIKELY((m_sigs_oldvalp[code+word*2] ^ newvalp[word])
|
||||
| (m_sigs_oldvalp[code+word*2+1] ^ newtrip[word]))) {
|
||||
fullTriArray (code,newvalp,newtrip,bits);
|
||||
return;
|
||||
}
|
||||
}
|
||||
inline void chgTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip, int bits) {
|
||||
for (int word=0; word<(((bits-1)/32)+1); ++word) {
|
||||
if (VL_UNLIKELY((m_sigs_oldvalp[code+word*2] ^ newvalp[word])
|
||||
| (m_sigs_oldvalp[code+word*2+1] ^ newtrip[word]))) {
|
||||
fullTriArray(code,newvalp,newtrip,bits);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgDouble (vluint32_t code, const double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
if (VL_UNLIKELY((*(reinterpret_cast<double*>(&m_sigs_oldvalp[code]))) != newval)) {
|
||||
fullDouble (code, newval);
|
||||
}
|
||||
inline void chgDouble(vluint32_t code, const double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
if (VL_UNLIKELY((*(reinterpret_cast<double*>(&m_sigs_oldvalp[code]))) != newval)) {
|
||||
fullDouble(code, newval);
|
||||
}
|
||||
}
|
||||
inline void chgFloat (vluint32_t code, const float newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
if (VL_UNLIKELY((*(reinterpret_cast<float*>(&m_sigs_oldvalp[code]))) != newval)) {
|
||||
fullFloat (code, newval);
|
||||
}
|
||||
inline void chgFloat(vluint32_t code, const float newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
if (VL_UNLIKELY((*(reinterpret_cast<float*>(&m_sigs_oldvalp[code]))) != newval)) {
|
||||
fullFloat(code, newval);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -404,7 +404,7 @@ protected:
|
||||
/// Thread safety: Unless otherwise indicated, every function is VL_MT_UNSAFE_ONE
|
||||
|
||||
class VerilatedVcdC {
|
||||
VerilatedVcd m_sptrace; ///< Trace file being created
|
||||
VerilatedVcd m_sptrace; ///< Trace file being created
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedVcdC);
|
||||
@@ -431,23 +431,23 @@ public:
|
||||
/// Flush dump
|
||||
void flush() VL_MT_UNSAFE_ONE { m_sptrace.flush(); }
|
||||
/// Write one cycle of dump data
|
||||
void dump (vluint64_t timeui) { m_sptrace.dump(timeui); }
|
||||
void dump(vluint64_t timeui) { m_sptrace.dump(timeui); }
|
||||
/// Write one cycle of dump data - backward compatible and to reduce
|
||||
/// conversion warnings. It's better to use a vluint64_t time instead.
|
||||
void dump (double timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
void dump (vluint32_t timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
void dump (int timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
void dump(double timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
void dump(vluint32_t timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
void dump(int timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
/// Set time units (s/ms, defaults to ns)
|
||||
/// See also VL_TIME_PRECISION, and VL_TIME_MULTIPLIER in verilated.h
|
||||
void set_time_unit (const char* unit) { m_sptrace.set_time_unit(unit); }
|
||||
void set_time_unit (const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||
void set_time_unit(const char* unit) { m_sptrace.set_time_unit(unit); }
|
||||
void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||
/// Set time resolution (s/ms, defaults to ns)
|
||||
/// See also VL_TIME_PRECISION, and VL_TIME_MULTIPLIER in verilated.h
|
||||
void set_time_resolution (const char* unit) { m_sptrace.set_time_resolution(unit); }
|
||||
void set_time_resolution (const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||
void set_time_resolution(const char* unit) { m_sptrace.set_time_resolution(unit); }
|
||||
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||
|
||||
/// Internal class access
|
||||
inline VerilatedVcd* spTrace () { return &m_sptrace; };
|
||||
inline VerilatedVcd* spTrace() { return &m_sptrace; };
|
||||
};
|
||||
|
||||
#endif // guard
|
||||
#endif // guard
|
||||
|
||||
@@ -31,8 +31,8 @@
|
||||
#if (SYSTEMC_VERSION>=20050714)
|
||||
// SystemC 2.1.v1
|
||||
// cppcheck-suppress unusedFunction
|
||||
void VerilatedVcdSc::write_comment (const std::string &) {}
|
||||
void VerilatedVcdSc::trace (const unsigned int &, const std::string &, const char **) {}
|
||||
void VerilatedVcdSc::write_comment(const std::string &) {}
|
||||
void VerilatedVcdSc::trace(const unsigned int &, const std::string &, const char **) {}
|
||||
|
||||
# define DECL_TRACE_METHOD_A(tp) \
|
||||
void VerilatedVcdSc::trace( const tp& object, const std::string& name ) {}
|
||||
@@ -81,8 +81,8 @@ void VerilatedVcdSc::trace (const unsigned int &, const std::string &, const cha
|
||||
#elif (SYSTEMC_VERSION>20011000)
|
||||
// SystemC 2.0.1
|
||||
// cppcheck-suppress unusedFunction
|
||||
void VerilatedVcdSc::write_comment (const sc_string &) {}
|
||||
void VerilatedVcdSc::trace (const unsigned int &, const sc_string &, const char **) {}
|
||||
void VerilatedVcdSc::write_comment(const sc_string &) {}
|
||||
void VerilatedVcdSc::trace(const unsigned int &, const sc_string &, const char **) {}
|
||||
|
||||
#define DECL_TRACE_METHOD_A(tp) \
|
||||
void VerilatedVcdSc::trace( const tp& object, const sc_string& name ) {}
|
||||
@@ -124,8 +124,8 @@ void VerilatedVcdSc::trace (const unsigned int &, const sc_string &, const char
|
||||
#else
|
||||
// SystemC 1.2.1beta
|
||||
// cppcheck-suppress unusedFunction
|
||||
void VerilatedVcdSc::write_comment (const sc_string &) {}
|
||||
void VerilatedVcdSc::trace (const unsigned int &, const sc_string &, const char **) {}
|
||||
void VerilatedVcdSc::write_comment(const sc_string &) {}
|
||||
void VerilatedVcdSc::trace(const unsigned int &, const sc_string &, const char **) {}
|
||||
|
||||
#define DECL_TRACE_METHOD_A(tp) \
|
||||
void VerilatedVcdSc::trace( const tp& object, const sc_string& name ) {}
|
||||
|
||||
+35
-34
@@ -25,6 +25,7 @@
|
||||
#ifndef _VERILATED_VCD_SC_H_
|
||||
#define _VERILATED_VCD_SC_H_ 1
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated_sc.h"
|
||||
#include "verilated_vcd_c.h"
|
||||
|
||||
@@ -43,33 +44,33 @@ class VerilatedVcdSc
|
||||
VL_UNCOPYABLE(VerilatedVcdSc);
|
||||
public:
|
||||
VerilatedVcdSc() {
|
||||
sc_get_curr_simcontext()->add_trace_file(this);
|
||||
sc_get_curr_simcontext()->add_trace_file(this);
|
||||
# if (SYSTEMC_VERSION>=20060505)
|
||||
// We want to avoid a depreciated warning, but still be back compatible.
|
||||
// Turning off the message just for this still results in an annoying "to turn off" message.
|
||||
sc_time t1sec(1,SC_SEC);
|
||||
if (t1sec.to_default_time_units()!=0) {
|
||||
sc_time tunits(1.0/t1sec.to_default_time_units(),SC_SEC);
|
||||
spTrace()->set_time_unit(tunits.to_string());
|
||||
}
|
||||
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
|
||||
// We want to avoid a depreciated warning, but still be back compatible.
|
||||
// Turning off the message just for this still results in an annoying "to turn off" message.
|
||||
sc_time t1sec(1,SC_SEC);
|
||||
if (t1sec.to_default_time_units()!=0) {
|
||||
sc_time tunits(1.0/t1sec.to_default_time_units(),SC_SEC);
|
||||
spTrace()->set_time_unit(tunits.to_string());
|
||||
}
|
||||
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
|
||||
# elif (SYSTEMC_VERSION>20011000)
|
||||
// To confuse matters 2.1.beta returns a char* here, while 2.1.v1 returns a std::string
|
||||
// we allow both flavors with overloaded set_time_* functions.
|
||||
spTrace()->set_time_unit(sc_get_default_time_unit().to_string());
|
||||
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
|
||||
// To confuse matters 2.1.beta returns a char* here, while 2.1.v1 returns a std::string
|
||||
// we allow both flavors with overloaded set_time_* functions.
|
||||
spTrace()->set_time_unit(sc_get_default_time_unit().to_string());
|
||||
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
|
||||
# endif
|
||||
}
|
||||
virtual ~VerilatedVcdSc() {}
|
||||
|
||||
// METHODS
|
||||
/// Called by SystemC simulate()
|
||||
virtual void cycle (bool delta_cycle) {
|
||||
virtual void cycle(bool delta_cycle) {
|
||||
# if (SYSTEMC_VERSION>20011000)
|
||||
if (!delta_cycle) { this->dump(sc_time_stamp().to_double()); }
|
||||
if (!delta_cycle) { this->dump(sc_time_stamp().to_double()); }
|
||||
# else
|
||||
// VCD files must have integer timestamps, so we write all times in increments of time_resolution
|
||||
if (!delta_cycle) { this->dump(sc_time_stamp().to_double()); }
|
||||
// VCD files must have integer timestamps, so we write all times in increments of time_resolution
|
||||
if (!delta_cycle) { this->dump(sc_time_stamp().to_double()); }
|
||||
# endif
|
||||
}
|
||||
|
||||
@@ -78,10 +79,10 @@ private:
|
||||
|
||||
#ifdef NC_SYSTEMC
|
||||
// Cadence Incisive has these as abstract functions so we must create them
|
||||
virtual void set_time_unit( int exponent10_seconds ) {} // deprecated
|
||||
virtual void set_time_unit(int exponent10_seconds) {} // deprecated
|
||||
#endif
|
||||
#if defined(NC_SYSTEMC) || (SYSTEMC_VERSION>=20111100)
|
||||
virtual void set_time_unit( double v, sc_time_unit tu ) {}
|
||||
virtual void set_time_unit(double v, sc_time_unit tu) {}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -89,12 +90,12 @@ private:
|
||||
# if (SYSTEMC_VERSION>=20050714)
|
||||
// SystemC 2.1.v1
|
||||
# define DECL_TRACE_METHOD_A(tp) \
|
||||
virtual void trace( const tp& object, const std::string& name );
|
||||
virtual void trace(const tp& object, const std::string& name);
|
||||
# define DECL_TRACE_METHOD_B(tp) \
|
||||
virtual void trace( const tp& object, const std::string& name, int width );
|
||||
virtual void trace(const tp& object, const std::string& name, int width);
|
||||
|
||||
virtual void write_comment (const std::string &);
|
||||
virtual void trace (const unsigned int &, const std::string &, const char **);
|
||||
virtual void write_comment(const std::string &);
|
||||
virtual void trace(const unsigned int &, const std::string &, const char **);
|
||||
|
||||
#if (SYSTEMC_VERSION>=20171012)
|
||||
DECL_TRACE_METHOD_A( sc_event )
|
||||
@@ -138,14 +139,14 @@ private:
|
||||
# elif (SYSTEMC_VERSION>20011000)
|
||||
// SystemC 2.0.1
|
||||
# define DECL_TRACE_METHOD_A(tp) \
|
||||
virtual void trace( const tp& object, const sc_string& name );
|
||||
virtual void trace(const tp& object, const sc_string& name);
|
||||
# define DECL_TRACE_METHOD_B(tp) \
|
||||
virtual void trace( const tp& object, const sc_string& name, int width );
|
||||
virtual void trace(const tp& object, const sc_string& name, int width);
|
||||
|
||||
virtual void write_comment (const sc_string &);
|
||||
virtual void trace (const unsigned int &, const sc_string &, const char **);
|
||||
virtual void delta_cycles (bool) {}
|
||||
virtual void space( int n ) {}
|
||||
virtual void write_comment(const sc_string &);
|
||||
virtual void trace(const unsigned int &, const sc_string &, const char **);
|
||||
virtual void delta_cycles(bool) {}
|
||||
virtual void space(int n) {}
|
||||
|
||||
DECL_TRACE_METHOD_A( bool )
|
||||
DECL_TRACE_METHOD_A( sc_bit )
|
||||
@@ -182,12 +183,12 @@ private:
|
||||
# else
|
||||
// SystemC 1.2.1beta
|
||||
# define DECL_TRACE_METHOD_A(tp) \
|
||||
virtual void trace( const tp& object, const sc_string& name );
|
||||
virtual void trace(const tp& object, const sc_string& name);
|
||||
# define DECL_TRACE_METHOD_B(tp) \
|
||||
virtual void trace( const tp& object, const sc_string& name, int width );
|
||||
virtual void trace(const tp& object, const sc_string& name, int width);
|
||||
|
||||
virtual void write_comment (const sc_string &);
|
||||
virtual void trace (const unsigned int &, const sc_string &, const char **);
|
||||
virtual void write_comment(const sc_string &);
|
||||
virtual void trace(const unsigned int &, const sc_string &, const char **);
|
||||
|
||||
DECL_TRACE_METHOD_A( bool )
|
||||
DECL_TRACE_METHOD_B( unsigned char )
|
||||
@@ -220,4 +221,4 @@ private:
|
||||
# undef DECL_TRACE_METHOD_B
|
||||
};
|
||||
|
||||
#endif // guard
|
||||
#endif // Guard
|
||||
|
||||
+15
-21
@@ -33,8 +33,8 @@
|
||||
#include "verilated_vpi.h"
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
//======================================================================
|
||||
// Internal constants
|
||||
@@ -79,11 +79,10 @@ public:
|
||||
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;
|
||||
}
|
||||
// +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;
|
||||
@@ -156,9 +155,8 @@ public:
|
||||
VerilatedVpioRange* nextp = new VerilatedVpioRange(*this);
|
||||
nextp->iterationInc();
|
||||
return ((nextp)->castVpiHandle());
|
||||
} else {
|
||||
return 0; // End of list - only one deep
|
||||
}
|
||||
return 0; // End of list - only one deep
|
||||
}
|
||||
};
|
||||
|
||||
@@ -275,9 +273,8 @@ public:
|
||||
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
|
||||
}
|
||||
return 0; // End of list - only one deep
|
||||
}
|
||||
};
|
||||
|
||||
@@ -388,9 +385,8 @@ public:
|
||||
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
|
||||
}
|
||||
return ~VL_ULL(0); // maxquad
|
||||
}
|
||||
static void callCbs(vluint32_t reason) {
|
||||
VpioCbList& cbObjList = s_s.m_cbObjLists[reason];
|
||||
@@ -1037,17 +1033,15 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) {
|
||||
return (new VerilatedVpioMemoryWord(varop->varp(), varop->scopep(), indx,
|
||||
indx - varop->varp()->unpacked().right()))
|
||||
->castVpiHandle();
|
||||
} else {
|
||||
if (VL_UNLIKELY(indx < varop->varp()->unpacked().left()
|
||||
|| indx > varop->varp()->unpacked().right())) return 0;
|
||||
return (new VerilatedVpioMemoryWord(varop->varp(), varop->scopep(), indx,
|
||||
indx - varop->varp()->unpacked().left()))
|
||||
->castVpiHandle();
|
||||
}
|
||||
} else {
|
||||
_VL_VPI_INTERNAL(__FILE__, __LINE__, "%s : can't resolve handle", VL_FUNC);
|
||||
return 0;
|
||||
if (VL_UNLIKELY(indx < varop->varp()->unpacked().left()
|
||||
|| indx > varop->varp()->unpacked().right())) return 0;
|
||||
return (new VerilatedVpioMemoryWord(varop->varp(), varop->scopep(), indx,
|
||||
indx - varop->varp()->unpacked().left()))
|
||||
->castVpiHandle();
|
||||
}
|
||||
_VL_VPI_INTERNAL(__FILE__, __LINE__, "%s : can't resolve handle", VL_FUNC);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// for traversing relationships
|
||||
@@ -1884,7 +1878,7 @@ PLI_INT32 vpi_free_object(vpiHandle object) {
|
||||
return vpi_release_handle(object); // Deprecated
|
||||
}
|
||||
|
||||
PLI_INT32 vpi_release_handle (vpiHandle object) {
|
||||
PLI_INT32 vpi_release_handle(vpiHandle object) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_release_handle %p\n",object););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
VerilatedVpio* vop = VerilatedVpio::castp(object);
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#ifndef _VERILATED_VPI_H_
|
||||
#define _VERILATED_VPI_H_ 1 ///< Header Guard
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h"
|
||||
#include "verilated_syms.h"
|
||||
|
||||
|
||||
+26
-24
@@ -40,7 +40,7 @@
|
||||
# ifdef _WIN32
|
||||
# define VL_ATTR_PRINTF(fmtArgNum) // GCC with MS runtime will fool the print arg checker
|
||||
# else
|
||||
# define VL_ATTR_PRINTF(fmtArgNum) __attribute__ ((format (printf, fmtArgNum, fmtArgNum+1)))
|
||||
# define VL_ATTR_PRINTF(fmtArgNum) __attribute__ ((format (printf, (fmtArgNum), (fmtArgNum)+1)))
|
||||
# endif
|
||||
# define VL_ATTR_PURE __attribute__ ((pure))
|
||||
# define VL_ATTR_UNUSED __attribute__ ((unused))
|
||||
@@ -157,21 +157,23 @@
|
||||
|
||||
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
|
||||
# define VL_EQ_DELETE = delete
|
||||
# define VL_HAS_UNIQUE_PTR
|
||||
# define VL_HAS_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>
|
||||
// By default we use std:: types in C++11.
|
||||
// Define VL_USE_UNORDERED_TYPES to test these pre-C++11 classes
|
||||
# ifdef VL_USE_UNORDERED_TYPES
|
||||
# define VL_INCLUDE_UNORDERED_MAP "verilated_unordered_set_map.h"
|
||||
# define VL_INCLUDE_UNORDERED_SET "verilated_unordered_set_map.h"
|
||||
# else
|
||||
# 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>
|
||||
# endif
|
||||
#else
|
||||
# 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>
|
||||
# define VL_INCLUDE_UNORDERED_MAP "verilated_unordered_set_map.h"
|
||||
# define VL_INCLUDE_UNORDERED_SET "verilated_unordered_set_map.h"
|
||||
#endif
|
||||
|
||||
//=========================================================================
|
||||
@@ -200,10 +202,11 @@
|
||||
//=========================================================================
|
||||
// Basic integer types
|
||||
|
||||
#ifdef VL_UINTS_DEFINED
|
||||
#elif defined(__CYGWIN__)
|
||||
#if defined(__CYGWIN__)
|
||||
|
||||
# include <stdint.h>
|
||||
# include <sys/types.h> // __WORDSIZE
|
||||
# include <unistd.h> // ssize_t
|
||||
typedef unsigned char uint8_t; ///< 8-bit unsigned type (backward compatibility)
|
||||
typedef unsigned short int uint16_t; ///< 16-bit unsigned type (backward compatibility)
|
||||
typedef unsigned char vluint8_t; ///< 8-bit unsigned type
|
||||
@@ -246,9 +249,10 @@ typedef signed __int32 ssize_t; ///< signed size_t; returned fro
|
||||
|
||||
#else // Linux or compliant Unix flavors, -m64
|
||||
|
||||
# include <stdint.h> // Linux and most flavors
|
||||
# include <unistd.h> // Linux ssize_t
|
||||
# include <inttypes.h> // Solaris
|
||||
# include <stdint.h> // Linux and most flavors
|
||||
# include <sys/types.h> // __WORDSIZE
|
||||
# include <unistd.h> // ssize_t
|
||||
typedef uint8_t vluint8_t; ///< 32-bit unsigned type
|
||||
typedef uint16_t vluint16_t; ///< 32-bit unsigned type
|
||||
typedef int vlsint32_t; ///< 32-bit signed type
|
||||
@@ -356,21 +360,19 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
||||
//=========================================================================
|
||||
// Performance counters
|
||||
|
||||
#if VL_THREADED
|
||||
/// The vluint64_t argument is loaded with a high-performance counter for profiling
|
||||
/// or 0x0 if not implemeted on this platform
|
||||
# if defined(__i386__) || defined(__x86_64__)
|
||||
# define VL_RDTSC(val) { \
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
# define VL_RDTSC(val) { \
|
||||
vluint32_t hi, lo; \
|
||||
asm volatile("rdtsc" : "=a" (lo), "=d" (hi)); \
|
||||
(val) = ((vluint64_t)lo) | (((vluint64_t)hi)<<32); \
|
||||
}
|
||||
# elif defined(__aarch64__)
|
||||
# define VL_RDTSC(val) asm volatile("mrs %[rt],PMCCNTR_EL0" : [rt] "=r" (val));
|
||||
# else
|
||||
#elif defined(__aarch64__)
|
||||
# define VL_RDTSC(val) asm volatile("mrs %[rt],PMCCNTR_EL0" : [rt] "=r" (val));
|
||||
#else
|
||||
// We just silently ignore unknown OSes, as only leads to missing statistics
|
||||
# define VL_RDTSC(val) (val) = 0;
|
||||
# endif
|
||||
# define VL_RDTSC(val) (val) = 0;
|
||||
#endif
|
||||
|
||||
//=========================================================================
|
||||
|
||||
+229
-17
@@ -155,6 +155,221 @@ provided and documented in C<V3GraphAlg.cpp>.
|
||||
|
||||
=back
|
||||
|
||||
=head2 Multithreaded Mode
|
||||
|
||||
In --threads mode, the frontend of the Verilator pipeline is the same as
|
||||
serial mode, up until V3Order.
|
||||
|
||||
V3Order builds a fine-grained, statement-level dependency graph that governs
|
||||
the ordering of code within a single eval() call. In serial mode, that
|
||||
dependency graph is used to order all statements into a total serial order.
|
||||
In parallel mode, the same dependency graph is the starting point for a
|
||||
partitioner (V3Partition).
|
||||
|
||||
The partitioner's goal is to coarsen the fine-grained DAG into a coarser
|
||||
DAG, while maintaining as much available parallelism as possible. Often the
|
||||
partitioner can transform an input graph with millions of nodes into a
|
||||
coarsened execution graph with a few dozen nodes, while maintaining enough
|
||||
parallelism to take advantage of a modern multicore CPU. Runtime
|
||||
synchronization cost is not prohibitive with so few nodes.
|
||||
|
||||
=head3 Partitioning
|
||||
|
||||
Our partitioner is similar to the one Vivek Sarkar described in his 1989
|
||||
paper "Partitioning and Scheduling Parallel Programs for Multiprocessors".
|
||||
|
||||
Let's define some terms:
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<Par Factor>
|
||||
|
||||
The available parallelism or "par-factor" of a DAG is the total cost to
|
||||
execute all nodes, divided by the cost to execute the longest critical path
|
||||
through the graph. This is the speedup you would get from running the graph
|
||||
in parallel, if given infinite CPU cores available and communication and
|
||||
synchronization are zero.
|
||||
|
||||
=item C<Macro Task>
|
||||
|
||||
When the partitioner coarsens the graph, it combines nodes together. Each
|
||||
fine-grained node represents an atomic "task"; combined nodes in the
|
||||
coarsened graph are "macro-tasks". This term comes from Sarkar. Each
|
||||
macro-task executes from start to end on one processor, without any
|
||||
synchronization to any other macro-task during its
|
||||
execution. (Synchronization only happens before the macro-task begins or
|
||||
after it ends.)
|
||||
|
||||
=item C<Edge Contraction>
|
||||
|
||||
Our partitioner, like Sarkar's, primarily relies on "edge contraction" to
|
||||
coarsen the graph. It starts with one macro-task per atomic task and
|
||||
iteratively combines pairs of edge-connected macro-tasks.
|
||||
|
||||
=item C<Local Critical Path>
|
||||
|
||||
Each node in the graph has a "local" critical path. That's the critical
|
||||
path from the start of the graph to the start of the node, plus the node's
|
||||
cost, plus the critical path from the end of the node to the end of the
|
||||
graph.
|
||||
|
||||
=back
|
||||
|
||||
Sarkar calls out an important trade-off: coarsening the graph reduces
|
||||
runtime synchronization overhead among the macro-tasks, but it tends to
|
||||
increase the critical path through the graph and thus reduces par-factor.
|
||||
|
||||
Sarkar's partitioner, and ours, chooses pairs of macro-tasks to merge such
|
||||
that the growth in critical path is minimized. Each candidate merge would
|
||||
result in a new node, which would have some local critical path. We choose
|
||||
the candidate that would produce the shortest local critical path. Repeat
|
||||
until par-factor falls to a target threshold. It's a greedy algorithm, and
|
||||
it's not guaranteed to produce the best partition (which Sarkar proves is
|
||||
NP-hard).
|
||||
|
||||
=head3 Estimating Logic Costs
|
||||
|
||||
To compute the cost of any given path through the graph, Verilator
|
||||
estimates an execution cost for each task. Each macro-task has an execution
|
||||
cost which is simply the sum of its tasks' costs. We assume that
|
||||
communication overhead and synchronization overhead are zero, so the cost
|
||||
of any given path through the graph is simply the sum of macro-task
|
||||
execution costs. Sarkar does almost the same thing, except that he has
|
||||
nonzero estimates for synchronization costs.
|
||||
|
||||
Verilator's cost estimates are assigned by the InstrCountCostVisitor. This
|
||||
class is perhaps the most fragile piece of the multithread implementation.
|
||||
It's easy to have a bug where you count something cheap (eg. accessing one
|
||||
element of a huge array) as if it were expensive (eg. by counting it as if
|
||||
it were an access to the entire array.) Even without such gross bugs, the
|
||||
estimates this produce are only loosely predictive of actual runtime cost.
|
||||
Multithread performance would be better with better runtime costs
|
||||
estimates. This is an area to improve.
|
||||
|
||||
=head3 Scheduling Macro-Tasks at Runtime
|
||||
|
||||
After coarsening the graph, we must schedule the macro-tasks for runtime.
|
||||
Sarkar describes two options: you can dynamically schedule tasks at
|
||||
runtime, with a runtime graph follower. Sarkar calls this the
|
||||
"macro-dataflow model." Verilator does not support this; early experiments
|
||||
with this approach had poor performance.
|
||||
|
||||
The other option is to statically assign macro-tasks to threads, with each
|
||||
thread running its macro-tasks in a static order. Sarkar describes this in
|
||||
Chapter 5. Verilator takes this static approach. The only dynamic aspect is
|
||||
that each macro task may block before starting, to wait until its
|
||||
prerequisites on other threads have finished.
|
||||
|
||||
The synchronization cost is cheap if the prereqs are done. If they're not,
|
||||
fragmentation (idle CPU cores waiting) is possible. This is the major
|
||||
source of overhead in this approach. The --prof-threads switch and the
|
||||
C<verilator_gantt> script can visualize the time lost to such
|
||||
fragmentation.
|
||||
|
||||
=head3 Locating Variables for Best Spatial Locality
|
||||
|
||||
After scheduling all code, we attempt to locate variables in memory such
|
||||
that variables accessed by a single macro-task are close together in
|
||||
memory. This provides "spatial locality" -- when we pull in a 64-byte
|
||||
cache line to access a 2-byte variable, we want the other 62 bytes to be
|
||||
ones we'll also likely access soon, for best cache performance.
|
||||
|
||||
This turns out to be critical for performance. It should allow Verilator
|
||||
to scale to very large models. We don't rely on our working set fitting
|
||||
in any CPU cache; instead we essentially "stream" data into caches from
|
||||
memory. It's not literally streaming, where the address increases
|
||||
monotonically, but it should have similar performance characteristics,
|
||||
so long as each macro-task's dataset fits in one core's local caches.
|
||||
|
||||
To achieve spatial locality, we tag each variable with the set of
|
||||
macro-tasks that access it. Let's call this set the "footprint" of that
|
||||
variable. The variables in a given module have a set of footprints. We can
|
||||
order those footprints to minimize the distance between them (distance is
|
||||
the number of macro-tasks that are different across any two footprints) and
|
||||
then emit all variables into the struct in ordered-footprint order.
|
||||
|
||||
The footprint ordering is literally the traveling salesman problem, and we
|
||||
use a TSP-approximation algorithm to get close to an optimal sort.
|
||||
|
||||
This is an old idea. Simulators designed at DEC in the early 1990s used
|
||||
similar techniques to optimize both single-thread and multi-thread modes.
|
||||
(Verilator does not optimize variable placement for spatial locality in
|
||||
serial mode; that is a possible area for improvement.)
|
||||
|
||||
=head3 Improving Multithreaded Performance Further (a TODO list)
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<Wave Scheduling>
|
||||
|
||||
To allow the verilated model to run in parallel with the testbench, it
|
||||
might be nice to support "wave" scheduling, in which work on a cycle begins
|
||||
before eval() is called or continues after eval() returns. For now all
|
||||
work on a cycle happens during the eval() call, leaving Verilator's threads
|
||||
idle while the testbench (everything outside eval()) is working. This would
|
||||
involve fundamental changes within the partitioner, however, it's probably
|
||||
the best bet for hiding testbench latency.
|
||||
|
||||
=item C<Efficient Dynamic Scheduling>
|
||||
|
||||
To scale to more than a few threads, we may revisit a fully dynamic
|
||||
scheduler. For large (>16 core) systems it might make sense to dedicate an
|
||||
entire core to scheduling, so that scheduler data structures would fit in
|
||||
its L1 cache and thus the cost of traversing priority-ordered ready lists
|
||||
would not be prohibitive.
|
||||
|
||||
=item C<Static Scheduling with Runtime Repack>
|
||||
|
||||
We could modify the static scheduling approach by gathering actual
|
||||
macro-task execution times at run time, and dynamically re-packing the
|
||||
macro-tasks into the threads also at run time. Say, re-pack once every
|
||||
10,000 cycles or something. This has the potential to do better than our
|
||||
static estimates about macro-task run times. It could potentially react to
|
||||
CPU cores that aren't performing equally, due to NUMA or thermal throttling
|
||||
or nonuniform competing memory traffic or whatever.
|
||||
|
||||
=item C<Clock Domain Balancing>
|
||||
|
||||
Right now Verilator makes no attempt to balance clock domains across
|
||||
macro-tasks. For a multi-domain model, that could lead to bad gantt chart
|
||||
fragmentation. This could be improved if it's a real problem in practice.
|
||||
|
||||
=item C<Other Forms of MTask Balancing>
|
||||
|
||||
The largest source of runtime overhead is idle CPUs, which happens due to
|
||||
variance between our predicted runtime for each MTask and its actual
|
||||
runtime. That variance is magnified if MTasks are homogeneous, containing
|
||||
similar repeating logic which was generally close together in source code
|
||||
and which is still packed together even after going through Verilator's
|
||||
digestive tract.
|
||||
|
||||
If Verilator could avoid doing that, and instead would take source logic
|
||||
that was close together and distribute it across MTasks, that would
|
||||
increase the diversity of any given MTask, and this should reduce variance
|
||||
in the cost estimates.
|
||||
|
||||
One way to do that might be to make various "tie breaker" comparison
|
||||
routines in the sources to rely more heavily on randomness, and generally
|
||||
try harder not to keep input nodes together when we have the option to
|
||||
scramble things.
|
||||
|
||||
=item C<Performance Regression>
|
||||
|
||||
It would be nice if we had a regression of large designs, with some
|
||||
diversity of design styles, to test on both single- and multi-threaded
|
||||
modes. This would help to avoid performance regressions, and also to
|
||||
evaluate the optimizations while minimizing the impact of parasitic noise.
|
||||
|
||||
=item C<Per-Instance Classes>
|
||||
|
||||
If we have multiple instances of the same module, and they partition
|
||||
differently (likely; we make no attempt to partition them the same) then
|
||||
the variable sort will be suboptimal for either instance. A possible
|
||||
improvement would be to emit a unique class for each instance of a module,
|
||||
and sort its variables optimally for that instance's code stream.
|
||||
|
||||
=back
|
||||
|
||||
=head2 Verilated Flow
|
||||
|
||||
The evaluation loop outputted by Verilator is designed to allow a single
|
||||
@@ -268,8 +483,8 @@ technique lifted from graph traversal packages).
|
||||
A visitor first clears the one it wants to use by calling
|
||||
C<AstNode::user#ClearTree()>, then it can mark any node's user() with whatever
|
||||
data it wants. Readers just call C<< nodep->user() >>, but may need to cast
|
||||
appropriately, so you'll often see C<< nodep->userp()->castSOMETYPE() >>. At
|
||||
the top of each visitor are comments describing how the C<user()> stuff
|
||||
appropriately, so you'll often see C<< VN_CAST(nodep->userp(), SOMETYPE) >>.
|
||||
At the top of each visitor are comments describing how the C<user()> stuff
|
||||
applies to that visitor class. For example:
|
||||
|
||||
// NODE STATE
|
||||
@@ -299,16 +514,13 @@ as it slows the program down to have to pass vup everywhere.)
|
||||
|
||||
=head2 Iterators
|
||||
|
||||
C<AstNode> provides a set of iterators to facilitate walking over the
|
||||
tree. Each takes two arguments, a visitor, C<v>, of type C<AstNVisitor> and
|
||||
an optional pointer user data, C<vup>, of type C<AstNUser*>. The second is
|
||||
one of the ways to pass parameters to visitors described in L</Visitor
|
||||
Functions>, but its use is now deprecated and should I<not> be used for new
|
||||
visitor classes.
|
||||
C<AstNVisitor> provides a set of iterators to facilitate walking over the
|
||||
tree. Each operates on the current C<AstNVisitor> class (as this) and takes
|
||||
an argument type C<AstNode*>.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<iterate()>
|
||||
=item C<iterate>
|
||||
|
||||
This just applies the C<accept> method of the C<AstNode> to the visitor
|
||||
function.
|
||||
@@ -349,9 +561,9 @@ to be aware that calling iteration may cause the children to change. For
|
||||
example:
|
||||
|
||||
// nodep->lhsp() is 0x1234000
|
||||
nodep->lhsp()->iterateAndNext(...); // and under covers nodep->lhsp() changes
|
||||
iterateAndNextNull(nodep->lhsp()); // and under covers nodep->lhsp() changes
|
||||
// nodep->lhsp() is 0x5678400
|
||||
nodep->lhsp()->iterateAndNext(...);
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
|
||||
Will work fine, as even if the first iterate causes a new node to take the
|
||||
place of the lhsp(), that edit will update nodep->lhsp() and the second
|
||||
@@ -359,9 +571,9 @@ call will correctly see the change. Alternatively:
|
||||
|
||||
lp = nodep->lhsp();
|
||||
// nodep->lhsp() is 0x1234000, lp is 0x1234000
|
||||
lp->iterateAndNext(...); **lhsp=NULL;** // and under covers nodep->lhsp() changes
|
||||
iterateAndNextNull(lp); **lhsp=NULL;** // and under covers nodep->lhsp() changes
|
||||
// nodep->lhsp() is 0x5678400, lp is 0x1234000
|
||||
lp->iterateAndNext(...);
|
||||
iterateAndNextNull(lp);
|
||||
|
||||
This will cause bugs or a core dump, as lp is a dangling pointer. Thus it
|
||||
is advisable to set lhsp=NULL shown in the *'s above to make sure these
|
||||
@@ -370,7 +582,7 @@ V3Width is to use acceptSubtreeReturnEdits, which operates on a single node
|
||||
and returns the new pointer if any. Note acceptSubtreeReturnEdits does not
|
||||
follow nextp() links.
|
||||
|
||||
lp = lp->acceptSubtreeReturnEdits()
|
||||
lp = acceptSubtreeReturnEdits(lp)
|
||||
|
||||
=head2 Identifying derived classes
|
||||
|
||||
@@ -379,16 +591,16 @@ dealing with. For example a visitor might not implement separate C<visit>
|
||||
methods for C<AstIf> and C<AstGenIf>, but just a single method for the base
|
||||
class:
|
||||
|
||||
void visit (AstNodeIf* nodep, AstNUser* vup)
|
||||
void visit (AstNodeIf* nodep)
|
||||
|
||||
However that method might want to specify additional code if it is called
|
||||
for C<AstGenIf>. Verilator does this by providing a C<castSOMETYPE()>
|
||||
for C<AstGenIf>. Verilator does this by providing a C<VN_CAST>
|
||||
method for each possible node type, using C++ C<dynamic_cast>. This either
|
||||
returns a pointer to the object cast to that type (if it is of class
|
||||
C<SOMETYPE>, or a derived class of C<SOMETYPE>) or else NULL. So our
|
||||
C<visit> method could use:
|
||||
|
||||
if (nodep->castAstGenIf()) {
|
||||
if (VN_CAST(nodep, AstGenIf) {
|
||||
<code specific to AstGenIf>
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ if (! GetOptions (
|
||||
}
|
||||
|
||||
dotread ($opt_filename);
|
||||
cwrite ("graph_export.cpp");
|
||||
cwrite("graph_export.cpp");
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
Executable
+237
@@ -0,0 +1,237 @@
|
||||
#!/usr/bin/perl -w
|
||||
# See copyright, etc in below POD section.
|
||||
######################################################################
|
||||
|
||||
use Getopt::Long;
|
||||
#use Data::Dumper; $Data::Dumper::Indent=1; $Data::Dumper::Sortkeys=1; #Debug
|
||||
use IO::File;
|
||||
use IO::Dir;
|
||||
use Pod::Usage;
|
||||
use strict;
|
||||
use vars qw ($Debug);
|
||||
|
||||
our $VERSION = '0.001';
|
||||
our $Opt_Widen = 1;
|
||||
|
||||
#======================================================================
|
||||
# main
|
||||
|
||||
autoflush STDOUT 1;
|
||||
autoflush STDERR 1;
|
||||
Getopt::Long::config ("no_auto_abbrev");
|
||||
if (! GetOptions (
|
||||
"debug" => sub { $Debug = 1; },
|
||||
"help" => sub { print "Version $VERSION\n";
|
||||
pod2usage(-verbose=>2, -exitval => 2, output=>\*STDOUT, -noperldoc=>1); },
|
||||
"narrow!" => sub { $Opt_Widen = 0; },
|
||||
"version" => sub { print "Version $VERSION\n"; exit(0); },
|
||||
"widen!" => sub { $Opt_Widen = 1; }, # Default, undocumented
|
||||
"<>" => sub { die "%Error: Unknown parameter: $_[0]\n"; },
|
||||
)) {
|
||||
die "%Error: Bad usage, try 'git_untabify --help'\n";
|
||||
}
|
||||
|
||||
read_patch();
|
||||
|
||||
#######################################################################
|
||||
|
||||
sub read_patch {
|
||||
my $filename = undef;
|
||||
my $lineno = 0;
|
||||
my $hunk = 0;
|
||||
my $editlines = {};
|
||||
while (defined(my $line = <STDIN>)) {
|
||||
chomp $line;
|
||||
if ($line =~ m!^\+\+\+ b/(.*)!) {
|
||||
find_edits($editlines);
|
||||
edit_file($filename, $editlines);
|
||||
$filename = $1;
|
||||
$lineno = 0;
|
||||
$editlines = {};
|
||||
print "FILE $filename\n" if $Debug;
|
||||
}
|
||||
elsif ($line =~ m!^@@ -?[0-9]+,?[0-9]* \+?([0-9]+)!) {
|
||||
$lineno = $1 - 1;
|
||||
++$hunk;
|
||||
print " LINE $1 $line" if $Debug;
|
||||
}
|
||||
elsif ($line =~ m!^ !) {
|
||||
++$lineno;
|
||||
$editlines->{$lineno} = {line => $line,
|
||||
hunk => $hunk,
|
||||
user_edit => 0, };
|
||||
}
|
||||
elsif ($line =~ m!^\+!) {
|
||||
++$lineno;
|
||||
print " $lineno: $line" if $Debug;
|
||||
$editlines->{$lineno} = {line => $line,
|
||||
hunk => $hunk,
|
||||
user_edit => 1, };
|
||||
}
|
||||
}
|
||||
find_edits($editlines);
|
||||
edit_file($filename, $editlines);
|
||||
}
|
||||
|
||||
sub find_edits {
|
||||
my $editlines = shift;
|
||||
# Expand edit regions so if have edited line, non-edited line, edited
|
||||
# line, we will tab expand the middle ones.
|
||||
my %hunk_firstlines;
|
||||
foreach my $lineno (sort {$a <=> $b} keys %$editlines) {
|
||||
my $hunk = $editlines->{$lineno}{hunk};
|
||||
$hunk_firstlines{$hunk} ||= $lineno if $editlines->{$lineno}{user_edit};
|
||||
}
|
||||
my %hunk_lastlines;
|
||||
foreach my $lineno (sort {$b <=> $a} keys %$editlines) {
|
||||
my $hunk = $editlines->{$lineno}{hunk};
|
||||
$hunk_lastlines{$hunk} ||= $lineno if $editlines->{$lineno}{user_edit};
|
||||
}
|
||||
# Expand to always untabify at least 3 lines (so that future diff will
|
||||
# have non-tabs within a edit hunk distance
|
||||
foreach my $hunk (keys %hunk_firstlines) {
|
||||
if ($hunk_firstlines{$hunk} == $hunk_lastlines{$hunk}) {
|
||||
--$hunk_firstlines{$hunk};
|
||||
++$hunk_lastlines{$hunk};
|
||||
}
|
||||
elsif ($hunk_firstlines{$hunk}+1 == $hunk_lastlines{$hunk}) {
|
||||
++$hunk_lastlines{$hunk};
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $lineno (sort {$a <=> $b} keys %$editlines) {
|
||||
if ($editlines->{$lineno}{line} =~ /\t/) {
|
||||
my $hunk = $editlines->{$lineno}{hunk};
|
||||
if ($hunk_firstlines{$hunk} <= $lineno && $hunk_lastlines{$hunk} >= $lineno) {
|
||||
$editlines->{$lineno}{editit} = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub edit_file {
|
||||
my $filename = shift;
|
||||
my $editlines = shift;
|
||||
|
||||
my @editits;
|
||||
foreach my $lineno (sort {$a <=> $b} keys %$editlines) {
|
||||
push @editits, $lineno if $editlines->{$lineno}{editit};
|
||||
}
|
||||
|
||||
return if $#editits < 0;
|
||||
if (ignore($filename)) {
|
||||
print "%Warning: Ignoring $filename\n";
|
||||
return;
|
||||
}
|
||||
print "Edit $filename ",join(",",@editits),"\n";
|
||||
|
||||
my $lineno = 0;
|
||||
my @out;
|
||||
{
|
||||
my $fh = IO::File->new("<$filename") or die "%Error: $! $filename\n";
|
||||
while (defined(my $line = $fh->getline)) {
|
||||
++$lineno;
|
||||
if ($editlines->{$lineno}{editit}) {
|
||||
print $line;
|
||||
push @out, untabify($line);
|
||||
} else {
|
||||
push @out, $line;
|
||||
}
|
||||
}
|
||||
$fh->close;
|
||||
}
|
||||
{
|
||||
my $fh = IO::File->new(">${filename}.untab") or die "%Error: $! ${filename}.untab,";
|
||||
$fh->print(join('',@out));
|
||||
$fh->close;
|
||||
|
||||
my ($dev,$ino,$mode) = stat($filename);
|
||||
chmod $mode, "${filename}.untab";
|
||||
}
|
||||
|
||||
rename("${filename}.untab", $filename) or die "%Error: $! ${filename}.untab,";
|
||||
}
|
||||
|
||||
sub ignore {
|
||||
my $filename = shift;
|
||||
return 1 if ($filename =~ /(Makefile|\.mk)/);
|
||||
return 1 if ($filename =~ /\.(y|l|out|vcd)$/);
|
||||
return 1 if ($filename =~ /gtkwave/);
|
||||
#
|
||||
return 0 if ($filename =~ /\.(sv|v|vh|svh|h|vc|cpp|pl)$/);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub untabify {
|
||||
my $line = shift;
|
||||
my $out = "";
|
||||
my $col = 0;
|
||||
foreach my $c (split //, $line) {
|
||||
if ($c eq "\t") {
|
||||
my $destcol = int(($col+8)/8)*8;
|
||||
while ($col < $destcol) { ++$col; $out .= " "; }
|
||||
} else {
|
||||
$out .= $c;
|
||||
$col++;
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
git_untabify - Pipe a git diff report and untabify differences
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
git diff a..b | git_untabify
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Take a patch file, and edit the files in the destination patch list to
|
||||
untabify the related patch lines.
|
||||
|
||||
=head1 ARGUMENTS
|
||||
|
||||
=over 4
|
||||
|
||||
=item --help
|
||||
|
||||
Displays this message and program version and exits.
|
||||
|
||||
=item --narrow
|
||||
|
||||
Only edit lines which the user edited.
|
||||
|
||||
If not provided, also edit other lines within at least a 3 line area around
|
||||
the edit, and between any edits within the same hunk.
|
||||
|
||||
=item --version
|
||||
|
||||
Displays program version and exits.
|
||||
|
||||
=back
|
||||
|
||||
=head1 DISTRIBUTION
|
||||
|
||||
Copyright 2005-2018 by Wilson Snyder. Verilator is free software; you can
|
||||
redistribute it and/or modify it under the terms of either the GNU Lesser
|
||||
General Public License Version 3 or the Perl Artistic License Version 2.0.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Wilson Snyder <[email protected]>
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
=cut
|
||||
|
||||
######################################################################
|
||||
### Local Variables:
|
||||
### compile-command: "./git_untabify "
|
||||
### End:
|
||||
+11
-6
@@ -36,7 +36,6 @@ sub test {
|
||||
-r "nodist/install_test" or die "%Error: Run from the top of the verilator kit,";
|
||||
|
||||
cleanenv();
|
||||
$ENV{VERILATOR_NO_OPT_BUILD} = 1; # Don't build optimized executables; just slows this down
|
||||
run("make distclean") if -r "Makefile";
|
||||
|
||||
# Try building from a scratch area
|
||||
@@ -59,11 +58,12 @@ sub test {
|
||||
run("/bin/mkdir -p $prefix");
|
||||
run("cd $blddir && make install");
|
||||
run("test -e $prefix/share/man/man1/verilator.1");
|
||||
run("test -e $prefix/share/verilator/examples/test_c/Makefile");
|
||||
run("test -e $prefix/share/verilator/examples/tracing_c/Makefile");
|
||||
run("test -e $prefix/share/verilator/include/verilated.h");
|
||||
run("test -e $prefix/bin/verilator");
|
||||
run("test -e $prefix/bin/verilator_bin");
|
||||
run("test -e $prefix/bin/verilator_bin_dbg");
|
||||
run("test -e $prefix/bin/verilator_gantt");
|
||||
run("test -e $prefix/bin/verilator_profcfunc");
|
||||
}
|
||||
|
||||
@@ -92,6 +92,10 @@ sub test {
|
||||
run("cd $dir/obj_dir && make -f Vfoo.mk");
|
||||
run("cd $dir/obj_dir && ./Vfoo");
|
||||
}
|
||||
|
||||
if ($Opt_Stage <= 9) {
|
||||
print "*-* All Finished *-*\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub write_verilog {
|
||||
@@ -100,14 +104,14 @@ sub write_verilog {
|
||||
my $fh = IO::File->new(">$dir/foo.cpp");
|
||||
$fh->print('#include "Vfoo.h"' ,"\n");
|
||||
$fh->print('unsigned int main_time = 0;' ,"\n");
|
||||
$fh->print('double sc_time_stamp () {' ,"\n");
|
||||
$fh->print('double sc_time_stamp() {' ,"\n");
|
||||
$fh->print(' return main_time;' ,"\n");
|
||||
$fh->print('}' ,"\n");
|
||||
$fh->print('int main() {' ,"\n");
|
||||
$fh->print(' Vfoo *top = new Vfoo;' ,"\n");
|
||||
$fh->print(' while (!Verilated::gotFinish()) {',"\n");
|
||||
$fh->print(' top->eval();' ,"\n");
|
||||
$fh->print(' main_time++;' ,"\n");
|
||||
$fh->print(' top->eval();' ,"\n");
|
||||
$fh->print(' main_time++;' ,"\n");
|
||||
$fh->print(' }' ,"\n");
|
||||
$fh->print(' top->final();' ,"\n");
|
||||
$fh->print('}' ,"\n");
|
||||
@@ -116,7 +120,8 @@ sub write_verilog {
|
||||
sub cleanenv {
|
||||
foreach my $var (keys %ENV) {
|
||||
if ($var eq "VERILATOR_ROOT"
|
||||
|| $var eq "VERILATOR_INCLUDE") {
|
||||
|| $var eq "VERILATOR_INCLUDE"
|
||||
|| $var eq "VERILATOR_NO_OPT_BUILD") {
|
||||
print "unset $var # Was '$ENV{$var}'\n";
|
||||
delete $ENV{$var}
|
||||
}
|
||||
|
||||
+15
-12
@@ -22,9 +22,10 @@
|
||||
#### Start of system configuration section. ####
|
||||
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
PERL = @PERL@
|
||||
EXEEXT = @EXEEXT@
|
||||
# VPATH only does sources; fix install_test's not creating ../bin
|
||||
vpath %.h @srcdir@
|
||||
|
||||
#### End of system configuration section. ####
|
||||
|
||||
@@ -42,9 +43,11 @@ UNDER_GIT = $(wildcard ${srcdir}/../.git/logs/HEAD)
|
||||
#*********************************************************************
|
||||
|
||||
obj_opt:
|
||||
mkdir $@
|
||||
mkdir -p $@
|
||||
obj_dbg:
|
||||
mkdir $@
|
||||
mkdir -p $@
|
||||
../bin:
|
||||
mkdir -p $@
|
||||
|
||||
.SUFFIXES:
|
||||
|
||||
@@ -56,19 +59,19 @@ ifeq ($(VERILATOR_NO_OPT_BUILD),1) # Faster laptop development... One build
|
||||
-rm -rf $@ $@.exe
|
||||
-cp -p $<$(EXEEXT) $@$(EXEEXT)
|
||||
else
|
||||
../bin/verilator_bin: obj_opt prefiles
|
||||
cd obj_opt && $(MAKE) -j 1 TGT=../$@ -f ../Makefile_obj serial
|
||||
cd obj_opt && $(MAKE) TGT=../$@ -f ../Makefile_obj
|
||||
../bin/verilator_bin: obj_opt ../bin prefiles
|
||||
$(MAKE) -C obj_opt -j 1 TGT=../$@ -f ../Makefile_obj serial
|
||||
$(MAKE) -C obj_opt TGT=../$@ -f ../Makefile_obj
|
||||
endif
|
||||
|
||||
dbg: ../bin/verilator_bin_dbg ../bin/verilator_coverage_bin_dbg
|
||||
../bin/verilator_bin_dbg: obj_dbg prefiles
|
||||
cd obj_dbg && $(MAKE) -j 1 TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj serial
|
||||
cd obj_dbg && $(MAKE) TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj
|
||||
../bin/verilator_bin_dbg: obj_dbg ../bin prefiles
|
||||
$(MAKE) -C obj_dbg -j 1 TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj serial
|
||||
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj
|
||||
|
||||
../bin/verilator_coverage_bin_dbg: obj_dbg prefiles
|
||||
cd obj_dbg && $(MAKE) TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj serial_vlcov
|
||||
cd obj_dbg && $(MAKE) TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj
|
||||
../bin/verilator_coverage_bin_dbg: obj_dbg ../bin prefiles
|
||||
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj serial_vlcov
|
||||
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj
|
||||
|
||||
prefiles::
|
||||
prefiles:: config_rev.h
|
||||
|
||||
+8
-2
@@ -78,7 +78,7 @@ ifeq ($(VL_DEBUG),)
|
||||
COPT = -O2
|
||||
else
|
||||
# Debug
|
||||
COPT = -ggdb -DVL_DEBUG
|
||||
COPT = -ggdb -DVL_DEBUG -D_GLIBCXX_DEBUG
|
||||
# Debug & Profile:
|
||||
#LDFLAGS += -pg -g
|
||||
#COPT = -ggdb -pg -g
|
||||
@@ -93,7 +93,7 @@ endif
|
||||
LIBS = -lm -lstdc++
|
||||
|
||||
CPPFLAGS += -MMD
|
||||
CPPFLAGS += -I. -I$(bldsrc) -I$(srcdir) -I$(incdir)
|
||||
CPPFLAGS += -I. -I$(bldsrc) -I$(srcdir) -I$(incdir) -I../../include
|
||||
CPPFLAGS += -DYYDEBUG # Required to get nice error messages
|
||||
#CPPFLAGS += -DVL_LEAK_CHECKS # If running valgrind or other hunting tool
|
||||
CPPFLAGS += $(COPT)
|
||||
@@ -195,10 +195,12 @@ RAW_OBJS = \
|
||||
V3GraphAlg.o \
|
||||
V3GraphAcyc.o \
|
||||
V3GraphDfa.o \
|
||||
V3GraphPathChecker.o \
|
||||
V3GraphTest.o \
|
||||
V3Hashed.o \
|
||||
V3Inline.o \
|
||||
V3Inst.o \
|
||||
V3InstrCount.o \
|
||||
V3Life.o \
|
||||
V3LifePost.o \
|
||||
V3LinkCells.o \
|
||||
@@ -215,9 +217,12 @@ RAW_OBJS = \
|
||||
V3Order.o \
|
||||
V3Os.o \
|
||||
V3Param.o \
|
||||
V3Partition.o \
|
||||
V3PreShell.o \
|
||||
V3Premit.o \
|
||||
V3Reloop.o \
|
||||
V3Scope.o \
|
||||
V3Scoreboard.o \
|
||||
V3Slice.o \
|
||||
V3Split.o \
|
||||
V3SplitAs.o \
|
||||
@@ -230,6 +235,7 @@ RAW_OBJS = \
|
||||
V3Trace.o \
|
||||
V3TraceDecl.o \
|
||||
V3Tristate.o \
|
||||
V3TSP.o \
|
||||
V3Undriven.o \
|
||||
V3Unknown.o \
|
||||
V3Unroll.o \
|
||||
|
||||
+37
-45
@@ -32,18 +32,15 @@
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <unistd.h>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Active.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3Const.h"
|
||||
#include "V3SenTree.h" // for SenTreeSet
|
||||
|
||||
#include VL_INCLUDE_UNORDERED_MAP
|
||||
|
||||
//***** See below for main transformation engine
|
||||
|
||||
@@ -52,11 +49,7 @@
|
||||
|
||||
class ActiveBaseVisitor : public AstNVisitor {
|
||||
protected:
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
};
|
||||
|
||||
class ActiveNamer : public ActiveBaseVisitor {
|
||||
@@ -66,7 +59,11 @@ private:
|
||||
AstScope* m_scopep; // Current scope to add statement to
|
||||
AstActive* m_iActivep; // For current scope, the IActive we're building
|
||||
AstActive* m_cActivep; // For current scope, the SActive(combo) we're building
|
||||
vector<AstActive*> m_activeVec; // List of sensitive actives, for folding
|
||||
|
||||
SenTreeSet m_activeSens; // Sen lists for each active we've made
|
||||
typedef vl_unordered_map<AstSenTree*, AstActive*> ActiveMap;
|
||||
ActiveMap m_activeMap; // Map sentree to active, for folding.
|
||||
|
||||
// METHODS
|
||||
void addActive(AstActive* nodep) {
|
||||
if (!m_scopep) nodep->v3fatalSrc("NULL scope");
|
||||
@@ -77,8 +74,9 @@ private:
|
||||
m_scopep = nodep;
|
||||
m_iActivep = NULL;
|
||||
m_cActivep = NULL;
|
||||
m_activeVec.clear();
|
||||
nodep->iterateChildren(*this);
|
||||
m_activeSens.clear();
|
||||
m_activeMap.clear();
|
||||
iterateChildren(nodep);
|
||||
// Don't clear scopep, the namer persists beyond this visit
|
||||
}
|
||||
virtual void visit(AstSenTree* nodep) {
|
||||
@@ -91,7 +89,7 @@ private:
|
||||
// Default
|
||||
virtual void visit(AstNode* nodep) {
|
||||
// Default: Just iterate
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
// METHODS
|
||||
public:
|
||||
@@ -116,22 +114,15 @@ public:
|
||||
}
|
||||
AstActive* getActive(FileLine* fl, AstSenTree* sensesp) {
|
||||
// Return a sentree in this scope that matches given sense list.
|
||||
// Not the fastest, but scopes tend to have few clocks
|
||||
|
||||
AstActive* activep = NULL;
|
||||
//sitemsp->dumpTree(cout," Lookingfor: ");
|
||||
for (vector<AstActive*>::iterator it = m_activeVec.begin(); it!=m_activeVec.end(); ++it) {
|
||||
activep = *it;
|
||||
if (activep) { // Not deleted
|
||||
// Compare the list
|
||||
AstSenTree* asenp = activep->sensesp();
|
||||
if (asenp->sameTree(sensesp)) {
|
||||
UINFO(8," Found ACTIVE "<<activep<<endl);
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
activep = NULL;
|
||||
}
|
||||
found:
|
||||
AstSenTree* activeSenp = m_activeSens.find(sensesp);
|
||||
if (activeSenp) {
|
||||
ActiveMap::iterator it = m_activeMap.find(activeSenp);
|
||||
UASSERT(it != m_activeMap.end(), "Corrupt active map");
|
||||
activep = it->second;
|
||||
}
|
||||
|
||||
// Not found, form a new one
|
||||
if (!activep) {
|
||||
AstSenTree* newsenp = sensesp->cloneTree(false);
|
||||
@@ -140,7 +131,8 @@ public:
|
||||
UINFO(8," New ACTIVE "<<activep<<endl);
|
||||
// Form the sensitivity list
|
||||
addActive(activep);
|
||||
m_activeVec.push_back(activep);
|
||||
m_activeMap[newsenp] = activep;
|
||||
m_activeSens.add(newsenp);
|
||||
// Note actives may have also been added above in the Active visitor
|
||||
}
|
||||
return activep;
|
||||
@@ -154,7 +146,7 @@ public:
|
||||
}
|
||||
virtual ~ActiveNamer() {}
|
||||
void main(AstScope* nodep) {
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -182,9 +174,9 @@ private:
|
||||
nodep->v3warn(COMBDLY, "Delayed assignments (<=) in non-clocked"
|
||||
" (non flop or latch) block; suggest blocking assignments (=).");
|
||||
}
|
||||
AstNode* newp = new AstAssign (nodep->fileline(),
|
||||
nodep->lhsp()->unlinkFrBack(),
|
||||
nodep->rhsp()->unlinkFrBack());
|
||||
AstNode* newp = new AstAssign(nodep->fileline(),
|
||||
nodep->lhsp()->unlinkFrBack(),
|
||||
nodep->rhsp()->unlinkFrBack());
|
||||
nodep->replaceWith(newp);
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
@@ -193,7 +185,7 @@ private:
|
||||
if (m_check == CT_SEQ) {
|
||||
AstNode* las = m_assignp;
|
||||
m_assignp = nodep;
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
m_assignp = las;
|
||||
}
|
||||
}
|
||||
@@ -217,7 +209,7 @@ private:
|
||||
}
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
@@ -225,7 +217,7 @@ public:
|
||||
m_alwaysp = nodep;
|
||||
m_check = check;
|
||||
m_assignp = NULL;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~ActiveDlyVisitor() {}
|
||||
};
|
||||
@@ -252,7 +244,7 @@ private:
|
||||
// Clear last scope's names, and collect this scope's existing names
|
||||
m_namer.main(nodep);
|
||||
m_scopeFinalp = NULL;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstActive* nodep) {
|
||||
// Actives are being formed, so we can ignore any already made
|
||||
@@ -314,8 +306,8 @@ private:
|
||||
// Move always to appropriate ACTIVE based on its sense list
|
||||
if (oldsensesp
|
||||
&& oldsensesp->sensesp()
|
||||
&& oldsensesp->sensesp()->castSenItem()
|
||||
&& oldsensesp->sensesp()->castSenItem()->isNever()) {
|
||||
&& VN_IS(oldsensesp->sensesp(), SenItem)
|
||||
&& VN_CAST(oldsensesp->sensesp(), SenItem)->isNever()) {
|
||||
// Never executing. Kill it.
|
||||
if (oldsensesp->sensesp()->nextp()) nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated.");
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
@@ -325,7 +317,7 @@ private:
|
||||
// Read sensitivitues
|
||||
m_itemCombo = false;
|
||||
m_itemSequent = false;
|
||||
if (oldsensesp) oldsensesp->iterateAndNext(*this);
|
||||
iterateAndNextNull(oldsensesp);
|
||||
bool combo = m_itemCombo;
|
||||
bool sequent = m_itemSequent;
|
||||
|
||||
@@ -399,7 +391,7 @@ private:
|
||||
&& subitemp->edgeType() != AstEdgeType::ET_NEGEDGE) {
|
||||
nodep->v3fatalSrc("Strange activity type under SenGate");
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstSenItem* nodep) {
|
||||
if (nodep->edgeType() == AstEdgeType::ET_ANYEDGE) {
|
||||
@@ -421,7 +413,7 @@ private:
|
||||
virtual void visit(AstVarScope* nodep) {}
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
@@ -429,7 +421,7 @@ public:
|
||||
m_scopeFinalp = NULL;
|
||||
m_itemCombo = false;
|
||||
m_itemSequent = false;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~ActiveVisitor() {}
|
||||
};
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
|
||||
#ifndef _V3ACTIVE_H_
|
||||
#define _V3ACTIVE_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
|
||||
+10
-19
@@ -29,11 +29,6 @@
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <unistd.h>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3ActiveTop.h"
|
||||
@@ -58,24 +53,20 @@ private:
|
||||
SenTreeFinder m_finder; // Find global sentree's and add them
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstTopScope* nodep) {
|
||||
m_topscopep = nodep;
|
||||
m_finder.main(m_topscopep);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_topscopep = NULL;
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
// Create required actives and add to module
|
||||
// We can start ordering at a module, or a scope
|
||||
UINFO(4," MOD "<<nodep<<endl);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstActive* nodep) {
|
||||
UINFO(4," ACTIVE "<<nodep<<endl);
|
||||
@@ -83,8 +74,8 @@ private:
|
||||
AstSenTree* sensesp = nodep->sensesp();
|
||||
if (!sensesp) nodep->v3fatalSrc("NULL");
|
||||
if (sensesp->sensesp()
|
||||
&& sensesp->sensesp()->castSenItem()
|
||||
&& sensesp->sensesp()->castSenItem()->isNever()) {
|
||||
&& VN_IS(sensesp->sensesp(), SenItem)
|
||||
&& VN_CAST(sensesp->sensesp(), SenItem)->isNever()) {
|
||||
// Never executing. Kill it.
|
||||
if (sensesp->sensesp()->nextp()) nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated.");
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
@@ -93,8 +84,8 @@ private:
|
||||
// Copy combo tree to settlement tree with duplicated statements
|
||||
if (sensesp->hasCombo()) {
|
||||
AstSenTree* newsentreep
|
||||
= new AstSenTree (nodep->fileline(),
|
||||
new AstSenItem (nodep->fileline(), AstSenItem::Settle()));
|
||||
= new AstSenTree(nodep->fileline(),
|
||||
new AstSenItem (nodep->fileline(), AstSenItem::Settle()));
|
||||
AstActive* newp = new AstActive(nodep->fileline(),"settle", newsentreep);
|
||||
newp->sensesStorep(newsentreep);
|
||||
if (nodep->stmtsp()) newp->addStmtsp(nodep->stmtsp()->cloneTree(true));
|
||||
@@ -118,7 +109,7 @@ private:
|
||||
nodep->sensesp(wantp);
|
||||
}
|
||||
// No need to do statements under it, they're already moved.
|
||||
//nodep->iterateChildren(*this);
|
||||
//iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstInitial* nodep) {
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
@@ -143,13 +134,13 @@ private:
|
||||
virtual void visit(AstVarScope* nodep) {}
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit ActiveTopVisitor(AstNetlist* nodep) {
|
||||
m_topscopep = NULL;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~ActiveTopVisitor() {}
|
||||
};
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
|
||||
#ifndef _V3ACTIVETOP_H_
|
||||
#define _V3ACTIVETOP_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
|
||||
+85
-49
@@ -20,11 +20,6 @@
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <unistd.h>
|
||||
#include <map>
|
||||
#include <iomanip>
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Assert.h"
|
||||
@@ -32,6 +27,9 @@
|
||||
#include "V3GraphDfa.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
#include <cstdarg>
|
||||
#include <iomanip>
|
||||
|
||||
//######################################################################
|
||||
// Assert class functions
|
||||
|
||||
@@ -45,6 +43,7 @@ private:
|
||||
// STATE
|
||||
AstNodeModule* m_modp; // Last module
|
||||
AstBegin* m_beginp; // Last begin
|
||||
unsigned m_modPastNum; // Module past numbering
|
||||
V3Double0 m_statAsCover; // Statistic tracking
|
||||
V3Double0 m_statAsPsl; // Statistic tracking
|
||||
V3Double0 m_statAsFull; // Statistic tracking
|
||||
@@ -76,24 +75,25 @@ private:
|
||||
AstNode* newIfAssertOn(AstNode* nodep) {
|
||||
// Add a internal if to check assertions are on.
|
||||
// Don't make this a AND term, as it's unlikely to need to test this.
|
||||
FileLine* fl = nodep->fileline();
|
||||
AstNode* newp
|
||||
= new AstIf (nodep->fileline(),
|
||||
// If assertions are off, have constant propagation rip them out later
|
||||
// This allows syntax errors and such to be detected normally.
|
||||
(v3Global.opt.assertOn()
|
||||
? (AstNode*)(new AstCMath(nodep->fileline(), "Verilated::assertOn()", 1))
|
||||
: (AstNode*)(new AstConst(nodep->fileline(), AstConst::LogicFalse()))),
|
||||
nodep, NULL);
|
||||
= new AstIf(fl,
|
||||
// If assertions are off, have constant propagation rip them out later
|
||||
// This allows syntax errors and such to be detected normally.
|
||||
(v3Global.opt.assertOn()
|
||||
? static_cast<AstNode*>(new AstCMath(fl, "Verilated::assertOn()", 1))
|
||||
: static_cast<AstNode*>(new AstConst(fl, AstConst::LogicFalse()))),
|
||||
nodep, NULL);
|
||||
newp->user1(true); // Don't assert/cover this if
|
||||
return newp;
|
||||
}
|
||||
|
||||
AstNode* newFireAssertUnchecked(AstNode* nodep, const string& message) {
|
||||
// Like newFireAssert() but omits the asserts-on check
|
||||
AstDisplay* dispp = new AstDisplay (nodep->fileline(), AstDisplayType::DT_ERROR, message, NULL, NULL);
|
||||
AstDisplay* dispp = new AstDisplay(nodep->fileline(), AstDisplayType::DT_ERROR, message, NULL, NULL);
|
||||
AstNode* bodysp = dispp;
|
||||
replaceDisplay(dispp, "%%Error"); // Convert to standard DISPLAY format
|
||||
bodysp->addNext(new AstStop (nodep->fileline()));
|
||||
bodysp->addNext(new AstStop(nodep->fileline()));
|
||||
return bodysp;
|
||||
}
|
||||
|
||||
@@ -112,13 +112,13 @@ private:
|
||||
AstNode* bodysp = NULL;
|
||||
bool selfDestruct = false;
|
||||
AstIf* ifp = NULL;
|
||||
if (AstPslCover* snodep = nodep->castPslCover()) {
|
||||
if (AstPslCover* snodep = VN_CAST(nodep, PslCover)) {
|
||||
++m_statAsCover;
|
||||
if (!v3Global.opt.coverageUser()) {
|
||||
selfDestruct = true;
|
||||
} else {
|
||||
// V3Coverage assigned us a bucket to increment.
|
||||
AstCoverInc* covincp = snodep->coverincp()->castCoverInc();
|
||||
AstCoverInc* covincp = VN_CAST(snodep->coverincp(), CoverInc);
|
||||
if (!covincp) snodep->v3fatalSrc("Missing AstCoverInc under assertion");
|
||||
covincp->unlinkFrBack();
|
||||
if (message!="") covincp->declp()->comment(message);
|
||||
@@ -126,10 +126,10 @@ private:
|
||||
}
|
||||
|
||||
if (bodysp && stmtsp) bodysp = bodysp->addNext(stmtsp);
|
||||
ifp = new AstIf (nodep->fileline(), propp, bodysp, NULL);
|
||||
ifp = new AstIf(nodep->fileline(), propp, bodysp, NULL);
|
||||
bodysp = ifp;
|
||||
|
||||
} else if (nodep->castPslAssert()) {
|
||||
} else if (VN_IS(nodep, PslAssert)) {
|
||||
++m_statAsPsl;
|
||||
// Insert an automatic error message and $stop after
|
||||
// any user-supplied statements.
|
||||
@@ -144,14 +144,16 @@ private:
|
||||
// than the sim-killing else clause:
|
||||
ifp->branchPred(AstBranchPred::BP_LIKELY);
|
||||
bodysp = newIfAssertOn(ifp);
|
||||
} else if (VN_IS(nodep, PslRestrict)) {
|
||||
// IEEE says simulator ignores these
|
||||
pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep);
|
||||
return;
|
||||
} else {
|
||||
nodep->v3fatalSrc("Unknown node type");
|
||||
}
|
||||
|
||||
AstNode* newp = new AstAlways (nodep->fileline(),
|
||||
VAlwaysKwd::ALWAYS,
|
||||
sentreep,
|
||||
bodysp);
|
||||
AstNode* newp = new AstAlways(nodep->fileline(),
|
||||
VAlwaysKwd::ALWAYS, sentreep, bodysp);
|
||||
// Install it
|
||||
if (selfDestruct) {
|
||||
// Delete it after making the tree. This way we can tell the user
|
||||
@@ -170,16 +172,16 @@ private:
|
||||
AstNode* passsp = nodep->passsp(); if (passsp) passsp->unlinkFrBackWithNext();
|
||||
AstNode* failsp = nodep->failsp(); if (failsp) failsp->unlinkFrBackWithNext();
|
||||
//
|
||||
if (nodep->castVAssert()) {
|
||||
if (VN_IS(nodep, VAssert)) {
|
||||
if (passsp) passsp = newIfAssertOn(passsp);
|
||||
if (failsp) failsp = newIfAssertOn(failsp);
|
||||
} else {
|
||||
nodep->v3fatalSrc("Unknown node type");
|
||||
}
|
||||
|
||||
AstIf* ifp = new AstIf (nodep->fileline(), propp, passsp, failsp);
|
||||
AstIf* ifp = new AstIf(nodep->fileline(), propp, passsp, failsp);
|
||||
AstNode* newp = ifp;
|
||||
if (nodep->castVAssert()) ifp->branchPred(AstBranchPred::BP_UNLIKELY);
|
||||
if (VN_IS(nodep, VAssert)) ifp->branchPred(AstBranchPred::BP_UNLIKELY);
|
||||
//
|
||||
// Install it
|
||||
nodep->replaceWith(newp);
|
||||
@@ -187,6 +189,7 @@ private:
|
||||
pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstIf* nodep) {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
if (nodep->uniquePragma() || nodep->unique0Pragma()) {
|
||||
@@ -197,14 +200,14 @@ private:
|
||||
// If this statement ends with 'else if', then nextIf will point to the
|
||||
// nextIf statement. Otherwise it will be null.
|
||||
AstNodeIf* nextifp = dynamic_cast<AstNodeIf*>(ifp->elsesp());
|
||||
ifp->condp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(ifp->condp());
|
||||
|
||||
// Recurse into the true case.
|
||||
ifp->ifsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(ifp->ifsp());
|
||||
|
||||
// If the last else is not an else if, recurse into that too.
|
||||
if (ifp->elsesp() && !nextifp) {
|
||||
ifp->elsesp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(ifp->elsesp());
|
||||
}
|
||||
|
||||
// Build a bitmask of the true predicates
|
||||
@@ -234,24 +237,24 @@ private:
|
||||
AstNode* ohot = ((allow_none || hasDefaultElse)
|
||||
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
|
||||
: static_cast<AstNode*>(new AstOneHot (nodep->fileline(), propp)));
|
||||
AstIf* checkifp = new AstIf (nodep->fileline(),
|
||||
new AstLogNot (nodep->fileline(), ohot),
|
||||
newFireAssert(nodep, "'unique if' statement violated"),
|
||||
newifp);
|
||||
AstIf* checkifp = new AstIf(nodep->fileline(),
|
||||
new AstLogNot(nodep->fileline(), ohot),
|
||||
newFireAssert(nodep, "'unique if' statement violated"),
|
||||
newifp);
|
||||
checkifp->branchPred(AstBranchPred::BP_UNLIKELY);
|
||||
nodep->replaceWith(checkifp);
|
||||
pushDeletep(nodep);
|
||||
} else {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS //========== Case assertions
|
||||
//========== Case assertions
|
||||
virtual void visit(AstCase* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->user1SetOnce()) {
|
||||
bool has_default=false;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
if (itemp->isDefault()) has_default=true;
|
||||
}
|
||||
if (nodep->fullPragma() || nodep->priorityPragma()) {
|
||||
@@ -270,7 +273,7 @@ private:
|
||||
// Not parallel, but harmlessly so.
|
||||
} else {
|
||||
AstNode* propp = NULL;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
|
||||
AstNode* onep;
|
||||
if (nodep->casex() || nodep->casez() || nodep->caseInside()) {
|
||||
@@ -293,10 +296,10 @@ private:
|
||||
AstNode* ohot = (allow_none
|
||||
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
|
||||
: static_cast<AstNode*>(new AstOneHot (nodep->fileline(), propp)));
|
||||
AstIf* ifp = new AstIf (nodep->fileline(),
|
||||
new AstLogNot (nodep->fileline(), ohot),
|
||||
newFireAssert(nodep, "synthesis parallel_case, but multiple matches found"),
|
||||
NULL);
|
||||
AstIf* ifp = new AstIf(nodep->fileline(),
|
||||
new AstLogNot(nodep->fileline(), ohot),
|
||||
newFireAssert(nodep, "synthesis parallel_case, but multiple matches found"),
|
||||
NULL);
|
||||
ifp->branchPred(AstBranchPred::BP_UNLIKELY);
|
||||
nodep->addNotParallelp(ifp);
|
||||
}
|
||||
@@ -304,9 +307,40 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS //========== Statements
|
||||
//========== Past
|
||||
virtual void visit(AstPast* nodep) {
|
||||
iterateChildren(nodep);
|
||||
uint32_t ticks = 1;
|
||||
if (nodep->ticksp()) {
|
||||
if (!VN_IS(nodep->ticksp(), Const)) nodep->v3fatalSrc("Expected constant ticks, checked in V3Width");
|
||||
ticks = VN_CAST(nodep->ticksp(), Const)->toUInt();
|
||||
}
|
||||
if (ticks<1) nodep->v3fatalSrc("0 tick should have been checked in V3Width");
|
||||
AstNode* inp = nodep->exprp()->unlinkFrBack();
|
||||
AstVar* invarp = NULL;
|
||||
AstSenTree* sentreep = nodep->sentreep(); sentreep->unlinkFrBack();
|
||||
AstAlways* alwaysp = new AstAlways(nodep->fileline(), VAlwaysKwd::ALWAYS,
|
||||
sentreep, NULL);
|
||||
m_modp->addStmtp(alwaysp);
|
||||
for (uint32_t i=0; i<ticks; ++i) {
|
||||
AstVar* outvarp = new AstVar(nodep->fileline(), AstVarType::MODULETEMP,
|
||||
"_Vpast_"+cvtToStr(m_modPastNum++)+"_"+cvtToStr(i),
|
||||
inp->dtypep());
|
||||
m_modp->addStmtp(outvarp);
|
||||
AstNode* assp = new AstAssignDly(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), outvarp, true),
|
||||
inp);
|
||||
alwaysp->addStmtp(assp);
|
||||
//if (debug()>-9) assp->dumpTree(cout, "-ass: ");
|
||||
invarp = outvarp;
|
||||
inp = new AstVarRef(nodep->fileline(), invarp, false);
|
||||
}
|
||||
nodep->replaceWith(inp);
|
||||
}
|
||||
|
||||
//========== Statements
|
||||
virtual void visit(AstDisplay* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Replace the special types with standard text
|
||||
if (nodep->displayType()==AstDisplayType::DT_INFO) {
|
||||
replaceDisplay(nodep, "-Info");
|
||||
@@ -319,21 +353,22 @@ private:
|
||||
}
|
||||
|
||||
virtual void visit(AstNodePslCoverOrAssert* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (m_beginp && nodep->name() == "") nodep->name(m_beginp->name());
|
||||
newPslAssertion(nodep, nodep->propp(), nodep->sentreep(),
|
||||
nodep->stmtsp(), nodep->name()); VL_DANGLING(nodep);
|
||||
}
|
||||
virtual void visit(AstVAssert* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
newVAssertion(nodep, nodep->propp()); VL_DANGLING(nodep);
|
||||
++m_statAsSV;
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
m_modp = nodep;
|
||||
m_modPastNum = 0;
|
||||
//
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Reset defaults
|
||||
m_modp = NULL;
|
||||
}
|
||||
@@ -343,21 +378,22 @@ private:
|
||||
AstBegin* lastp = m_beginp;
|
||||
{
|
||||
m_beginp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_beginp = lastp;
|
||||
}
|
||||
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit AssertVisitor(AstNetlist* nodep) {
|
||||
m_beginp = NULL;
|
||||
m_modp = NULL;
|
||||
m_modPastNum = 0;
|
||||
// Process
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~AssertVisitor() {
|
||||
V3Stats::addStat("Assertions, PSL asserts", m_statAsPsl);
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
|
||||
#ifndef _V3ASSERT_H_
|
||||
#define _V3ASSERT_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
|
||||
+34
-20
@@ -23,15 +23,12 @@
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <unistd.h>
|
||||
#include <map>
|
||||
#include <iomanip>
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3AssertPre.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
#include <cstdarg>
|
||||
#include <iomanip>
|
||||
|
||||
//######################################################################
|
||||
// Assert class functions
|
||||
@@ -44,22 +41,22 @@ private:
|
||||
// NODE STATE/TYPES
|
||||
// STATE
|
||||
// Reset each module:
|
||||
AstNodeSenItem* m_seniDefaultp; // Default sensitivity (from AstDefClock)
|
||||
AstNodeSenItem* m_seniDefaultp; // Default sensitivity (from AstDefClock)
|
||||
// Reset each assertion:
|
||||
AstNodeSenItem* m_senip; // Last sensitivity
|
||||
AstNodeSenItem* m_senip; // Last sensitivity
|
||||
// Reset each always:
|
||||
AstNodeSenItem* m_seniAlwaysp; // Last sensitivity in always
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
AstSenTree* newSenTree(AstNode* nodep) {
|
||||
// Create sentree based on clocked or default clock
|
||||
// Return NULL for always
|
||||
AstSenTree* newp = NULL;
|
||||
AstNodeSenItem* senip = m_senip ? m_senip : m_seniDefaultp;
|
||||
AstNodeSenItem* senip = m_senip;
|
||||
if (!senip) senip = m_seniDefaultp;
|
||||
if (!senip) senip = m_seniAlwaysp;
|
||||
if (!senip) {
|
||||
nodep->v3error("Unsupported: Unclocked assertion");
|
||||
newp = new AstSenTree(nodep->fileline(), NULL);
|
||||
@@ -72,7 +69,8 @@ private:
|
||||
m_senip = NULL;
|
||||
}
|
||||
|
||||
// VISITORS //========== Statements
|
||||
// VISITORS
|
||||
//========== Statements
|
||||
virtual void visit(AstClocking* nodep) {
|
||||
UINFO(8," CLOCKING"<<nodep<<endl);
|
||||
// Store the new default clock, reset on new module
|
||||
@@ -85,16 +83,31 @@ private:
|
||||
}
|
||||
pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
}
|
||||
virtual void visit(AstAlways* nodep) {
|
||||
iterateAndNextNull(nodep->sensesp());
|
||||
if (nodep->sensesp()) {
|
||||
m_seniAlwaysp = nodep->sensesp()->sensesp();
|
||||
}
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
m_seniAlwaysp = NULL;
|
||||
}
|
||||
|
||||
virtual void visit(AstNodePslCoverOrAssert* nodep) {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
clearAssertInfo();
|
||||
nodep->iterateChildren(*this);
|
||||
// Find PslClocking's burried under nodep->exprsp
|
||||
iterateChildren(nodep);
|
||||
nodep->sentreep(newSenTree(nodep));
|
||||
clearAssertInfo();
|
||||
}
|
||||
virtual void visit(AstPast* nodep) {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
iterateChildren(nodep);
|
||||
nodep->sentreep(newSenTree(nodep));
|
||||
}
|
||||
virtual void visit(AstPslClocked* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
// No need to iterate the body, once replace will get iterated
|
||||
iterateAndNextNull(nodep->sensesp());
|
||||
if (m_senip) {
|
||||
nodep->v3error("Unsupported: Only one PSL clock allowed per assertion");
|
||||
}
|
||||
@@ -112,21 +125,22 @@ private:
|
||||
pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Reset defaults
|
||||
m_seniDefaultp = NULL;
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit AssertPreVisitor(AstNetlist* nodep) {
|
||||
m_seniDefaultp = NULL;
|
||||
m_seniAlwaysp = NULL;
|
||||
clearAssertInfo();
|
||||
// Process
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~AssertPreVisitor() {}
|
||||
};
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
|
||||
#ifndef _V3ASSERTPRE_H_
|
||||
#define _V3ASSERTPRE_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
|
||||
+79
-86
@@ -18,11 +18,8 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3File.h"
|
||||
@@ -30,6 +27,10 @@
|
||||
#include "V3Broken.h"
|
||||
#include "V3String.h"
|
||||
|
||||
#include <cstdarg>
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
|
||||
//======================================================================
|
||||
// Statics
|
||||
|
||||
@@ -58,7 +59,7 @@ int AstNodeDType::s_uniqueNum = 0;
|
||||
//######################################################################
|
||||
// V3AstType
|
||||
|
||||
ostream& operator<<(ostream& os, AstType rhs);
|
||||
std::ostream& operator<<(std::ostream& os, AstType rhs);
|
||||
|
||||
//######################################################################
|
||||
// Creators
|
||||
@@ -158,7 +159,7 @@ string AstNode::vcdName(const string& namein) {
|
||||
while ((pos=pretty.find("__DOT__")) != string::npos) {
|
||||
pretty.replace(pos, 7, " ");
|
||||
}
|
||||
while ((pos=pretty.find(".")) != string::npos) {
|
||||
while ((pos = pretty.find('.')) != string::npos) {
|
||||
pretty.replace(pos, 1, " ");
|
||||
}
|
||||
// Now convert escaped special characters, etc
|
||||
@@ -230,7 +231,7 @@ inline void AstNode::debugTreeChange(const char* prefix, int lineno, bool next)
|
||||
// Only for use for those really nasty bugs relating to internals
|
||||
// Note this may be null.
|
||||
//if (debug()) cout<<"-treeChange: V3Ast.cpp:"<<lineno<<" Tree Change for "
|
||||
// <<prefix<<": "<<(void*)this<<" <e"<<AstNode::s_editCntGbl<<">"<<endl;
|
||||
// <<prefix<<": "<<cvtToHex(this)<<" <e"<<AstNode::s_editCntGbl<<">"<<endl;
|
||||
//if (debug()) {
|
||||
// cout<<"-treeChange: V3Ast.cpp:"<<lineno<<" Tree Change for "<<prefix<<endl;
|
||||
// // Commenting out the section below may crash, as the tree state
|
||||
@@ -290,9 +291,8 @@ void AstNode::addNextHere(AstNode* newp) {
|
||||
// Add to m_nextp on exact node passed, not at the end.
|
||||
// This could be at head, tail, or both (single)
|
||||
// New could be head of single node, or list
|
||||
UDEBUGONLY(UASSERT(dynamic_cast<AstNode*>(this),"this should not be NULL"););
|
||||
UASSERT(newp,"Null item passed to addNext");
|
||||
UASSERT(newp->backp()==NULL,"New node (back) already assigned?");
|
||||
UASSERT(!newp->backp(), "New node (back) already assigned?");
|
||||
this->debugTreeChange("-addHereThs: ", __LINE__, false);
|
||||
newp->debugTreeChange("-addHereNew: ", __LINE__, true);
|
||||
newp->editCountInc();
|
||||
@@ -335,7 +335,7 @@ void AstNode::addNextHere(AstNode* newp) {
|
||||
}
|
||||
|
||||
void AstNode::setOp1p(AstNode* newp) {
|
||||
UASSERT(newp,"Null item passed to setOp1p\n");
|
||||
UASSERT(newp, "Null item passed to setOp1p");
|
||||
UDEBUGONLY(if (m_op1p) this->v3fatalSrc("Adding to non-empty, non-list op1"););
|
||||
UDEBUGONLY(if (newp->m_backp) newp->v3fatalSrc("Adding already linked node"););
|
||||
UDEBUGONLY(if (newp->m_nextp) newp->v3fatalSrc("Adding list to non-list op1"););
|
||||
@@ -348,7 +348,7 @@ void AstNode::setOp1p(AstNode* newp) {
|
||||
}
|
||||
|
||||
void AstNode::setOp2p(AstNode* newp) {
|
||||
UASSERT(newp,"Null item passed to setOp2p\n");
|
||||
UASSERT(newp, "Null item passed to setOp2p");
|
||||
UDEBUGONLY(if (m_op2p) this->v3fatalSrc("Adding to non-empty, non-list op2"););
|
||||
UDEBUGONLY(if (newp->m_backp) newp->v3fatalSrc("Adding already linked node"););
|
||||
UDEBUGONLY(if (newp->m_nextp) newp->v3fatalSrc("Adding list to non-list op2"););
|
||||
@@ -361,7 +361,7 @@ void AstNode::setOp2p(AstNode* newp) {
|
||||
}
|
||||
|
||||
void AstNode::setOp3p(AstNode* newp) {
|
||||
UASSERT(newp,"Null item passed to setOp3p\n");
|
||||
UASSERT(newp, "Null item passed to setOp3p");
|
||||
UDEBUGONLY(if (m_op3p) this->v3fatalSrc("Adding to non-empty, non-list op3"););
|
||||
UDEBUGONLY(if (newp->m_backp) newp->v3fatalSrc("Adding already linked node"););
|
||||
UDEBUGONLY(if (newp->m_nextp) newp->v3fatalSrc("Adding list to non-list op3"););
|
||||
@@ -374,7 +374,7 @@ void AstNode::setOp3p(AstNode* newp) {
|
||||
}
|
||||
|
||||
void AstNode::setOp4p(AstNode* newp) {
|
||||
UASSERT(newp,"Null item passed to setOp4p\n");
|
||||
UASSERT(newp, "Null item passed to setOp4p");
|
||||
UDEBUGONLY(if (m_op4p) this->v3fatalSrc("Adding to non-empty, non-list op4"););
|
||||
UDEBUGONLY(if (newp->m_backp) newp->v3fatalSrc("Adding already linked node"););
|
||||
UDEBUGONLY(if (newp->m_nextp) newp->v3fatalSrc("Adding list to non-list op4"););
|
||||
@@ -387,25 +387,25 @@ void AstNode::setOp4p(AstNode* newp) {
|
||||
}
|
||||
|
||||
void AstNode::addOp1p(AstNode* newp) {
|
||||
UASSERT(newp,"Null item passed to addOp1p\n");
|
||||
UASSERT(newp, "Null item passed to addOp1p");
|
||||
if (!m_op1p) { op1p(newp); }
|
||||
else { m_op1p->addNext(newp); }
|
||||
}
|
||||
|
||||
void AstNode::addOp2p(AstNode* newp) {
|
||||
UASSERT(newp,"Null item passed to addOp2p\n");
|
||||
UASSERT(newp, "Null item passed to addOp2p");
|
||||
if (!m_op2p) { op2p(newp); }
|
||||
else { m_op2p->addNext(newp); }
|
||||
}
|
||||
|
||||
void AstNode::addOp3p(AstNode* newp) {
|
||||
UASSERT(newp,"Null item passed to addOp3p\n");
|
||||
UASSERT(newp, "Null item passed to addOp3p");
|
||||
if (!m_op3p) { op3p(newp); }
|
||||
else { m_op3p->addNext(newp); }
|
||||
}
|
||||
|
||||
void AstNode::addOp4p(AstNode* newp) {
|
||||
UASSERT(newp,"Null item passed to addOp4p\n");
|
||||
UASSERT(newp, "Null item passed to addOp4p");
|
||||
if (!m_op4p) { op4p(newp); }
|
||||
else { m_op4p->addNext(newp); }
|
||||
}
|
||||
@@ -418,9 +418,9 @@ void AstNode::replaceWith(AstNode* newp) {
|
||||
repHandle.relink(newp);
|
||||
}
|
||||
|
||||
void AstNRelinker::dump(ostream& str) const {
|
||||
str<<" BK="<<(uint32_t*)m_backp;
|
||||
str<<" ITER="<<(uint32_t*)m_iterpp;
|
||||
void AstNRelinker::dump(std::ostream& str) const {
|
||||
str<<" BK="<<reinterpret_cast<uint32_t*>(m_backp);
|
||||
str<<" ITER="<<reinterpret_cast<uint32_t*>(m_iterpp);
|
||||
str<<" CHG="<<(m_chg==RELINK_NEXT?"[NEXT] ":"");
|
||||
str<<(m_chg==RELINK_OP1?"[OP1] ":"");
|
||||
str<<(m_chg==RELINK_OP2?"[OP2] ":"");
|
||||
@@ -431,7 +431,7 @@ void AstNRelinker::dump(ostream& str) const {
|
||||
AstNode* AstNode::unlinkFrBackWithNext(AstNRelinker* linkerp) {
|
||||
this->debugTreeChange("-unlinkWNextThs: ", __LINE__, true);
|
||||
AstNode* oldp = this;
|
||||
UASSERT(oldp->m_backp,"Node has no back, already unlinked?\n");
|
||||
UASSERT(oldp->m_backp, "Node has no back, already unlinked?");
|
||||
oldp->editCountInc();
|
||||
AstNode* backp = oldp->m_backp;
|
||||
if (linkerp) {
|
||||
@@ -479,7 +479,7 @@ AstNode* AstNode::unlinkFrBackWithNext(AstNRelinker* linkerp) {
|
||||
AstNode* AstNode::unlinkFrBack(AstNRelinker* linkerp) {
|
||||
this->debugTreeChange("-unlinkFrBkThs: ", __LINE__, true);
|
||||
AstNode* oldp = this;
|
||||
UASSERT(oldp->m_backp,"Node has no back, already unlinked?\n");
|
||||
UASSERT(oldp->m_backp, "Node has no back, already unlinked?");
|
||||
oldp->editCountInc();
|
||||
AstNode* backp = oldp->m_backp;
|
||||
if (linkerp) {
|
||||
@@ -531,8 +531,8 @@ AstNode* AstNode::unlinkFrBack(AstNRelinker* linkerp) {
|
||||
void AstNode::relink(AstNRelinker* linkerp) {
|
||||
if (debug()>8) { UINFO(0," EDIT: relink: "); dumpPtrs(); }
|
||||
AstNode* newp = this;
|
||||
UASSERT(linkerp && linkerp->m_backp, "Need non-empty linker\n");
|
||||
UASSERT(newp->backp()==NULL, "New node already linked?\n");
|
||||
UASSERT(linkerp && linkerp->m_backp, "Need non-empty linker");
|
||||
UASSERT(!newp->backp(), "New node already linked?");
|
||||
newp->editCountInc();
|
||||
|
||||
if (debug()>8) { linkerp->dump(cout); cout<<endl; }
|
||||
@@ -594,7 +594,7 @@ void AstNode::relinkOneLink(AstNode*& pointpr, // Ref to pointer that gets set
|
||||
pointpr = newp;
|
||||
}
|
||||
|
||||
void AstNode::addHereThisAsNext (AstNode* newp) {
|
||||
void AstNode::addHereThisAsNext(AstNode* newp) {
|
||||
// {old}->this->{next} becomes {old}->new->this->{next}
|
||||
AstNRelinker handle;
|
||||
this->unlinkFrBackWithNext(&handle);
|
||||
@@ -602,7 +602,7 @@ void AstNode::addHereThisAsNext (AstNode* newp) {
|
||||
handle.relink(newp);
|
||||
}
|
||||
|
||||
void AstNode::swapWith (AstNode* bp) {
|
||||
void AstNode::swapWith(AstNode* bp) {
|
||||
AstNRelinker aHandle;
|
||||
AstNRelinker bHandle;
|
||||
this->unlinkFrBack(&aHandle);
|
||||
@@ -646,7 +646,6 @@ AstNode* AstNode::cloneTreeIterList() {
|
||||
}
|
||||
|
||||
AstNode* AstNode::cloneTree(bool cloneNextLink) {
|
||||
if (!this) return NULL; // verilog.y relies on this
|
||||
this->debugTreeChange("-cloneThs: ", __LINE__, cloneNextLink);
|
||||
cloneClearTree();
|
||||
AstNode* newp;
|
||||
@@ -668,16 +667,16 @@ AstNode* AstNode::cloneTree(bool cloneNextLink) {
|
||||
|
||||
void AstNode::deleteNode() {
|
||||
// private: Delete single node. Publicly call deleteTree() instead.
|
||||
UASSERT(m_backp==NULL,"Delete called on node with backlink still set\n");
|
||||
UASSERT(!m_backp, "Delete called on node with backlink still set");
|
||||
editCountInc();
|
||||
// Change links of old node so we coredump if used
|
||||
this->m_nextp = (AstNode*)1;
|
||||
this->m_backp = (AstNode*)1;
|
||||
this->m_headtailp = (AstNode*)1;
|
||||
this->m_op1p = (AstNode*)1;
|
||||
this->m_op2p = (AstNode*)1;
|
||||
this->m_op3p = (AstNode*)1;
|
||||
this->m_op4p = (AstNode*)1;
|
||||
this->m_nextp = reinterpret_cast<AstNode*>(0x1);
|
||||
this->m_backp = reinterpret_cast<AstNode*>(0x1);
|
||||
this->m_headtailp = reinterpret_cast<AstNode*>(0x1);
|
||||
this->m_op1p = reinterpret_cast<AstNode*>(0x1);
|
||||
this->m_op2p = reinterpret_cast<AstNode*>(0x1);
|
||||
this->m_op3p = reinterpret_cast<AstNode*>(0x1);
|
||||
this->m_op4p = reinterpret_cast<AstNode*>(0x1);
|
||||
if (
|
||||
#if !defined(VL_DEBUG) || defined(VL_LEAK_CHECKS)
|
||||
1
|
||||
@@ -713,7 +712,7 @@ void AstNode::deleteTreeIter() {
|
||||
void AstNode::deleteTree() {
|
||||
// deleteTree always deletes the next link, because you must have called
|
||||
// unlinkFromBack or unlinkFromBackWithNext as appropriate before calling this.
|
||||
UASSERT(m_backp==NULL,"Delete called on node with backlink still set\n");
|
||||
UASSERT(!m_backp, "Delete called on node with backlink still set");
|
||||
this->debugTreeChange("-delTree: ", __LINE__, true);
|
||||
this->editCountInc();
|
||||
// MUST be depth first!
|
||||
@@ -789,8 +788,8 @@ void AstNode::iterateAndNext(AstNVisitor& v) {
|
||||
niterp->accept(v);
|
||||
// accept may do a replaceNode and change niterp on us...
|
||||
// niterp maybe NULL, so need cast if printing
|
||||
//if (niterp != nodep) UINFO(1,"iterateAndNext edited "<<(void*)nodep
|
||||
// <<" now into "<<(void*)niterp<<endl);
|
||||
//if (niterp != nodep) UINFO(1,"iterateAndNext edited "<<cvtToHex(nodep)
|
||||
// <<" now into "<<cvtToHex(niterp)<<endl);
|
||||
if (!niterp) return; // Perhaps node deleted inside accept
|
||||
niterp->m_iterpp = NULL;
|
||||
if (VL_UNLIKELY(niterp!=nodep)) { // Edited node inside accept
|
||||
@@ -802,7 +801,6 @@ void AstNode::iterateAndNext(AstNVisitor& v) {
|
||||
}
|
||||
|
||||
void AstNode::iterateListBackwards(AstNVisitor& v) {
|
||||
UDEBUGONLY(UASSERT(dynamic_cast<AstNode*>(this),"this should not be NULL"););
|
||||
AstNode* nodep=this;
|
||||
while (nodep->m_nextp) nodep=nodep->m_nextp;
|
||||
while (nodep) {
|
||||
@@ -822,13 +820,13 @@ void AstNode::iterateChildrenBackwards(AstNVisitor& v) {
|
||||
|
||||
void AstNode::iterateAndNextConst(AstNVisitor& v) {
|
||||
// Keep following the current list even if edits change it
|
||||
if (!this) return; // A few cases could be cleaned up, but want symmetry with iterateAndNext
|
||||
for (AstNode* nodep=this; nodep; ) { // effectively: if (!this) return; // Callers rely on this
|
||||
AstNode* nodep=this;
|
||||
do {
|
||||
AstNode* nnextp = nodep->m_nextp;
|
||||
ASTNODE_PREFETCH(nnextp);
|
||||
nodep->accept(v);
|
||||
nodep = nnextp;
|
||||
}
|
||||
} while (nodep);
|
||||
}
|
||||
|
||||
AstNode* AstNode::iterateSubtreeReturnEdits(AstNVisitor& v) {
|
||||
@@ -839,7 +837,7 @@ AstNode* AstNode::iterateSubtreeReturnEdits(AstNVisitor& v) {
|
||||
// To solve this, this function returns the pointer to the replacement node,
|
||||
// which in many cases is just the same node that was passed in.
|
||||
AstNode* nodep = this; // Note "this" may point to bogus point later in this function
|
||||
if (nodep->castNetlist()) {
|
||||
if (VN_IS(nodep, Netlist)) {
|
||||
// Calling on top level; we know the netlist won't get replaced
|
||||
nodep->accept(v);
|
||||
} else if (!nodep->backp()) {
|
||||
@@ -886,7 +884,7 @@ void AstNode::cloneRelinkTree() {
|
||||
//======================================================================
|
||||
// Comparison
|
||||
|
||||
bool AstNode::gateTreeIter() {
|
||||
bool AstNode::gateTreeIter() const {
|
||||
// private: Return true if the two trees are identical
|
||||
if (!isGateOptimizable()) return false;
|
||||
if (m_op1p && !m_op1p->gateTreeIter()) return false;
|
||||
@@ -896,7 +894,7 @@ bool AstNode::gateTreeIter() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AstNode::sameTreeIter(AstNode* node1p, AstNode* node2p, bool ignNext, bool gateOnly) {
|
||||
bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ignNext, bool gateOnly) {
|
||||
// private: Return true if the two trees are identical
|
||||
if (!node1p && !node2p) return true;
|
||||
if (!node1p || !node2p) return false;
|
||||
@@ -917,9 +915,9 @@ bool AstNode::sameTreeIter(AstNode* node1p, AstNode* node2p, bool ignNext, bool
|
||||
//======================================================================
|
||||
// Static utilities
|
||||
|
||||
ostream& operator<<(ostream& os, const V3Hash& rhs) {
|
||||
return os<<hex<<setw(2)<<setfill('0')<<rhs.depth()
|
||||
<<"_"<<setw(6)<<setfill('0')<<rhs.hshval();
|
||||
std::ostream& operator<<(std::ostream& os, const V3Hash& rhs) {
|
||||
return os<<std::hex<<std::setw(2)<<std::setfill('0')<<rhs.depth()
|
||||
<<"_"<<std::setw(6)<<std::setfill('0')<<rhs.hshval();
|
||||
}
|
||||
|
||||
V3Hash::V3Hash(const string& name) {
|
||||
@@ -938,7 +936,7 @@ void AstNode::checkTreeIter(AstNode* backp) {
|
||||
if (backp != this->backp()) {
|
||||
this->v3fatalSrc("Back node inconsistent");
|
||||
}
|
||||
if (castNodeTermop() || castNodeVarRef()) {
|
||||
if (VN_IS(this, NodeTermop) || VN_IS(this, NodeVarRef)) {
|
||||
// Termops have a short-circuited iterateChildren, so check usage
|
||||
if (op1p()||op2p()||op3p()||op4p())
|
||||
this->v3fatalSrc("Terminal operation with non-terminals");
|
||||
@@ -975,17 +973,14 @@ void AstNode::checkTree() {
|
||||
}
|
||||
|
||||
void AstNode::dumpGdb() { // For GDB only
|
||||
if (!dynamic_cast<const AstNode*>(this)) { cout<<"This=NULL"<<endl; return; }
|
||||
dumpGdbHeader();
|
||||
cout<<" "; dump(cout); cout<<endl;
|
||||
}
|
||||
void AstNode::dumpGdbHeader() const { // For GDB only
|
||||
if (!dynamic_cast<const AstNode*>(this)) { cout<<"This=NULL"<<endl; return; }
|
||||
dumpPtrs(cout);
|
||||
cout<<" Fileline = "<<fileline()<<endl;
|
||||
}
|
||||
void AstNode::dumpTreeGdb() { // For GDB only
|
||||
if (!dynamic_cast<const AstNode*>(this)) { cout<<"This=NULL"<<endl; return; }
|
||||
dumpGdbHeader();
|
||||
dumpTree(cout);
|
||||
}
|
||||
@@ -1002,29 +997,29 @@ void AstNode::checkIter() const {
|
||||
}
|
||||
}
|
||||
|
||||
void AstNode::dumpPtrs(ostream& os) const {
|
||||
os<<"This="<<typeName()<<" "<<(void*)this;
|
||||
os<<" back="<<(void*)backp();
|
||||
if (nextp()) os<<" next="<<(void*)nextp();
|
||||
void AstNode::dumpPtrs(std::ostream& os) const {
|
||||
os<<"This="<<typeName()<<" "<<cvtToHex(this);
|
||||
os<<" back="<<cvtToHex(backp());
|
||||
if (nextp()) os<<" next="<<cvtToHex(nextp());
|
||||
if (m_headtailp==this) os<<" headtail=this";
|
||||
else os<<" headtail="<<(void*)m_headtailp;
|
||||
if (op1p()) os<<" op1p="<<(void*)op1p();
|
||||
if (op2p()) os<<" op2p="<<(void*)op2p();
|
||||
if (op3p()) os<<" op3p="<<(void*)op3p();
|
||||
if (op4p()) os<<" op4p="<<(void*)op4p();
|
||||
if (user1p()) os<<" user1p="<<(void*)user1p();
|
||||
if (user2p()) os<<" user2p="<<(void*)user2p();
|
||||
if (user3p()) os<<" user3p="<<(void*)user3p();
|
||||
if (user4p()) os<<" user4p="<<(void*)user4p();
|
||||
if (user5p()) os<<" user5p="<<(void*)user5p();
|
||||
else os<<" headtail="<<cvtToHex(m_headtailp);
|
||||
if (op1p()) os<<" op1p="<<cvtToHex(op1p());
|
||||
if (op2p()) os<<" op2p="<<cvtToHex(op2p());
|
||||
if (op3p()) os<<" op3p="<<cvtToHex(op3p());
|
||||
if (op4p()) os<<" op4p="<<cvtToHex(op4p());
|
||||
if (user1p()) os<<" user1p="<<cvtToHex(user1p());
|
||||
if (user2p()) os<<" user2p="<<cvtToHex(user2p());
|
||||
if (user3p()) os<<" user3p="<<cvtToHex(user3p());
|
||||
if (user4p()) os<<" user4p="<<cvtToHex(user4p());
|
||||
if (user5p()) os<<" user5p="<<cvtToHex(user5p());
|
||||
if (m_iterpp) {
|
||||
os<<" iterpp="<<(void*)m_iterpp;
|
||||
os<<"*="<<(void*)*m_iterpp;
|
||||
os<<" iterpp="<<cvtToHex(m_iterpp);
|
||||
os<<"*="<<cvtToHex(*m_iterpp);
|
||||
}
|
||||
os<<endl;
|
||||
}
|
||||
|
||||
void AstNode::dumpTree(ostream& os, const string& indent, int maxDepth) {
|
||||
void AstNode::dumpTree(std::ostream& os, const string& indent, int maxDepth) {
|
||||
os<<indent<<" "<<this<<endl;
|
||||
if (debug()>8) { os<<indent<<" "; dumpPtrs(os); }
|
||||
if (maxDepth==1) {
|
||||
@@ -1037,7 +1032,7 @@ void AstNode::dumpTree(ostream& os, const string& indent, int maxDepth) {
|
||||
}
|
||||
}
|
||||
|
||||
void AstNode::dumpTreeAndNext(ostream& os, const string& indent, int maxDepth) {
|
||||
void AstNode::dumpTreeAndNext(std::ostream& os, const string& indent, int maxDepth) {
|
||||
// Audited to make sure this is never NULL
|
||||
for (AstNode* nodep=this; nodep; nodep=nodep->nextp()) {
|
||||
nodep->dumpTree(os, indent, maxDepth);
|
||||
@@ -1048,10 +1043,10 @@ 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));
|
||||
if (logsp->fail()) v3fatalSrc("Can't write "<<filename);
|
||||
*logsp<<"Verilator Tree Dump (format 0x3900) from <e"<<dec<<editCountLast()<<">";
|
||||
*logsp<<" to <e"<<dec<<editCountGbl()<<">"<<endl;
|
||||
const vl_unique_ptr<std::ofstream> logsp (V3File::new_ofstream(filename, append));
|
||||
if (logsp->fail()) v3fatal("Can't write "<<filename);
|
||||
*logsp<<"Verilator Tree Dump (format 0x3900) from <e"<<std::dec<<editCountLast()<<">";
|
||||
*logsp<<" to <e"<<std::dec<<editCountGbl()<<">"<<endl;
|
||||
if (editCountGbl()==editCountLast()
|
||||
&& !(v3Global.opt.dumpTree()>=9)) {
|
||||
*logsp<<endl;
|
||||
@@ -1066,27 +1061,25 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump) {
|
||||
checkTree();
|
||||
// Broken isn't part of check tree because it can munge iterp's
|
||||
// set by other steps if it is called in the middle of other operations
|
||||
if (AstNetlist* netp=this->castNetlist()) V3Broken::brokenAll(netp);
|
||||
if (AstNetlist* netp=VN_CAST(this, Netlist)) V3Broken::brokenAll(netp);
|
||||
}
|
||||
// Next dump can indicate start from here
|
||||
editCountSetLast();
|
||||
}
|
||||
|
||||
void AstNode::v3errorEndFatal(ostringstream& str) const { v3errorEnd(str); assert(0); }
|
||||
void AstNode::v3errorEndFatal(std::ostringstream& str) const { v3errorEnd(str); assert(0); }
|
||||
|
||||
void AstNode::v3errorEnd(ostringstream& str) const {
|
||||
if (!dynamic_cast<const AstNode*>(this)) {
|
||||
// No known cases cause this, but better than a core dump
|
||||
if (debug()) UINFO(0, "-node: NULL. Please report this along with a --gdbbt backtrace as a Verilator bug.\n");
|
||||
V3Error::v3errorEnd(str);
|
||||
} else if (!m_fileline) {
|
||||
void AstNode::v3errorEnd(std::ostringstream& str) const {
|
||||
if (!m_fileline) {
|
||||
V3Error::v3errorEnd(str);
|
||||
} else {
|
||||
ostringstream nsstr;
|
||||
std::ostringstream nsstr;
|
||||
nsstr<<str.str();
|
||||
if (debug()) {
|
||||
nsstr<<endl;
|
||||
nsstr<<"-node: "; ((AstNode*)this)->dump(nsstr); nsstr<<endl;
|
||||
nsstr<<"-node: ";
|
||||
const_cast<AstNode*>(this)->dump(nsstr);
|
||||
nsstr<<endl;
|
||||
}
|
||||
m_fileline->v3errorEnd(nsstr);
|
||||
}
|
||||
@@ -1153,7 +1146,7 @@ AstBasicDType* AstNode::findInsertSameDType(AstBasicDType* nodep) {
|
||||
// AstNVisitor
|
||||
|
||||
void AstNVisitor::doDeletes() {
|
||||
for (vector<AstNode*>::iterator it = m_deleteps.begin(); it != m_deleteps.end(); ++it) {
|
||||
for (std::vector<AstNode*>::iterator it = m_deleteps.begin(); it != m_deleteps.end(); ++it) {
|
||||
(*it)->deleteTree();
|
||||
}
|
||||
m_deleteps.clear();
|
||||
|
||||
+215
-141
@@ -20,28 +20,46 @@
|
||||
|
||||
#ifndef _V3AST_H_
|
||||
#define _V3AST_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3FileLine.h"
|
||||
#include "V3Number.h"
|
||||
#include "V3Global.h"
|
||||
#include <vector>
|
||||
|
||||
#include <cmath>
|
||||
#include <map>
|
||||
#include VL_INCLUDE_UNORDERED_SET
|
||||
|
||||
#include "V3Ast__gen_classes.h" // From ./astgen
|
||||
// Things like:
|
||||
// class V3AstNode;
|
||||
|
||||
// Forward declarations
|
||||
class V3Graph;
|
||||
class ExecMTask;
|
||||
|
||||
// Hint class so we can choose constructors
|
||||
class VFlagLogicPacked {};
|
||||
class VFlagBitPacked {};
|
||||
class VFlagChildDType {}; // Used by parser.y to select constructor that sets childDType
|
||||
|
||||
// Used as key for another map, needs operator<, hence not an unordered_set
|
||||
typedef std::set<int> MTaskIdSet; // Set of mtaskIds for Var sorting
|
||||
|
||||
//######################################################################
|
||||
|
||||
// For broken() function, return error string if have a match
|
||||
#define BROKEN_RTN(test) do { if (VL_UNLIKELY(test)) return # test; } while(0)
|
||||
|
||||
// (V)erilator (N)ode is: True if AstNode is of a a given AstType
|
||||
#define VN_IS(nodep,nodetypename) (AstNode::privateIs ## nodetypename(nodep))
|
||||
|
||||
// (V)erilator (N)ode cast: Cast to given type if can; effectively dynamic_cast(nodep)(nodetypename)
|
||||
#define VN_CAST(nodep,nodetypename) (AstNode::privateCast ## nodetypename(nodep))
|
||||
#define VN_CAST_CONST(nodep,nodetypename) (AstNode::privateConstCast ## nodetypename(nodep) )
|
||||
|
||||
//######################################################################
|
||||
|
||||
class AstType {
|
||||
@@ -52,16 +70,16 @@ public:
|
||||
// const char* ascii() const {...};
|
||||
enum en m_e;
|
||||
// cppcheck-suppress uninitVar // responsiblity of each subclass
|
||||
inline AstType () {}
|
||||
inline AstType() {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline AstType (en _e) : m_e(_e) {}
|
||||
explicit inline AstType (int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en () const { return m_e; }
|
||||
inline AstType(en _e) : m_e(_e) {}
|
||||
explicit inline AstType(int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en() const { return m_e; }
|
||||
};
|
||||
inline bool operator== (AstType lhs, AstType rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (AstType lhs, AstType::en rhs) { return (lhs.m_e == rhs); }
|
||||
inline bool operator== (AstType::en lhs, AstType rhs) { return (lhs == rhs.m_e); }
|
||||
inline ostream& operator<<(ostream& os, const AstType& rhs) { return os<<rhs.ascii(); }
|
||||
inline std::ostream& operator<<(std::ostream& os, const AstType& rhs) { return os<<rhs.ascii(); }
|
||||
|
||||
//######################################################################
|
||||
|
||||
@@ -87,19 +105,19 @@ public:
|
||||
};
|
||||
return names[m_e];
|
||||
};
|
||||
inline AstNumeric () : m_e(UNSIGNED) {}
|
||||
inline AstNumeric() : m_e(UNSIGNED) {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline AstNumeric (en _e) : m_e(_e) {}
|
||||
inline AstNumeric(en _e) : m_e(_e) {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline AstNumeric (VSignedState signst) {
|
||||
inline AstNumeric(VSignedState signst) {
|
||||
if (signst==signedst_UNSIGNED) m_e=UNSIGNED;
|
||||
else if (signst==signedst_SIGNED) m_e=SIGNED;
|
||||
else m_e=NOSIGN;
|
||||
}
|
||||
static inline AstNumeric fromBool (bool isSigned) { // Factory method
|
||||
static inline AstNumeric fromBool(bool isSigned) { // Factory method
|
||||
return isSigned ? AstNumeric(SIGNED) : AstNumeric(UNSIGNED); }
|
||||
explicit inline AstNumeric (int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en () const { return m_e; }
|
||||
explicit inline AstNumeric(int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en() const { return m_e; }
|
||||
inline bool isSigned() const { return m_e==SIGNED; }
|
||||
inline bool isNosign() const { return m_e==NOSIGN; }
|
||||
// No isUnsigned() as it's ambiguous if NOSIGN should be included or not.
|
||||
@@ -107,7 +125,7 @@ public:
|
||||
inline bool operator== (AstNumeric lhs, AstNumeric rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (AstNumeric lhs, AstNumeric::en rhs) { return (lhs.m_e == rhs); }
|
||||
inline bool operator== (AstNumeric::en lhs, AstNumeric rhs) { return (lhs == rhs.m_e); }
|
||||
inline ostream& operator<<(ostream& os, const AstNumeric& rhs) { return os<<rhs.ascii(); }
|
||||
inline std::ostream& operator<<(std::ostream& os, const AstNumeric& rhs) { return os<<rhs.ascii(); }
|
||||
|
||||
//######################################################################
|
||||
|
||||
@@ -123,11 +141,11 @@ public:
|
||||
PUBLIC_TASK
|
||||
};
|
||||
enum en m_e;
|
||||
inline AstPragmaType () : m_e(ILLEGAL) {}
|
||||
inline AstPragmaType() : m_e(ILLEGAL) {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline AstPragmaType (en _e) : m_e(_e) {}
|
||||
explicit inline AstPragmaType (int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en () const { return m_e; }
|
||||
inline AstPragmaType(en _e) : m_e(_e) {}
|
||||
explicit inline AstPragmaType(int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en() const { return m_e; }
|
||||
};
|
||||
inline bool operator== (AstPragmaType lhs, AstPragmaType rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (AstPragmaType lhs, AstPragmaType::en rhs) { return (lhs.m_e == rhs); }
|
||||
@@ -147,11 +165,11 @@ public:
|
||||
TRACE_CHANGE_SUB
|
||||
};
|
||||
enum en m_e;
|
||||
inline AstCFuncType () : m_e(FT_NORMAL) {}
|
||||
inline AstCFuncType() : m_e(FT_NORMAL) {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline AstCFuncType (en _e) : m_e(_e) {}
|
||||
explicit inline AstCFuncType (int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en () const { return m_e; }
|
||||
inline AstCFuncType(en _e) : m_e(_e) {}
|
||||
explicit inline AstCFuncType(int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en() const { return m_e; }
|
||||
// METHODS
|
||||
bool isTrace() const { return (m_e==TRACE_INIT || m_e==TRACE_INIT_SUB
|
||||
|| m_e==TRACE_FULL || m_e==TRACE_FULL_SUB
|
||||
@@ -167,7 +185,7 @@ class AstEdgeType {
|
||||
public:
|
||||
// REMEMBER to edit the strings below too
|
||||
enum en {
|
||||
// These must be in general -> most specific order, as we sort by it in V3Const::visit AstSenTre
|
||||
// These must be in general -> most specific order, as we sort by it in V3Const::visit AstSenTree
|
||||
ET_ILLEGAL,
|
||||
// Involving a variable
|
||||
ET_ANYEDGE, // Default for sensitivities; rip them out
|
||||
@@ -239,11 +257,11 @@ public:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
inline AstEdgeType () : m_e(ET_ILLEGAL) {}
|
||||
inline AstEdgeType() : m_e(ET_ILLEGAL) {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline AstEdgeType (en _e) : m_e(_e) {}
|
||||
explicit inline AstEdgeType (int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en () const { return m_e; }
|
||||
inline AstEdgeType(en _e) : m_e(_e) {}
|
||||
explicit inline AstEdgeType(int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en() const { return m_e; }
|
||||
};
|
||||
inline bool operator== (AstEdgeType lhs, AstEdgeType rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (AstEdgeType lhs, AstEdgeType::en rhs) { return (lhs.m_e == rhs); }
|
||||
@@ -307,11 +325,11 @@ public:
|
||||
};
|
||||
return names[m_e];
|
||||
};
|
||||
inline AstAttrType () : m_e(ILLEGAL) {}
|
||||
inline AstAttrType() : m_e(ILLEGAL) {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline AstAttrType (en _e) : m_e(_e) {}
|
||||
explicit inline AstAttrType (int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en () const { return m_e; }
|
||||
inline AstAttrType(en _e) : m_e(_e) {}
|
||||
explicit inline AstAttrType(int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en() const { return m_e; }
|
||||
};
|
||||
inline bool operator== (AstAttrType lhs, AstAttrType rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (AstAttrType lhs, AstAttrType::en rhs) { return (lhs.m_e == rhs); }
|
||||
@@ -364,14 +382,14 @@ public:
|
||||
return names[m_e];
|
||||
};
|
||||
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");
|
||||
UASSERT(0==strcmp(AstBasicDTypeKwd(_ENUM_MAX).ascii()," MAX"), "SelfTest: Enum mismatch");
|
||||
UASSERT(0==strcmp(AstBasicDTypeKwd(_ENUM_MAX).dpiType()," MAX"),"SelfTest: Enum mismatch");
|
||||
}
|
||||
inline AstBasicDTypeKwd () : m_e(UNKNOWN) {}
|
||||
inline AstBasicDTypeKwd() : m_e(UNKNOWN) {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline AstBasicDTypeKwd (en _e) : m_e(_e) {}
|
||||
explicit inline AstBasicDTypeKwd (int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en () const { return m_e; }
|
||||
inline AstBasicDTypeKwd(en _e) : m_e(_e) {}
|
||||
explicit inline AstBasicDTypeKwd(int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en() const { return m_e; }
|
||||
int width() const {
|
||||
switch (m_e) {
|
||||
case BIT: return 1; // scalar, can't bit extract unless ranged
|
||||
@@ -473,11 +491,11 @@ public:
|
||||
IFACEREF // Used to link Interfaces between modules
|
||||
};
|
||||
enum en m_e;
|
||||
inline AstVarType () : m_e(UNKNOWN) {}
|
||||
inline AstVarType() : m_e(UNKNOWN) {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline AstVarType (en _e) : m_e(_e) {}
|
||||
explicit inline AstVarType (int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en () const { return m_e; }
|
||||
inline AstVarType(en _e) : m_e(_e) {}
|
||||
explicit inline AstVarType(int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en() const { return m_e; }
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {
|
||||
"?","GPARAM","LPARAM","GENVAR",
|
||||
@@ -497,7 +515,7 @@ public:
|
||||
inline bool operator== (AstVarType lhs, AstVarType rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (AstVarType lhs, AstVarType::en rhs) { return (lhs.m_e == rhs); }
|
||||
inline bool operator== (AstVarType::en lhs, AstVarType rhs) { return (lhs == rhs.m_e); }
|
||||
inline ostream& operator<<(ostream& os, const AstVarType& rhs) { return os<<rhs.ascii(); }
|
||||
inline std::ostream& operator<<(std::ostream& os, const AstVarType& rhs) { return os<<rhs.ascii(); }
|
||||
|
||||
//######################################################################
|
||||
|
||||
@@ -511,11 +529,11 @@ public:
|
||||
};
|
||||
enum en m_e;
|
||||
// CONSTRUCTOR - note defaults to *UNKNOWN*
|
||||
inline AstBranchPred () : m_e(BP_UNKNOWN) {}
|
||||
inline AstBranchPred() : m_e(BP_UNKNOWN) {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline AstBranchPred (en _e) : m_e(_e) {}
|
||||
explicit inline AstBranchPred (int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en () const { return m_e; }
|
||||
inline AstBranchPred(en _e) : m_e(_e) {}
|
||||
explicit inline AstBranchPred(int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en() const { return m_e; }
|
||||
AstBranchPred invert() const {
|
||||
if (m_e==BP_UNLIKELY) return BP_LIKELY;
|
||||
else if (m_e==BP_LIKELY) return BP_UNLIKELY;
|
||||
@@ -529,7 +547,7 @@ public:
|
||||
inline bool operator== (AstBranchPred lhs, AstBranchPred rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (AstBranchPred lhs, AstBranchPred::en rhs) { return (lhs.m_e == rhs); }
|
||||
inline bool operator== (AstBranchPred::en lhs, AstBranchPred rhs) { return (lhs == rhs.m_e); }
|
||||
inline ostream& operator<<(ostream& os, const AstBranchPred& rhs) { return os<<rhs.ascii(); }
|
||||
inline std::ostream& operator<<(std::ostream& os, const AstBranchPred& rhs) { return os<<rhs.ascii(); }
|
||||
|
||||
//######################################################################
|
||||
|
||||
@@ -543,11 +561,11 @@ public:
|
||||
};
|
||||
enum en m_e;
|
||||
// CONSTRUCTOR - note defaults to *UNKNOWN*
|
||||
inline AstVarAttrClocker () : m_e(CLOCKER_UNKNOWN) {}
|
||||
inline AstVarAttrClocker() : m_e(CLOCKER_UNKNOWN) {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline AstVarAttrClocker (en _e) : m_e(_e) {}
|
||||
explicit inline AstVarAttrClocker (int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en () const { return m_e; }
|
||||
inline AstVarAttrClocker(en _e) : m_e(_e) {}
|
||||
explicit inline AstVarAttrClocker(int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en() const { return m_e; }
|
||||
bool unknown() const { return m_e==CLOCKER_UNKNOWN; }
|
||||
AstVarAttrClocker invert() const {
|
||||
if (m_e==CLOCKER_YES) return CLOCKER_NO;
|
||||
@@ -562,7 +580,7 @@ public:
|
||||
inline bool operator== (AstVarAttrClocker lhs, AstVarAttrClocker rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (AstVarAttrClocker lhs, AstVarAttrClocker::en rhs) { return (lhs.m_e == rhs); }
|
||||
inline bool operator== (AstVarAttrClocker::en lhs, AstVarAttrClocker rhs) { return (lhs == rhs.m_e); }
|
||||
inline ostream& operator<<(ostream& os, const AstVarAttrClocker& rhs) { return os<<rhs.ascii(); }
|
||||
inline std::ostream& operator<<(std::ostream& os, const AstVarAttrClocker& rhs) { return os<<rhs.ascii(); }
|
||||
|
||||
//######################################################################
|
||||
|
||||
@@ -575,11 +593,11 @@ public:
|
||||
ALWAYS_COMB
|
||||
};
|
||||
enum en m_e;
|
||||
inline VAlwaysKwd () : m_e(ALWAYS) {}
|
||||
inline VAlwaysKwd() : m_e(ALWAYS) {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline VAlwaysKwd (en _e) : m_e(_e) {}
|
||||
explicit inline VAlwaysKwd (int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en () const { return m_e; }
|
||||
inline VAlwaysKwd(en _e) : m_e(_e) {}
|
||||
explicit inline VAlwaysKwd(int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en() const { return m_e; }
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {
|
||||
"always","always_ff","always_latch","always_comb"};
|
||||
@@ -600,11 +618,11 @@ public:
|
||||
CT_CASEINSIDE
|
||||
};
|
||||
enum en m_e;
|
||||
inline VCaseType () : m_e(CT_CASE) {}
|
||||
inline VCaseType() : m_e(CT_CASE) {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline VCaseType (en _e) : m_e(_e) {}
|
||||
explicit inline VCaseType (int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en () const { return m_e; }
|
||||
inline VCaseType(en _e) : m_e(_e) {}
|
||||
explicit inline VCaseType(int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en() const { return m_e; }
|
||||
};
|
||||
inline bool operator== (VCaseType lhs, VCaseType rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (VCaseType lhs, VCaseType::en rhs) { return (lhs.m_e == rhs); }
|
||||
@@ -623,11 +641,11 @@ public:
|
||||
DT_FATAL
|
||||
};
|
||||
enum en m_e;
|
||||
inline AstDisplayType () : m_e(DT_DISPLAY) {}
|
||||
inline AstDisplayType() : m_e(DT_DISPLAY) {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline AstDisplayType (en _e) : m_e(_e) {}
|
||||
explicit inline AstDisplayType (int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en () const { return m_e; }
|
||||
inline AstDisplayType(en _e) : m_e(_e) {}
|
||||
explicit inline AstDisplayType(int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en() const { return m_e; }
|
||||
bool addNewline() const { return m_e!=DT_WRITE; }
|
||||
bool needScopeTracking() const { return m_e!=DT_DISPLAY && m_e!=DT_WRITE; }
|
||||
const char* ascii() const {
|
||||
@@ -650,9 +668,9 @@ public:
|
||||
enum en m_e;
|
||||
inline AstParseRefExp() : m_e(PX_NONE) {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline AstParseRefExp (en _e) : m_e(_e) {}
|
||||
explicit inline AstParseRefExp (int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en () const { return m_e; }
|
||||
inline AstParseRefExp(en _e) : m_e(_e) {}
|
||||
explicit inline AstParseRefExp(int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en() const { return m_e; }
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {
|
||||
"","TEXT","PREDOT"};
|
||||
@@ -661,7 +679,7 @@ public:
|
||||
inline bool operator== (AstParseRefExp lhs, AstParseRefExp rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (AstParseRefExp lhs, AstParseRefExp::en rhs) { return (lhs.m_e == rhs); }
|
||||
inline bool operator== (AstParseRefExp::en lhs, AstParseRefExp rhs) { return (lhs == rhs.m_e); }
|
||||
inline ostream& operator<<(ostream& os, const AstParseRefExp& rhs) { return os<<rhs.ascii(); }
|
||||
inline std::ostream& operator<<(std::ostream& os, const AstParseRefExp& rhs) { return os<<rhs.ascii(); }
|
||||
|
||||
//######################################################################
|
||||
// VNumRange - Structure containing numberic range information
|
||||
@@ -715,9 +733,9 @@ struct VNumRange {
|
||||
int hiMaxSelect() const { return (lo()<0 ? hi()-lo() : hi()); } // Maximum value a [] select may index
|
||||
bool representableByWidth() const // Could be represented by just width=1, or [width-1:0]
|
||||
{ return (!m_ranged || (m_lo==0 && m_hi>=1 && !m_littleEndian)); }
|
||||
void dump(ostream& str) const { if (ranged()) str<<"["<<left()<<":"<<right()<<"]"; else str<<"[norg]"; }
|
||||
void dump(std::ostream& str) const { if (ranged()) str<<"["<<left()<<":"<<right()<<"]"; else str<<"[norg]"; }
|
||||
};
|
||||
inline ostream& operator<<(ostream& os, const VNumRange& rhs) { rhs.dump(os); return os; }
|
||||
inline std::ostream& operator<<(std::ostream& os, const VNumRange& rhs) { rhs.dump(os); return os; }
|
||||
|
||||
//######################################################################
|
||||
|
||||
@@ -786,8 +804,8 @@ public:
|
||||
inline int toInt() {
|
||||
return m_u.ui;
|
||||
}
|
||||
static inline VNUser fromZero () { return VNUser(0); }
|
||||
static inline VNUser fromInt (int i) { return VNUser(i); }
|
||||
static inline VNUser fromZero() { return VNUser(0); }
|
||||
static inline VNUser fromInt(int i) { return VNUser(i); }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -891,10 +909,12 @@ public:
|
||||
|
||||
class AstNVisitor {
|
||||
private:
|
||||
vector<AstNode*> m_deleteps; // Nodes to delete when doDeletes() called
|
||||
// MEMBERS
|
||||
std::vector<AstNode*> m_deleteps; // Nodes to delete when doDeletes() called
|
||||
protected:
|
||||
friend class AstNode;
|
||||
public:
|
||||
// METHODS
|
||||
/// At the end of the visitor (or doDeletes()), delete this pushed node
|
||||
/// along with all children and next(s). This is often better to use
|
||||
/// than an immediate deleteTree, as any pointers into this node will
|
||||
@@ -908,6 +928,21 @@ public:
|
||||
virtual ~AstNVisitor() {
|
||||
doDeletes();
|
||||
}
|
||||
/// Call visit()s on nodep
|
||||
void iterate(AstNode* nodep);
|
||||
/// Call visit()s on nodep's children
|
||||
void iterateChildren(AstNode* nodep);
|
||||
/// Call visit()s on nodep's children in backp() order
|
||||
void iterateChildrenBackwards(AstNode* nodep);
|
||||
/// Call visit()s on const nodep's children
|
||||
void iterateChildrenConst(AstNode* nodep);
|
||||
/// Call visit()s on nodep (maybe NULL) and nodep's nextp() list
|
||||
void iterateAndNextNull(AstNode* nodep);
|
||||
/// Call visit()s on const nodep (maybe NULL) and nodep's nextp() list
|
||||
void iterateAndNextConstNull(AstNode* nodep);
|
||||
/// Return edited nodep; see comments in V3Ast.cpp
|
||||
AstNode* iterateSubtreeReturnEdits(AstNode* nodep);
|
||||
|
||||
#include "V3Ast__gen_visitor.h" // From ./astgen
|
||||
// Things like:
|
||||
// virtual void visit(AstBreak* nodep) { visit((AstNodeStmt*)(nodep)); }
|
||||
@@ -932,9 +967,9 @@ public:
|
||||
AstNRelinker() { m_oldp=NULL; m_backp=NULL; m_chg=RELINK_BAD; m_iterpp=NULL;}
|
||||
void relink(AstNode* newp);
|
||||
AstNode* oldp() const { return m_oldp; }
|
||||
void dump(ostream& str=cout) const;
|
||||
void dump(std::ostream& str=std::cout) const;
|
||||
};
|
||||
inline ostream& operator<<(ostream& os, const AstNRelinker& rhs) { rhs.dump(os); return os;}
|
||||
inline std::ostream& operator<<(std::ostream& os, const AstNRelinker& rhs) { rhs.dump(os); return os;}
|
||||
|
||||
//######################################################################
|
||||
// V3Hash -- Node hashing for V3Combine
|
||||
@@ -984,7 +1019,7 @@ public:
|
||||
V3Hash(V3Hash h1, V3Hash h2, V3Hash h3, V3Hash h4) {
|
||||
setBoth(1,((h1.hshval()*31+h2.hshval())*31+h3.hshval())*31+h4.hshval()); }
|
||||
};
|
||||
ostream& operator<<(ostream& os, const V3Hash& rhs);
|
||||
std::ostream& operator<<(std::ostream& os, const V3Hash& rhs);
|
||||
|
||||
//######################################################################
|
||||
// AstNode -- Base type of all Ast types
|
||||
@@ -992,7 +1027,7 @@ ostream& operator<<(ostream& os, const V3Hash& rhs);
|
||||
// Prefetch a node.
|
||||
// The if() makes it faster, even though prefetch won't fault on null pointers
|
||||
#define ASTNODE_PREFETCH(nodep) \
|
||||
{ if (nodep) { VL_PREFETCH_RD(&(nodep->m_nextp)); VL_PREFETCH_RD(&(nodep->m_iterpp)); }}
|
||||
{ if (nodep) { VL_PREFETCH_RD(&((nodep)->m_nextp)); VL_PREFETCH_RD(&((nodep)->m_iterpp)); }}
|
||||
|
||||
class AstNode {
|
||||
// v ASTNODE_PREFETCH depends on below ordering of members
|
||||
@@ -1042,14 +1077,13 @@ class AstNode {
|
||||
void op4p(AstNode* nodep) { m_op4p = nodep; if (nodep) nodep->m_backp = this; }
|
||||
|
||||
void init(); // initialize value of AstNode
|
||||
void iterateListBackwards(AstNVisitor& v);
|
||||
private:
|
||||
AstNode* cloneTreeIter();
|
||||
AstNode* cloneTreeIterList();
|
||||
void checkTreeIter(AstNode* backp);
|
||||
void checkTreeIterList(AstNode* backp);
|
||||
bool gateTreeIter();
|
||||
bool sameTreeIter(AstNode* node1p, AstNode* node2p, bool ignNext, bool gateOnly);
|
||||
bool gateTreeIter() const;
|
||||
static bool sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ignNext, bool gateOnly);
|
||||
void deleteTreeIter();
|
||||
void deleteNode();
|
||||
public:
|
||||
@@ -1137,8 +1171,8 @@ public:
|
||||
static string dedotName(const string& namein); // Name with dots removed
|
||||
static string prettyName(const string& namein); // Name for printing out to the user
|
||||
static string encodeName(const string& namein); // Encode user name into internal C representation
|
||||
static string encodeNumber(vlsint64_t numin); // Encode number into internal C representation
|
||||
static string vcdName(const string& namein); // Name for printing out to vcd files
|
||||
static string encodeNumber(vlsint64_t num); // Encode number into internal C representation
|
||||
static string vcdName(const string& namein); // Name for printing out to vcd files
|
||||
string prettyName() const { return prettyName(name()); }
|
||||
string prettyTypeName() const; // "VARREF" for error messages
|
||||
virtual string prettyOperatorName() const { return "operator "+prettyTypeName(); }
|
||||
@@ -1241,11 +1275,11 @@ public:
|
||||
|
||||
// ACCESSORS for specific types
|
||||
// Alas these can't be virtual or they break when passed a NULL
|
||||
bool isZero();
|
||||
bool isOne();
|
||||
bool isNeqZero();
|
||||
bool isAllOnes();
|
||||
bool isAllOnesV(); // Verilog width rules apply
|
||||
bool isZero() const;
|
||||
bool isOne() const;
|
||||
bool isNeqZero() const;
|
||||
bool isAllOnes() const;
|
||||
bool isAllOnesV() const; // Verilog width rules apply
|
||||
|
||||
// METHODS - data type changes especially for initial creation
|
||||
void dtypep(AstNodeDType* nodep) { if (m_dtypep != nodep) { m_dtypep = nodep; editCountInc(); } }
|
||||
@@ -1276,10 +1310,10 @@ public:
|
||||
AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
|
||||
|
||||
// METHODS - dump and error
|
||||
void v3errorEnd(ostringstream& str) const;
|
||||
void v3errorEndFatal(ostringstream& str) const VL_ATTR_NORETURN;
|
||||
void v3errorEnd(std::ostringstream& str) const;
|
||||
void v3errorEndFatal(std::ostringstream& str) const VL_ATTR_NORETURN;
|
||||
string warnMore() const;
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual void dump(std::ostream& str=std::cout);
|
||||
void dumpGdb(); // For GDB only
|
||||
void dumpGdbHeader() const;
|
||||
|
||||
@@ -1303,21 +1337,23 @@ public:
|
||||
virtual void addBeforeStmt(AstNode* newp, AstNode* belowp); // When calling, "this" is second argument
|
||||
|
||||
// METHODS - Iterate on a tree
|
||||
static AstNode* cloneTreeNull(AstNode* nodep, bool cloneNextLink) { // Clone or return NULL if NULL
|
||||
return nodep ? nodep->cloneTree(cloneNextLink) : NULL; }
|
||||
AstNode* cloneTree(bool cloneNextLink);
|
||||
bool gateTree() { return gateTreeIter(); } // Is tree isGateOptimizable?
|
||||
bool sameTree(AstNode* node2p); // Does tree of this == node2p?
|
||||
bool sameGateTree(AstNode* node2p); // Does tree of this == node2p?, not allowing non-isGateOptimizable
|
||||
bool sameTree(const AstNode* node2p) const; // Does tree of this == node2p?
|
||||
bool sameGateTree(const AstNode* node2p) const; // Does tree of this == node2p?, not allowing non-isGateOptimizable
|
||||
void deleteTree(); // Always deletes the next link
|
||||
void checkTree(); // User Interface version
|
||||
void checkIter() const;
|
||||
void clearIter() { m_iterpp=NULL; }
|
||||
void dumpPtrs(ostream& str=cout) const;
|
||||
void dumpTree(ostream& str=cout, const string& indent=" ", int maxDepth=0);
|
||||
void dumpTree(const string& indent, int maxDepth=0) { dumpTree(cout,indent,maxDepth); }
|
||||
void dumpTreeGdb(); // For GDB only
|
||||
void dumpTreeAndNext(ostream& str=cout, const string& indent=" ", int maxDepth=0);
|
||||
void dumpTreeFile(const string& filename, bool append=false, bool doDump=true);
|
||||
static void dumpTreeFileGdb(const char* filenamep=NULL);
|
||||
void dumpPtrs(std::ostream& os=std::cout) const;
|
||||
void dumpTree(std::ostream& os=std::cout, const string& indent=" ", int maxDepth=0);
|
||||
void dumpTree(const string& indent, int maxDepth=0) { dumpTree(cout,indent,maxDepth); }
|
||||
void dumpTreeGdb(); // For GDB only
|
||||
void dumpTreeAndNext(std::ostream& os=std::cout, const string& indent=" ", int maxDepth=0);
|
||||
void dumpTreeFile(const string& filename, bool append=false, bool doDump=true);
|
||||
static void dumpTreeFileGdb(const char* filenamep=NULL);
|
||||
|
||||
// METHODS - queries
|
||||
virtual bool isPure() const { return true; } // Else a $display, etc, that must be ordered with other displays
|
||||
@@ -1341,21 +1377,27 @@ public:
|
||||
|
||||
// INVOKERS
|
||||
virtual void accept(AstNVisitor& v) = 0;
|
||||
void iterate(AstNVisitor& v) { this->accept(v); } // Does this; excludes following this->next
|
||||
void iterateAndNext(AstNVisitor& v);
|
||||
void iterateAndNextConst(AstNVisitor& v);
|
||||
void iterateChildren(AstNVisitor& v); // Excludes following this->next
|
||||
void iterateChildrenBackwards(AstNVisitor& v); // Excludes following this->next
|
||||
void iterateChildrenConst(AstNVisitor& v); // Excludes following this->next
|
||||
AstNode* iterateSubtreeReturnEdits(AstNVisitor& v); // Return edited nodep; see comments in V3Ast.cpp
|
||||
|
||||
protected:
|
||||
// All AstNVisitor related functions are called as methods off the visitor
|
||||
friend class AstNVisitor;
|
||||
void iterateChildren(AstNVisitor& v); // Use instead AstNVisitor::iterateChildren
|
||||
void iterateChildrenBackwards(AstNVisitor& v); // Use instead AstNVisitor::iterateChildrenBackwards
|
||||
void iterateChildrenConst(AstNVisitor& v); // Use instead AstNVisitor::iterateChildrenConst
|
||||
void iterateAndNext(AstNVisitor& v); // Use instead AstNVisitor::iterateAndNextNull
|
||||
void iterateAndNextConst(AstNVisitor& v); // Use instead AstNVisitor::iterateAndNextConstNull
|
||||
AstNode* iterateSubtreeReturnEdits(AstNVisitor& v); // Use instead AstNVisitor::iterateSubtreeReturnEdits
|
||||
private:
|
||||
void iterateListBackwards(AstNVisitor& v);
|
||||
|
||||
// CONVERSION
|
||||
public:
|
||||
#include "V3Ast__gen_interface.h" // From ./astgen
|
||||
// Things like:
|
||||
// AstAlways* castAlways();
|
||||
};
|
||||
|
||||
inline ostream& operator<<(ostream& os, AstNode* rhs) { if (!rhs) os<<"NULL"; else rhs->dump(os); return os; }
|
||||
inline std::ostream& operator<<(std::ostream& os, AstNode* rhs) { if (!rhs) os<<"NULL"; else rhs->dump(os); return os; }
|
||||
inline void AstNRelinker::relink(AstNode* newp) { newp->AstNode::relink(this); }
|
||||
|
||||
//######################################################################
|
||||
@@ -1364,6 +1406,8 @@ inline void AstNRelinker::relink(AstNode* newp) { newp->AstNode::relink(this); }
|
||||
|
||||
#define ASTNODE_BASE_FUNCS(name) \
|
||||
virtual ~Ast ##name() {} \
|
||||
static Ast ##name * cloneTreeNull(Ast ##name * nodep, bool cloneNextLink) { \
|
||||
return nodep ? nodep->cloneTree(cloneNextLink) : NULL; } \
|
||||
Ast ##name * cloneTree(bool cloneNext) { return static_cast<Ast ##name *>(AstNode::cloneTree(cloneNext)); } \
|
||||
Ast ##name * clonep() const { return static_cast<Ast ##name *>(AstNode::clonep()); }
|
||||
|
||||
@@ -1381,7 +1425,7 @@ public:
|
||||
virtual bool cleanOut() = 0; // True if output has extra upper bits zero
|
||||
// Someday we will generically support data types on every math node
|
||||
// Until then isOpaque indicates we shouldn't constant optimize this node type
|
||||
bool isOpaque() { return castCvtPackString()!=NULL; }
|
||||
bool isOpaque() { return VN_IS(this, CvtPackString); }
|
||||
};
|
||||
|
||||
class AstNodeTermop : public AstNodeMath {
|
||||
@@ -1523,7 +1567,7 @@ public:
|
||||
AstNode* fromp() const { return lhsp(); }
|
||||
AstNode* rhsp() const { return op2p(); }
|
||||
AstNode* thsp() const { return op3p(); }
|
||||
AstAttrOf* attrp() const { return op4p()->castAttrOf(); }
|
||||
AstAttrOf* attrp() const { return VN_CAST(op4p(), AttrOf); }
|
||||
void lhsp(AstNode* nodep) { return setOp1p(nodep); }
|
||||
void rhsp(AstNode* nodep) { return setOp2p(nodep); }
|
||||
void thsp(AstNode* nodep) { return setOp3p(nodep); }
|
||||
@@ -1618,7 +1662,7 @@ public:
|
||||
ASTNODE_BASE_FUNCS(NodeCase)
|
||||
virtual int instrCount() const { return instrCountBranch(); }
|
||||
AstNode* exprp() const { return op1p(); } // op1 = case condition <expression>
|
||||
AstCaseItem* itemsp() const { return op2p()->castCaseItem(); } // op2 = list of case expressions
|
||||
AstCaseItem* itemsp() const { return VN_CAST(op2p(), CaseItem); } // op2 = list of case expressions
|
||||
AstNode* notParallelp() const { return op3p(); } // op3 = assertion code for non-full case's
|
||||
void addItemsp(AstNode* nodep) { addOp2p(nodep); }
|
||||
void addNotParallelp(AstNode* nodep) { setOp3p(nodep); }
|
||||
@@ -1694,7 +1738,7 @@ public:
|
||||
m_text = textp; // Copy it
|
||||
}
|
||||
ASTNODE_BASE_FUNCS(NodeText)
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual void dump(std::ostream& str=std::cout);
|
||||
virtual V3Hash sameHash() const { return V3Hash(text()); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
const AstNodeText* asamep = static_cast<const AstNodeText*>(samep);
|
||||
@@ -1720,8 +1764,8 @@ public:
|
||||
}
|
||||
ASTNODE_BASE_FUNCS(NodeDType)
|
||||
// ACCESSORS
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dumpSmall(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual void dumpSmall(std::ostream& str);
|
||||
virtual bool hasDType() const { return true; }
|
||||
virtual AstBasicDType* basicp() const = 0; // (Slow) recurse down to find basic data type
|
||||
virtual AstNodeDType* skipRefp() const = 0; // recurses over typedefs/const/enum to next non-typeref type
|
||||
@@ -1754,8 +1798,8 @@ public:
|
||||
bool widthSized() const { return !m_widthMin || m_widthMin==m_width; }
|
||||
bool generic() const { return m_generic; }
|
||||
void generic(bool flag) { m_generic = flag; }
|
||||
AstNodeDType* dtypeDimensionp(int depth);
|
||||
pair<uint32_t,uint32_t> dimensions(bool includeBasic);
|
||||
AstNodeDType* dtypeDimensionp(int dimension);
|
||||
std::pair<uint32_t,uint32_t> dimensions(bool includeBasic);
|
||||
uint32_t arrayUnpackedElements(); // 1, or total multiplication of all dimensions
|
||||
static int uniqueNumInc() { return ++s_uniqueNum; }
|
||||
const char* charIQWN() const { return (isString() ? "N" : isWide() ? "W" : isQuad() ? "Q" : "I"); }
|
||||
@@ -1764,8 +1808,9 @@ public:
|
||||
class AstNodeClassDType : public AstNodeDType {
|
||||
private:
|
||||
// TYPES
|
||||
typedef map<string,AstMemberDType*> MemberNameMap;
|
||||
typedef std::map<string,AstMemberDType*> MemberNameMap;
|
||||
// MEMBERS
|
||||
string m_name; // Name from upper typedef, if any
|
||||
bool m_packed;
|
||||
bool m_isFourstate;
|
||||
MemberNameMap m_members;
|
||||
@@ -1779,11 +1824,12 @@ public:
|
||||
}
|
||||
ASTNODE_BASE_FUNCS(NodeClassDType)
|
||||
virtual const char* broken() const;
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
// For basicp() we reuse the size to indicate a "fake" basic type of same size
|
||||
virtual AstBasicDType* basicp() const {
|
||||
return (isFourstate() ? findLogicDType(width(),width(),numeric())->castBasicDType()
|
||||
: findBitDType(width(),width(),numeric())->castBasicDType()); }
|
||||
return (isFourstate()
|
||||
? VN_CAST(findLogicDType(width(),width(),numeric()), BasicDType)
|
||||
: VN_CAST(findBitDType(width(),width(),numeric()), BasicDType)); }
|
||||
virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; }
|
||||
virtual AstNodeDType* skipRefToConstp() const { return (AstNodeDType*)this; }
|
||||
virtual AstNodeDType* skipRefToEnump() const { return (AstNodeDType*)this; }
|
||||
@@ -1793,7 +1839,9 @@ public:
|
||||
virtual bool similarDType(AstNodeDType* samep) const {
|
||||
return this==samep; // We don't compare members, require exact equivalence
|
||||
}
|
||||
AstMemberDType* membersp() const { return op1p()->castMemberDType(); } // op1 = AstMember list
|
||||
virtual string name() const { return m_name; }
|
||||
void name(const string& flag) { m_name = flag; }
|
||||
AstMemberDType* membersp() const { return VN_CAST(op1p(), MemberDType); } // op1 = AstMember list
|
||||
void addMembersp(AstNode* nodep) { addNOp1p(nodep); }
|
||||
bool packed() const { return m_packed; }
|
||||
bool packedUnsup() const { return true; } // packed() but as don't support unpacked, presently all structs
|
||||
@@ -1822,8 +1870,8 @@ public:
|
||||
m_refDTypep = NULL;
|
||||
}
|
||||
ASTNODE_BASE_FUNCS(NodeArrayDType)
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dumpSmall(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual void dumpSmall(std::ostream& str);
|
||||
virtual const char* broken() const { BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|
||||
|| (!m_refDTypep && childDTypep()))); return NULL; }
|
||||
virtual void cloneRelink() { if (m_refDTypep && m_refDTypep->clonep()) {
|
||||
@@ -1844,13 +1892,13 @@ public:
|
||||
}
|
||||
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
|
||||
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Range of variable
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
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); }
|
||||
AstRange* rangep() const { return op2p()->castRange(); } // op2 = Array(s) of variable
|
||||
AstRange* rangep() const { return VN_CAST(op2p(), Range); } // op2 = Array(s) of variable
|
||||
void rangep(AstRange* nodep);
|
||||
// METHODS
|
||||
virtual AstBasicDType* basicp() const { return subDTypep()->basicp(); } // (Slow) recurse down to find basic data type
|
||||
@@ -1919,7 +1967,7 @@ public:
|
||||
cname(name); // Might be overridden by dpi import/export
|
||||
}
|
||||
ASTNODE_BASE_FUNCS(NodeFTask)
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual void dump(std::ostream& str=std::cout);
|
||||
virtual string name() const { return m_name; } // * = Var name
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
virtual bool isGateOptimizable() const { return !((m_dpiExport || m_dpiImport) && !m_pure); }
|
||||
@@ -1935,7 +1983,7 @@ public:
|
||||
AstNode* stmtsp() const { return op3p(); } // op3 = List of statements
|
||||
void addStmtsp(AstNode* nodep) { addNOp3p(nodep); }
|
||||
// op4 = scope name
|
||||
AstScopeName* scopeNamep() const { return op4p()->castScopeName(); }
|
||||
AstScopeName* scopeNamep() const { return VN_CAST(op4p(), ScopeName); }
|
||||
// MORE ACCESSORS
|
||||
void dpiOpenParentInc() { ++m_dpiOpenParent; }
|
||||
void dpiOpenParentClear() { m_dpiOpenParent=0; }
|
||||
@@ -1985,7 +2033,7 @@ public:
|
||||
virtual void cloneRelink() { if (m_taskp && m_taskp->clonep()) {
|
||||
m_taskp = m_taskp->clonep();
|
||||
}}
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual void dump(std::ostream& str=std::cout);
|
||||
virtual string name() const { return m_name; } // * = Var name
|
||||
virtual bool isGateOptimizable() const { return m_taskp && m_taskp->isGateOptimizable(); }
|
||||
string dotted() const { return m_dotted; } // * = Scope name or ""
|
||||
@@ -2004,7 +2052,7 @@ public:
|
||||
AstNode* pinsp() const { return op2p(); }
|
||||
void addPinsp(AstNode* nodep) { addOp2p(nodep); }
|
||||
// op3 = scope tracking
|
||||
AstScopeName* scopeNamep() const { return op3p()->castScopeName(); }
|
||||
AstScopeName* scopeNamep() const { return VN_CAST(op3p(), ScopeName); }
|
||||
void scopeNamep(AstNode* nodep) { setNOp3p(nodep); }
|
||||
};
|
||||
|
||||
@@ -2027,17 +2075,17 @@ private:
|
||||
int m_typeNum; // Incrementing implicit type number
|
||||
public:
|
||||
AstNodeModule(FileLine* fl, const string& name)
|
||||
: AstNode (fl)
|
||||
: AstNode(fl)
|
||||
,m_name(name), m_origName(name)
|
||||
,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);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
virtual string name() const { return m_name; }
|
||||
AstNode* stmtsp() const { return op2p(); } // op2 = List of statements
|
||||
AstActive* activesp() const { return op3p()->castActive(); } // op3 = List of i/sblocks
|
||||
AstActive* activesp() const { return VN_CAST(op3p(), Active); } // op3 = List of i/sblocks
|
||||
// METHODS
|
||||
void addInlinesp(AstNode* nodep) { addOp1p(nodep); }
|
||||
void addStmtp(AstNode* nodep) { addOp2p(nodep); }
|
||||
@@ -2069,7 +2117,7 @@ public:
|
||||
class AstNodeRange : public AstNode {
|
||||
// A range, sized or unsized
|
||||
public:
|
||||
explicit AstNodeRange(FileLine* fl) : AstNode (fl) { }
|
||||
explicit AstNodeRange(FileLine* fl) : AstNode(fl) { }
|
||||
ASTNODE_BASE_FUNCS(NodeRange)
|
||||
};
|
||||
|
||||
@@ -2079,7 +2127,33 @@ public:
|
||||
|
||||
#include "V3Ast__gen_impl.h" // From ./astgen
|
||||
// Things like:
|
||||
// inline AstAlways* AstNode::castAlways() { return dynamic_cast<AstAlways*>(this);}
|
||||
// inline AstAlways* AstNode::castAlways() { return dynamic_cast<AstAlways*>(this);}
|
||||
// inline bool AstNode::privateIsaAlways(const AstNode* nodep) { return nodep && nodep->type() == AstType::atAlways; }
|
||||
|
||||
//######################################################################
|
||||
// Inline AstNVisitor METHODS
|
||||
|
||||
inline void AstNVisitor::iterate(AstNode* nodep) {
|
||||
nodep->accept(*this);
|
||||
}
|
||||
inline void AstNVisitor::iterateChildren(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
inline void AstNVisitor::iterateChildrenBackwards(AstNode* nodep) {
|
||||
nodep->iterateChildrenBackwards(*this);
|
||||
}
|
||||
inline void AstNVisitor::iterateChildrenConst(AstNode* nodep) {
|
||||
nodep->iterateChildrenConst(*this);
|
||||
}
|
||||
inline void AstNVisitor::iterateAndNextNull(AstNode* nodep) {
|
||||
if (VL_LIKELY(nodep)) nodep->iterateAndNext(*this);
|
||||
}
|
||||
inline void AstNVisitor::iterateAndNextConstNull(AstNode* nodep) {
|
||||
if (VL_LIKELY(nodep)) nodep->iterateAndNextConst(*this);
|
||||
}
|
||||
inline AstNode* AstNVisitor::iterateSubtreeReturnEdits(AstNode* nodep) {
|
||||
return nodep->iterateSubtreeReturnEdits(*this);
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// Inline ACCESSORS
|
||||
@@ -2091,19 +2165,19 @@ inline bool AstNode::width1() const { // V3Const uses to know it can optimize
|
||||
inline int AstNode::widthInstrs() const {
|
||||
return (!dtypep() ? 1 : (dtypep()->isWide() ? dtypep()->widthWords() : 1)); }
|
||||
inline bool AstNode::isDouble() const {
|
||||
return dtypep() && dtypep()->castBasicDType() && dtypep()->castBasicDType()->isDouble(); }
|
||||
return dtypep() && VN_IS(dtypep(), BasicDType) && VN_CAST(dtypep(), BasicDType)->isDouble(); }
|
||||
inline bool AstNode::isString() const {
|
||||
return dtypep() && dtypep()->basicp() && dtypep()->basicp()->isString(); }
|
||||
inline bool AstNode::isSigned() const {
|
||||
return dtypep() && dtypep()->isSigned(); }
|
||||
|
||||
inline bool AstNode::isZero() { return (this->castConst() && this->castConst()->num().isEqZero()); }
|
||||
inline bool AstNode::isNeqZero() { return (this->castConst() && this->castConst()->num().isNeqZero()); }
|
||||
inline bool AstNode::isOne() { return (this->castConst() && this->castConst()->num().isEqOne()); }
|
||||
inline bool AstNode::isAllOnes() { return (this->castConst() && this->castConst()->isEqAllOnes()); }
|
||||
inline bool AstNode::isAllOnesV() { return (this->castConst() && this->castConst()->isEqAllOnesV()); }
|
||||
inline bool AstNode::sameTree(AstNode* node2p) { return sameTreeIter(this, node2p, true, false); }
|
||||
inline bool AstNode::sameGateTree(AstNode* node2p) { return sameTreeIter(this, node2p, true, true); }
|
||||
inline bool AstNode::isZero() const { return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isEqZero()); }
|
||||
inline bool AstNode::isNeqZero() const { return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isNeqZero()); }
|
||||
inline bool AstNode::isOne() const { return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isEqOne()); }
|
||||
inline bool AstNode::isAllOnes() const { return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->isEqAllOnes()); }
|
||||
inline bool AstNode::isAllOnesV() const { return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->isEqAllOnesV()); }
|
||||
inline bool AstNode::sameTree(const AstNode* node2p) const { return sameTreeIter(this, node2p, true, false); }
|
||||
inline bool AstNode::sameGateTree(const AstNode* node2p) const { return sameTreeIter(this, node2p, true, true); }
|
||||
|
||||
inline void AstNodeVarRef::init() { if (m_varp) dtypep(m_varp->dtypep()); }
|
||||
|
||||
|
||||
+149
-130
@@ -20,17 +20,16 @@
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include VL_INCLUDE_UNORDERED_SET
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3File.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Graph.h"
|
||||
#include "V3PartitionGraph.h" // Just for mtask dumping
|
||||
|
||||
#include <cstdarg>
|
||||
#include <iomanip>
|
||||
#include <vector>
|
||||
|
||||
//======================================================================
|
||||
// Special methods
|
||||
@@ -44,7 +43,7 @@ const char* AstIfaceRefDType::broken() const {
|
||||
}
|
||||
|
||||
AstIface* AstIfaceRefDType::ifaceViaCellp() const {
|
||||
return ((m_cellp && m_cellp->modp()) ? m_cellp->modp()->castIface() : m_ifacep);
|
||||
return ((m_cellp && m_cellp->modp()) ? VN_CAST(m_cellp->modp(), Iface) : m_ifacep);
|
||||
}
|
||||
|
||||
const char* AstNodeVarRef::broken() const {
|
||||
@@ -58,12 +57,13 @@ void AstNodeVarRef::cloneRelink() {
|
||||
}
|
||||
|
||||
int AstNodeSel::bitConst() const {
|
||||
AstConst* constp=bitp()->castConst(); return (constp?constp->toSInt():0);
|
||||
AstConst* constp=VN_CAST(bitp(), Const);
|
||||
return (constp ? constp->toSInt() : 0);
|
||||
}
|
||||
|
||||
void AstNodeClassDType::repairMemberCache() {
|
||||
clearCache();
|
||||
for (AstMemberDType* itemp = membersp(); itemp; itemp=itemp->nextp()->castMemberDType()) {
|
||||
for (AstMemberDType* itemp = membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) {
|
||||
if (m_members.find(itemp->name())!=m_members.end()) {
|
||||
itemp->v3error("Duplicate declaration of member name: "<<itemp->prettyName()); }
|
||||
else m_members.insert(make_pair(itemp->name(), itemp));
|
||||
@@ -72,7 +72,7 @@ void AstNodeClassDType::repairMemberCache() {
|
||||
|
||||
const char* AstNodeClassDType::broken() const {
|
||||
vl_unordered_set<AstMemberDType*> exists;
|
||||
for (AstMemberDType* itemp = membersp(); itemp; itemp=itemp->nextp()->castMemberDType()) {
|
||||
for (AstMemberDType* itemp = membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) {
|
||||
exists.insert(itemp);
|
||||
}
|
||||
for (MemberNameMap::const_iterator it=m_members.begin(); it!=m_members.end(); ++it) {
|
||||
@@ -150,22 +150,26 @@ AstNodeBiop* AstEqWild::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
||||
}
|
||||
}
|
||||
|
||||
AstExecGraph::AstExecGraph(FileLine* fileline)
|
||||
: AstNode(fileline) {
|
||||
m_depGraphp = new V3Graph;
|
||||
}
|
||||
AstExecGraph::~AstExecGraph() {
|
||||
delete m_depGraphp; VL_DANGLING(m_depGraphp);
|
||||
}
|
||||
|
||||
bool AstVar::isSigPublic() const {
|
||||
return (m_sigPublic || (v3Global.opt.allPublic() && !isTemp() && !isGenVar()));
|
||||
}
|
||||
|
||||
bool AstVar::isScQuad() const {
|
||||
return (isSc() && isQuad() && !isScBv() && !isScBigUint());
|
||||
}
|
||||
|
||||
bool AstVar::isScBv() const {
|
||||
return ((isSc() && width() >= v3Global.opt.pinsBv()) || m_attrScBv);
|
||||
}
|
||||
|
||||
bool AstVar::isScUint() const {
|
||||
return ((isSc() && v3Global.opt.pinsScUint() && width() >= 2 && width() <= 64) && !isScBv());
|
||||
}
|
||||
|
||||
bool AstVar::isScBigUint() const {
|
||||
return ((isSc() && v3Global.opt.pinsScBigUint() && width() >= 65 && width() <= 512) && !isScBv());
|
||||
}
|
||||
@@ -174,12 +178,12 @@ void AstVar::combineType(AstVarType type) {
|
||||
// These flags get combined with the existing settings of the flags.
|
||||
// We don't test varType for certain types, instead set flags since
|
||||
// when we combine wires cross-hierarchy we need a union of all characteristics.
|
||||
if (type == AstVarType::SUPPLY0) type = AstVarType::WIRE;
|
||||
if (type == AstVarType::SUPPLY1) type = AstVarType::WIRE;
|
||||
m_varType=type; // For debugging prints only
|
||||
// These flags get combined with the existing settings of the flags.
|
||||
if (type==AstVarType::INPUT || type==AstVarType::INOUT)
|
||||
if (type==AstVarType::INPUT || type==AstVarType::INOUT) {
|
||||
m_input = true;
|
||||
m_declInput = true;
|
||||
}
|
||||
if (type==AstVarType::OUTPUT || type==AstVarType::INOUT) {
|
||||
m_output = true;
|
||||
m_declOutput = true;
|
||||
@@ -244,7 +248,7 @@ string AstVar::vlArgType(bool named, bool forReturn, bool forFunc) const {
|
||||
arg += " (& "+name()+")";
|
||||
for (AstNodeDType* dtp=dtypep(); dtp; ) {
|
||||
dtp = dtp->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
|
||||
if (AstUnpackArrayDType* adtypep = dtp->castUnpackArrayDType()) {
|
||||
if (AstUnpackArrayDType* adtypep = VN_CAST(dtp, UnpackArrayDType)) {
|
||||
arg += "["+cvtToStr(adtypep->declRange().elements())+"]";
|
||||
dtp = adtypep->subDTypep();
|
||||
} else break;
|
||||
@@ -316,7 +320,7 @@ string AstVar::vlPropInit() const {
|
||||
bool first = true;
|
||||
for (AstNodeDType* dtp=dtypep(); dtp; ) {
|
||||
dtp = dtp->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
|
||||
if (AstNodeArrayDType* adtypep = dtp->castNodeArrayDType()) {
|
||||
if (AstNodeArrayDType* adtypep = VN_CAST(dtp, NodeArrayDType)) {
|
||||
if (first) {
|
||||
out += ", VerilatedVarProps::Unpacked()";
|
||||
first = false;
|
||||
@@ -423,15 +427,15 @@ AstVar* AstVar::scVarRecurse(AstNode* nodep) {
|
||||
// See if this is a SC assignment; if so return that type
|
||||
// Historically sc variables are identified by a variable
|
||||
// attribute. TODO it would better be a data type attribute.
|
||||
if (AstVar* anodep = nodep->castVar()) {
|
||||
if (AstVar* anodep = VN_CAST(nodep, Var)) {
|
||||
if (anodep->isSc()) return anodep;
|
||||
else return NULL;
|
||||
}
|
||||
else if (nodep->castVarRef()) {
|
||||
if (nodep->castVarRef()->varp()->isSc()) return nodep->castVarRef()->varp();
|
||||
else if (VN_IS(nodep, VarRef)) {
|
||||
if (VN_CAST(nodep, VarRef)->varp()->isSc()) return VN_CAST(nodep, VarRef)->varp();
|
||||
else return NULL;
|
||||
}
|
||||
else if (nodep->castArraySel()) {
|
||||
else if (VN_IS(nodep, ArraySel)) {
|
||||
if (nodep->op1p()) if (AstVar* p = scVarRecurse(nodep->op1p())) return p;
|
||||
if (nodep->op2p()) if (AstVar* p = scVarRecurse(nodep->op2p())) return p;
|
||||
if (nodep->op3p()) if (AstVar* p = scVarRecurse(nodep->op3p())) return p;
|
||||
@@ -440,6 +444,16 @@ AstVar* AstVar::scVarRecurse(AstNode* nodep) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
string AstVar::mtasksString() const {
|
||||
std::ostringstream os;
|
||||
os<<" all: ";
|
||||
for (MTaskIdSet::const_iterator it = m_mtaskIds.begin();
|
||||
it != m_mtaskIds.end(); ++it) {
|
||||
os<<*it<<" ";
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
|
||||
AstNodeDType* AstNodeDType::dtypeDimensionp(int dimension) {
|
||||
// dimension passed from AstArraySel::dimension
|
||||
// Dimension 0 means the VAR itself, 1 is the closest SEL to the AstVar,
|
||||
@@ -455,17 +469,16 @@ AstNodeDType* AstNodeDType::dtypeDimensionp(int dimension) {
|
||||
// TODO this function should be removed in favor of recursing the dtype(),
|
||||
// as that allows for more complicated data types.
|
||||
int dim = 0;
|
||||
UDEBUGONLY(UASSERT(dynamic_cast<AstNode*>(this),"this should not be NULL"););
|
||||
for (AstNodeDType* dtypep=this; dtypep; ) {
|
||||
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
|
||||
if (AstNodeArrayDType* adtypep = dtypep->castNodeArrayDType()) {
|
||||
if (AstNodeArrayDType* adtypep = VN_CAST(dtypep, NodeArrayDType)) {
|
||||
if ((dim++)==dimension) {
|
||||
return dtypep;
|
||||
}
|
||||
dtypep = adtypep->subDTypep();
|
||||
continue;
|
||||
}
|
||||
else if (AstBasicDType* adtypep = dtypep->castBasicDType()) {
|
||||
else if (AstBasicDType* adtypep = VN_CAST(dtypep, BasicDType)) {
|
||||
// AstBasicDType - nothing below, return null
|
||||
if (adtypep->isRanged()) {
|
||||
if ((dim++) == dimension) {
|
||||
@@ -474,7 +487,7 @@ AstNodeDType* AstNodeDType::dtypeDimensionp(int dimension) {
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
else if (AstNodeClassDType* adtypep = dtypep->castNodeClassDType()) {
|
||||
else if (AstNodeClassDType* adtypep = VN_CAST(dtypep, NodeClassDType)) {
|
||||
if (adtypep->packed()) {
|
||||
if ((dim++) == dimension) {
|
||||
return adtypep;
|
||||
@@ -490,10 +503,9 @@ AstNodeDType* AstNodeDType::dtypeDimensionp(int dimension) {
|
||||
|
||||
uint32_t AstNodeDType::arrayUnpackedElements() {
|
||||
uint32_t entries=1;
|
||||
UDEBUGONLY(UASSERT(dynamic_cast<AstNode*>(this),"this should not be NULL"););
|
||||
for (AstNodeDType* dtypep=this; dtypep; ) {
|
||||
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
|
||||
if (AstUnpackArrayDType* adtypep = dtypep->castUnpackArrayDType()) {
|
||||
if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
entries *= adtypep->elementsConst();
|
||||
dtypep = adtypep->subDTypep();
|
||||
}
|
||||
@@ -505,20 +517,19 @@ uint32_t AstNodeDType::arrayUnpackedElements() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
pair<uint32_t,uint32_t> AstNodeDType::dimensions(bool includeBasic) {
|
||||
std::pair<uint32_t,uint32_t> AstNodeDType::dimensions(bool includeBasic) {
|
||||
// How many array dimensions (packed,unpacked) does this Var have?
|
||||
uint32_t packed = 0;
|
||||
uint32_t unpacked = 0;
|
||||
UDEBUGONLY(UASSERT(dynamic_cast<AstNode*>(this),"this should not be NULL"););
|
||||
for (AstNodeDType* dtypep=this; dtypep; ) {
|
||||
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
|
||||
if (AstNodeArrayDType* adtypep = dtypep->castNodeArrayDType()) {
|
||||
if (adtypep->castPackArrayDType()) packed++;
|
||||
if (const AstNodeArrayDType* adtypep = VN_CAST(dtypep, NodeArrayDType)) {
|
||||
if (VN_IS(adtypep, PackArrayDType)) packed++;
|
||||
else unpacked++;
|
||||
dtypep = adtypep->subDTypep();
|
||||
continue;
|
||||
}
|
||||
else if (AstBasicDType* adtypep = dtypep->castBasicDType()) {
|
||||
else if (const AstBasicDType* adtypep = VN_CAST(dtypep, BasicDType)) {
|
||||
if (includeBasic && adtypep->isRanged()) packed++;
|
||||
}
|
||||
break;
|
||||
@@ -538,15 +549,15 @@ int AstNodeDType::widthPow2() const {
|
||||
AstNode* AstArraySel::baseFromp(AstNode* nodep) { ///< What is the base variable (or const) this dereferences?
|
||||
// Else AstArraySel etc; search for the base
|
||||
while (nodep) {
|
||||
if (nodep->castArraySel()) { nodep=nodep->castArraySel()->fromp(); continue; }
|
||||
else if (nodep->castSel()) { nodep=nodep->castSel()->fromp(); continue; }
|
||||
if (VN_IS(nodep, ArraySel)) { nodep=VN_CAST(nodep, ArraySel)->fromp(); continue; }
|
||||
else if (VN_IS(nodep, Sel)) { nodep=VN_CAST(nodep, Sel)->fromp(); continue; }
|
||||
// AstNodeSelPre stashes the associated variable under an ATTROF of AstAttrType::VAR_BASE/MEMBER_BASE so it isn't constified
|
||||
else if (nodep->castAttrOf()) { nodep=nodep->castAttrOf()->fromp(); continue; }
|
||||
else if (nodep->castNodePreSel()) {
|
||||
if (nodep->castNodePreSel()->attrp()) {
|
||||
nodep=nodep->castNodePreSel()->attrp();
|
||||
else if (VN_IS(nodep, AttrOf)) { nodep=VN_CAST(nodep, AttrOf)->fromp(); continue; }
|
||||
else if (VN_IS(nodep, NodePreSel)) {
|
||||
if (VN_CAST(nodep, NodePreSel)->attrp()) {
|
||||
nodep=VN_CAST(nodep, NodePreSel)->attrp();
|
||||
} else {
|
||||
nodep=nodep->castNodePreSel()->lhsp();
|
||||
nodep=VN_CAST(nodep, NodePreSel)->lhsp();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -566,13 +577,15 @@ const char* AstScope::broken() const {
|
||||
void AstScope::cloneRelink() {
|
||||
if (m_aboveScopep && m_aboveScopep->clonep()) m_aboveScopep->clonep();
|
||||
if (m_aboveCellp && m_aboveCellp->clonep()) m_aboveCellp->clonep();
|
||||
if (m_modp && ((AstNode*)m_modp)->clonep()) ((AstNode*)m_modp)->clonep();
|
||||
if (m_modp && static_cast<AstNode*>(m_modp)->clonep()) {
|
||||
static_cast<AstNode*>(m_modp)->clonep();
|
||||
}
|
||||
}
|
||||
|
||||
string AstScope::nameDotless() const {
|
||||
string out = shortName();
|
||||
string::size_type pos;
|
||||
while ((pos=out.find(".")) != string::npos) {
|
||||
while ((pos = out.find('.')) != string::npos) {
|
||||
out.replace(pos, 1, "__");
|
||||
}
|
||||
return out;
|
||||
@@ -580,7 +593,7 @@ string AstScope::nameDotless() const {
|
||||
|
||||
string AstScopeName::scopePrettyNameFormatter(AstText* scopeTextp) const {
|
||||
string out;
|
||||
for (AstText* textp=scopeTextp; textp; textp=textp->nextp()->castText()) {
|
||||
for (AstText* textp=scopeTextp; textp; textp=VN_CAST(textp->nextp(), Text)) {
|
||||
out += textp->text();
|
||||
}
|
||||
// TOP will be replaced by top->name()
|
||||
@@ -592,14 +605,14 @@ string AstScopeName::scopePrettyNameFormatter(AstText* scopeTextp) const {
|
||||
|
||||
string AstScopeName::scopeNameFormatter(AstText* scopeTextp) const {
|
||||
string out;
|
||||
for (AstText* textp=scopeTextp; textp; textp=textp->nextp()->castText()) {
|
||||
for (AstText* textp=scopeTextp; textp; textp=VN_CAST(textp->nextp(), Text)) {
|
||||
out += textp->text();
|
||||
}
|
||||
if (out.substr(0,10) == "__DOT__TOP") out.replace(0,10,"");
|
||||
if (out.substr(0,7) == "__DOT__") out.replace(0,7,"");
|
||||
if (out.substr(0,1) == ".") out.replace(0,1,"");
|
||||
string::size_type pos;
|
||||
while ((pos=out.find(".")) != string::npos) {
|
||||
while ((pos = out.find('.')) != string::npos) {
|
||||
out.replace(pos, 1, "__");
|
||||
}
|
||||
while ((pos=out.find("__DOT__")) != string::npos) {
|
||||
@@ -610,28 +623,28 @@ string AstScopeName::scopeNameFormatter(AstText* scopeTextp) const {
|
||||
|
||||
bool AstSenTree::hasClocked() const {
|
||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=senp->nextp()->castNodeSenItem()) {
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||
if (senp->isClocked()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool AstSenTree::hasSettle() const {
|
||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=senp->nextp()->castNodeSenItem()) {
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||
if (senp->isSettle()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool AstSenTree::hasInitial() const {
|
||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=senp->nextp()->castNodeSenItem()) {
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||
if (senp->isInitial()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool AstSenTree::hasCombo() const {
|
||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=senp->nextp()->castNodeSenItem()) {
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||
if (senp->isCombo()) return true;
|
||||
}
|
||||
return false;
|
||||
@@ -640,7 +653,7 @@ bool AstSenTree::hasCombo() const {
|
||||
void AstTypeTable::clearCache() {
|
||||
// When we mass-change widthMin in V3WidthCommit, we need to correct the table.
|
||||
// Just clear out the maps; the search functions will be used to rebuild the map
|
||||
for (int i=0; i<(int)(AstBasicDTypeKwd::_ENUM_MAX); ++i) {
|
||||
for (int i=0; i < static_cast<int>(AstBasicDTypeKwd::_ENUM_MAX); ++i) {
|
||||
m_basicps[i] = NULL;
|
||||
}
|
||||
for (int isbit=0; isbit<_IDX0_MAX; ++isbit) {
|
||||
@@ -652,7 +665,7 @@ void AstTypeTable::clearCache() {
|
||||
m_detailedMap.clear();
|
||||
// Clear generic()'s so dead detection will work
|
||||
for (AstNode* nodep = typesp(); nodep; nodep=nodep->nextp()) {
|
||||
if (AstBasicDType* bdtypep = nodep->castBasicDType()) {
|
||||
if (AstBasicDType* bdtypep = VN_CAST(nodep, BasicDType)) {
|
||||
bdtypep->generic(false);
|
||||
}
|
||||
}
|
||||
@@ -662,7 +675,7 @@ void AstTypeTable::repairCache() {
|
||||
// After we mass-change widthMin in V3WidthCommit, we need to correct the table.
|
||||
clearCache();
|
||||
for (AstNode* nodep = typesp(); nodep; nodep=nodep->nextp()) {
|
||||
if (AstBasicDType* bdtypep = nodep->castBasicDType()) {
|
||||
if (AstBasicDType* bdtypep = VN_CAST(nodep, BasicDType)) {
|
||||
(void)findInsertSameDType(bdtypep);
|
||||
}
|
||||
}
|
||||
@@ -689,8 +702,8 @@ AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kw
|
||||
if (kwd == AstBasicDTypeKwd::LOGIC) idx = IDX0_LOGIC;
|
||||
else if (kwd == AstBasicDTypeKwd::BIT) idx = IDX0_BIT;
|
||||
else fl->v3fatalSrc("Bad kwd for findLogicBitDType");
|
||||
pair<int,int> widths = make_pair(width,widthMin);
|
||||
LogicMap& mapr = m_logicMap[idx][(int)numeric];
|
||||
std::pair<int,int> widths = make_pair(width,widthMin);
|
||||
LogicMap& mapr = m_logicMap[idx][static_cast<int>(numeric)];
|
||||
LogicMap::const_iterator it = mapr.find(widths);
|
||||
if (it != mapr.end()) return it->second;
|
||||
//
|
||||
@@ -792,71 +805,72 @@ void AstWhile::addNextStmt(AstNode* newp, AstNode* belowp) {
|
||||
//======================================================================
|
||||
// Per-type Debugging
|
||||
|
||||
void AstNode::dump(ostream& str) {
|
||||
str<<typeName()<<" "<<(void*)this
|
||||
//<<" "<<(void*)this->m_backp
|
||||
<<" <e"<<dec<<editCount()
|
||||
void AstNode::dump(std::ostream& str) {
|
||||
str<<typeName()<<" "<<cvtToHex(this)
|
||||
//<<" "<<cvtToHex(this)->m_backp
|
||||
<<" <e"<<std::dec<<editCount()
|
||||
<<((editCount()>=editCountLast())?"#>":">")
|
||||
<<" {"<<fileline()->filenameLetters()<<dec<<fileline()->lineno()<<"}";
|
||||
if (user1p()) str<<" u1="<<(void*)user1p();
|
||||
if (user2p()) str<<" u2="<<(void*)user2p();
|
||||
if (user3p()) str<<" u3="<<(void*)user3p();
|
||||
if (user4p()) str<<" u4="<<(void*)user4p();
|
||||
if (user5p()) str<<" u5="<<(void*)user5p();
|
||||
<<" {"<<fileline()->filenameLetters()<<std::dec<<fileline()->lineno()<<"}";
|
||||
if (user1p()) str<<" u1="<<cvtToHex(user1p());
|
||||
if (user2p()) str<<" u2="<<cvtToHex(user2p());
|
||||
if (user3p()) str<<" u3="<<cvtToHex(user3p());
|
||||
if (user4p()) str<<" u4="<<cvtToHex(user4p());
|
||||
if (user5p()) str<<" u5="<<cvtToHex(user5p());
|
||||
if (hasDType()) {
|
||||
// Final @ so less likely to by accident read it as a nodep
|
||||
if (dtypep()==this) str<<" @dt="<<"this@";
|
||||
else str<<" @dt="<<(void*)dtypep()<<"@"; // Final @ so less likely to by accident think it's nodep
|
||||
else str<<" @dt="<<cvtToHex(dtypep())<<"@";
|
||||
if (AstNodeDType* dtp = dtypep()) {
|
||||
dtp->dumpSmall(str);
|
||||
}
|
||||
} else { // V3Broken will throw an error
|
||||
if (dtypep()) str<<" %Error-dtype-exp=null,got="<<(void*)dtypep();
|
||||
if (dtypep()) str<<" %Error-dtype-exp=null,got="<<cvtToHex(dtypep());
|
||||
}
|
||||
if (name()!="") {
|
||||
if (castConst()) str<<" "<<name(); // Already quoted
|
||||
if (VN_IS(this, Const)) str<<" "<<name(); // Already quoted
|
||||
else str<<" "<<V3Number::quoteNameControls(name());
|
||||
}
|
||||
}
|
||||
|
||||
void AstAlways::dump(ostream& str) {
|
||||
void AstAlways::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (keyword() != VAlwaysKwd::ALWAYS) str<<" ["<<keyword().ascii()<<"]";
|
||||
}
|
||||
|
||||
void AstAttrOf::dump(ostream& str) {
|
||||
void AstAttrOf::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" ["<<attrType().ascii()<<"]";
|
||||
}
|
||||
void AstBasicDType::dump(ostream& str) {
|
||||
void AstBasicDType::dump(std::ostream& str) {
|
||||
this->AstNodeDType::dump(str);
|
||||
str<<" kwd="<<keyword().ascii();
|
||||
if (isRanged() && !rangep()) str<<" range=["<<left()<<":"<<right()<<"]";
|
||||
}
|
||||
void AstCCast::dump(ostream& str) {
|
||||
void AstCCast::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" sz"<<size();
|
||||
}
|
||||
void AstCell::dump(ostream& str) {
|
||||
void AstCell::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (recursive()) str<<" [RECURSIVE]";
|
||||
if (modp()) { str<<" -> "; modp()->dump(str); }
|
||||
else { str<<" ->UNLINKED:"<<modName(); }
|
||||
}
|
||||
void AstCellInline::dump(ostream& str) {
|
||||
void AstCellInline::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" -> "<<origModName();
|
||||
}
|
||||
void AstDisplay::dump(ostream& str) {
|
||||
void AstDisplay::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
//str<<" "<<displayType().ascii();
|
||||
}
|
||||
void AstEnumItemRef::dump(ostream& str) {
|
||||
void AstEnumItemRef::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" -> ";
|
||||
if (itemp()) { itemp()->dump(str); }
|
||||
else { str<<"UNLINKED"; }
|
||||
}
|
||||
void AstIfaceRefDType::dump(ostream& str) {
|
||||
void AstIfaceRefDType::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (cellName()!="") { str<<" cell="<<cellName(); }
|
||||
if (ifaceName()!="") { str<<" if="<<ifaceName(); }
|
||||
@@ -865,62 +879,62 @@ void AstIfaceRefDType::dump(ostream& str) {
|
||||
else if (ifacep()) { str<<" -> "; ifacep()->dump(str); }
|
||||
else { str<<" -> UNLINKED"; }
|
||||
}
|
||||
void AstIfaceRefDType::dumpSmall(ostream& str) {
|
||||
void AstIfaceRefDType::dumpSmall(std::ostream& str) {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str<<"iface";
|
||||
}
|
||||
void AstJumpGo::dump(ostream& str) {
|
||||
void AstJumpGo::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" -> ";
|
||||
if (labelp()) { labelp()->dump(str); }
|
||||
else { str<<"%Error:UNLINKED"; }
|
||||
}
|
||||
void AstModportFTaskRef::dump(ostream& str) {
|
||||
void AstModportFTaskRef::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (isExport()) str<<" EXPORT";
|
||||
if (isImport()) str<<" IMPORT";
|
||||
if (ftaskp()) { str<<" -> "; ftaskp()->dump(str); }
|
||||
else { str<<" -> UNLINKED"; }
|
||||
}
|
||||
void AstModportVarRef::dump(ostream& str) {
|
||||
void AstModportVarRef::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" "<<varType();
|
||||
if (varp()) { str<<" -> "; varp()->dump(str); }
|
||||
else { str<<" -> UNLINKED"; }
|
||||
}
|
||||
void AstPin::dump(ostream& str) {
|
||||
void AstPin::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (modVarp()) { str<<" -> "; modVarp()->dump(str); }
|
||||
else { str<<" ->UNLINKED"; }
|
||||
if (svImplicit()) str<<" [.SV]";
|
||||
}
|
||||
void AstTypedef::dump(ostream& str) {
|
||||
void AstTypedef::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (attrPublic()) str<<" [PUBLIC]";
|
||||
}
|
||||
void AstRange::dump(ostream& str) {
|
||||
void AstRange::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (littleEndian()) str<<" [LITTLE]";
|
||||
}
|
||||
void AstRefDType::dump(ostream& str) {
|
||||
void AstRefDType::dump(std::ostream& str) {
|
||||
this->AstNodeDType::dump(str);
|
||||
if (defp()) { str<<" -> "; defp()->dump(str); }
|
||||
else { str<<" -> UNLINKED"; }
|
||||
}
|
||||
void AstNodeClassDType::dump(ostream& str) {
|
||||
void AstNodeClassDType::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (packed()) str<<" [PACKED]";
|
||||
if (isFourstate()) str<<" [4STATE]";
|
||||
}
|
||||
void AstNodeDType::dump(ostream& str) {
|
||||
void AstNodeDType::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (generic()) str<<" [GENERIC]";
|
||||
if (AstNodeDType* dtp = virtRefDTypep()) {
|
||||
str<<" refdt="<<(void*)(dtp);
|
||||
str<<" refdt="<<cvtToHex(dtp);
|
||||
dtp->dumpSmall(str);
|
||||
}
|
||||
}
|
||||
void AstNodeDType::dumpSmall(ostream& str) {
|
||||
void AstNodeDType::dumpSmall(std::ostream& str) {
|
||||
str<<"("
|
||||
<<(generic()?"G/":"")
|
||||
<<((isSigned()&&!isDouble())?"s":"")
|
||||
@@ -933,16 +947,16 @@ void AstNodeDType::dumpSmall(ostream& str) {
|
||||
if (!widthSized()) str<<"/"<<widthMin();
|
||||
str<<")";
|
||||
}
|
||||
void AstNodeArrayDType::dumpSmall(ostream& str) {
|
||||
void AstNodeArrayDType::dumpSmall(std::ostream& str) {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
if (castPackArrayDType()) str<<"p"; else str<<"u";
|
||||
if (VN_IS(this, PackArrayDType)) str<<"p"; else str<<"u";
|
||||
str<<declRange();
|
||||
}
|
||||
void AstNodeArrayDType::dump(ostream& str) {
|
||||
void AstNodeArrayDType::dump(std::ostream& str) {
|
||||
this->AstNodeDType::dump(str);
|
||||
str<<" "<<declRange();
|
||||
}
|
||||
void AstNodeModule::dump(ostream& str) {
|
||||
void AstNodeModule::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" L"<<level();
|
||||
if (modPublic()) str<<" [P]";
|
||||
@@ -951,33 +965,38 @@ void AstNodeModule::dump(ostream& str) {
|
||||
if (recursiveClone()) str<<" [RECURSIVE-CLONE]";
|
||||
else if (recursive()) str<<" [RECURSIVE]";
|
||||
}
|
||||
void AstPackageExport::dump(ostream& str) {
|
||||
void AstPackageExport::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" -> "<<packagep();
|
||||
}
|
||||
void AstPackageImport::dump(ostream& str) {
|
||||
void AstPackageImport::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" -> "<<packagep();
|
||||
}
|
||||
void AstSel::dump(ostream& str) {
|
||||
void AstSel::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (declRange().ranged()) {
|
||||
str<<" decl"<<declRange()<<"]";
|
||||
if (declElWidth()!=1) str<<"/"<<declElWidth();
|
||||
}
|
||||
}
|
||||
void AstSliceSel::dump(ostream& str) {
|
||||
void AstSliceSel::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (declRange().ranged()) {
|
||||
str<<" decl"<<declRange();
|
||||
}
|
||||
}
|
||||
void AstTypeTable::dump(ostream& str) {
|
||||
void AstMTaskBody::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
for (int i=0; i<(int)(AstBasicDTypeKwd::_ENUM_MAX); ++i) {
|
||||
str<<" ";
|
||||
m_execMTaskp->dump(str);
|
||||
}
|
||||
void AstTypeTable::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
for (int i=0; i < static_cast<int>(AstBasicDTypeKwd::_ENUM_MAX); ++i) {
|
||||
if (AstBasicDType* subnodep=m_basicps[i]) {
|
||||
str<<endl; // Newline from caller, so newline first
|
||||
str<<"\t\t"<<setw(8)<<AstBasicDTypeKwd(i).ascii();
|
||||
str<<"\t\t"<<std::setw(8)<<AstBasicDTypeKwd(i).ascii();
|
||||
str<<" -> ";
|
||||
subnodep->dump(str);
|
||||
}
|
||||
@@ -988,10 +1007,10 @@ void AstTypeTable::dump(ostream& str) {
|
||||
for (LogicMap::const_iterator it = mapr.begin(); it != mapr.end(); ++it) {
|
||||
AstBasicDType* dtypep = it->second;
|
||||
str<<endl; // Newline from caller, so newline first
|
||||
stringstream nsstr;
|
||||
std::stringstream nsstr;
|
||||
nsstr<<(isbit?"bw":"lw")
|
||||
<<it->first.first<<"/"<<it->first.second;
|
||||
str<<"\t\t"<<setw(8)<<nsstr.str();
|
||||
str<<"\t\t"<<std::setw(8)<<nsstr.str();
|
||||
if (issigned) str<<" s"; else str<<" u";
|
||||
str<<" -> ";
|
||||
dtypep->dump(str);
|
||||
@@ -1009,19 +1028,19 @@ void AstTypeTable::dump(ostream& str) {
|
||||
}
|
||||
// Note get newline from caller too.
|
||||
}
|
||||
void AstUnsizedArrayDType::dumpSmall(ostream& str) {
|
||||
void AstUnsizedArrayDType::dumpSmall(std::ostream& str) {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str<<"[]";
|
||||
}
|
||||
void AstVarScope::dump(ostream& str) {
|
||||
void AstVarScope::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (isCircular()) str<<" [CIRC]";
|
||||
if (varp()) { str<<" -> "; varp()->dump(str); }
|
||||
else { str<<" ->UNLINKED"; }
|
||||
}
|
||||
void AstVarXRef::dump(ostream& str) {
|
||||
void AstVarXRef::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (packagep()) { str<<" pkg="<<(void*)packagep(); }
|
||||
if (packagep()) { str<<" pkg="<<cvtToHex(packagep()); }
|
||||
if (lvalue()) str<<" [LV] => ";
|
||||
else str<<" [RV] <- ";
|
||||
str<<".="<<dotted()<<" ";
|
||||
@@ -1030,16 +1049,16 @@ void AstVarXRef::dump(ostream& str) {
|
||||
else if (varp()) { varp()->dump(str); }
|
||||
else { str<<"UNLINKED"; }
|
||||
}
|
||||
void AstVarRef::dump(ostream& str) {
|
||||
void AstVarRef::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (packagep()) { str<<" pkg="<<(void*)packagep(); }
|
||||
if (packagep()) { str<<" pkg="<<cvtToHex(packagep()); }
|
||||
if (lvalue()) str<<" [LV] => ";
|
||||
else str<<" [RV] <- ";
|
||||
if (varScopep()) { varScopep()->dump(str); }
|
||||
else if (varp()) { varp()->dump(str); }
|
||||
else { str<<"UNLINKED"; }
|
||||
}
|
||||
void AstVar::dump(ostream& str) {
|
||||
void AstVar::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (isSc()) str<<" [SC]";
|
||||
if (isPrimaryIO()) str<<(isInout()?" [PIO]":(isInput()?" [PI]":" [PO]"));
|
||||
@@ -1063,43 +1082,43 @@ void AstVar::dump(ostream& str) {
|
||||
if (!attrClocker().unknown()) str<<" ["<<attrClocker().ascii()<<"] ";
|
||||
str<<" "<<varType();
|
||||
}
|
||||
void AstSenTree::dump(ostream& str) {
|
||||
void AstSenTree::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (isMulti()) str<<" [MULTI]";
|
||||
}
|
||||
void AstSenItem::dump(ostream& str) {
|
||||
void AstSenItem::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" ["<<edgeType().ascii()<<"]";
|
||||
}
|
||||
void AstParseRef::dump(ostream& str) {
|
||||
void AstParseRef::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" ["<<expect().ascii()<<"]";
|
||||
}
|
||||
void AstPackageRef::dump(ostream& str) {
|
||||
void AstPackageRef::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (packagep()) { str<<" pkg="<<(void*)packagep(); }
|
||||
if (packagep()) { str<<" pkg="<<cvtToHex(packagep()); }
|
||||
str<<" -> ";
|
||||
if (packagep()) { packagep()->dump(str); }
|
||||
else { str<<"UNLINKED"; }
|
||||
}
|
||||
void AstDot::dump(ostream& str) {
|
||||
void AstDot::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
}
|
||||
void AstActive::dump(ostream& str) {
|
||||
void AstActive::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" => ";
|
||||
if (sensesp()) { sensesp()->dump(str); }
|
||||
else { str<<"UNLINKED"; }
|
||||
}
|
||||
void AstNodeFTaskRef::dump(ostream& str) {
|
||||
void AstNodeFTaskRef::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (packagep()) { str<<" pkg="<<(void*)packagep(); }
|
||||
if (packagep()) { str<<" pkg="<<cvtToHex(packagep()); }
|
||||
str<<" -> ";
|
||||
if (dotted()!="") { str<<".="<<dotted()<<" "; }
|
||||
if (taskp()) { taskp()->dump(str); }
|
||||
else { str<<"UNLINKED"; }
|
||||
}
|
||||
void AstNodeFTask::dump(ostream& str) {
|
||||
void AstNodeFTask::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (taskPublic()) str<<" [PUBLIC]";
|
||||
if (prototype()) str<<" [PROTOTYPE]";
|
||||
@@ -1109,57 +1128,57 @@ void AstNodeFTask::dump(ostream& str) {
|
||||
if (dpiOpenParent()) str<<" [DPIOPENPARENT]";
|
||||
if ((dpiImport() || dpiExport()) && cname()!=name()) str<<" [c="<<cname()<<"]";
|
||||
}
|
||||
void AstBegin::dump(ostream& str) {
|
||||
void AstBegin::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (unnamed()) str<<" [UNNAMED]";
|
||||
if (generate()) str<<" [GEN]";
|
||||
if (genforp()) str<<" [GENFOR]";
|
||||
}
|
||||
void AstCoverDecl::dump(ostream& str) {
|
||||
void AstCoverDecl::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (this->dataDeclNullp()) {
|
||||
str<<" -> ";
|
||||
this->dataDeclNullp()->dump(str);
|
||||
} else {
|
||||
if (binNum()) { str<<" bin"<<dec<<binNum(); }
|
||||
if (binNum()) { str<<" bin"<<std::dec<<binNum(); }
|
||||
}
|
||||
}
|
||||
void AstCoverInc::dump(ostream& str) {
|
||||
void AstCoverInc::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" -> ";
|
||||
if (declp()) { declp()->dump(str); }
|
||||
else { str<<"%Error:UNLINKED"; }
|
||||
}
|
||||
void AstTraceInc::dump(ostream& str) {
|
||||
void AstTraceInc::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" -> ";
|
||||
if (declp()) { declp()->dump(str); }
|
||||
else { str<<"%Error:UNLINKED"; }
|
||||
}
|
||||
void AstNodeText::dump(ostream& str) {
|
||||
void AstNodeText::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
string out = text();
|
||||
string::size_type pos;
|
||||
if ((pos = out.find("\n")) != string::npos) {
|
||||
if ((pos = out.find('\n')) != string::npos) {
|
||||
out.erase(pos,out.length()-pos);
|
||||
out += "...";
|
||||
}
|
||||
str<<" \""<<out<<"\"";
|
||||
}
|
||||
|
||||
void AstCFile::dump(ostream& str) {
|
||||
void AstCFile::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (source()) str<<" [SRC]";
|
||||
if (slow()) str<<" [SLOW]";
|
||||
}
|
||||
void AstCCall::dump(ostream& str) {
|
||||
void AstCCall::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (funcp()) {
|
||||
str<<" "<<funcp()->name()<<" => ";
|
||||
funcp()->dump(str);
|
||||
}
|
||||
}
|
||||
void AstCFunc::dump(ostream& str) {
|
||||
void AstCFunc::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (slow()) str<<" [SLOW]";
|
||||
if (pure()) str<<" [PURE]";
|
||||
|
||||
+308
-177
File diff suppressed because it is too large
Load Diff
+20
-25
@@ -31,17 +31,16 @@
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <unistd.h>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Begin.h"
|
||||
#include "V3Inst.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <vector>
|
||||
|
||||
//######################################################################
|
||||
|
||||
class BeginState {
|
||||
@@ -77,17 +76,13 @@ private:
|
||||
int m_ifDepth; // Current if depth
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
m_modp = nodep;
|
||||
m_repeatNum = 0;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstNodeFTask* nodep) {
|
||||
@@ -107,7 +102,7 @@ private:
|
||||
m_namedScope = "";
|
||||
m_unnamedScope = "";
|
||||
m_ftaskp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_ftaskp = NULL;
|
||||
}
|
||||
m_namedScope = oldScope;
|
||||
@@ -143,7 +138,7 @@ private:
|
||||
}
|
||||
|
||||
// Remap var names and replace lower Begins
|
||||
nodep->stmtsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
if (nodep->genforp()) nodep->v3fatalSrc("GENFORs should have been expanded earlier");
|
||||
}
|
||||
m_namedScope = oldScope;
|
||||
@@ -184,7 +179,7 @@ private:
|
||||
nodep->unlinkFrBack();
|
||||
m_modp->addStmtp(nodep);
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstVarXRef* nodep) {
|
||||
UINFO(9, " VARXREF "<<nodep<<endl);
|
||||
@@ -201,15 +196,15 @@ private:
|
||||
// To keep correct visual order, must add before other Text's
|
||||
AstNode* afterp = nodep->scopeAttrp();
|
||||
if (afterp) afterp->unlinkFrBackWithNext();
|
||||
nodep->scopeAttrp(new AstText(nodep->fileline(), (string)"__DOT__"+m_namedScope));
|
||||
nodep->scopeAttrp(new AstText(nodep->fileline(), string("__DOT__")+m_namedScope));
|
||||
if (afterp) nodep->scopeAttrp(afterp);
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCoverDecl* nodep) {
|
||||
// Don't need to fix path in coverage statements, they're not under
|
||||
// any BEGINs, but V3Coverage adds them all under the module itself.
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
// VISITORS - LINT CHECK
|
||||
virtual void visit(AstIf* nodep) { // Note not AstNodeIf; other types don't get covered
|
||||
@@ -223,11 +218,11 @@ private:
|
||||
nodep->fileline()->modifyWarnOff(V3ErrorCode::IFDEPTH, true); // Warn only once
|
||||
m_ifDepth = -1;
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_ifDepth = prevIfDepth;
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
@@ -237,7 +232,7 @@ public:
|
||||
m_ftaskp = NULL;
|
||||
m_repeatNum = 0;
|
||||
m_ifDepth = 0;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~BeginVisitor() {}
|
||||
};
|
||||
@@ -257,14 +252,14 @@ private:
|
||||
UINFO(9, " relinkFTask "<<nodep<<endl);
|
||||
nodep->name(nodep->taskp()->name());
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
if (nodep->varp()->user1()) { // It was converted
|
||||
UINFO(9, " relinVarRef "<<nodep<<endl);
|
||||
nodep->name(nodep->varp()->name());
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstIfaceRefDType* nodep) {
|
||||
// May have changed cell names
|
||||
@@ -272,16 +267,16 @@ private:
|
||||
UINFO(8," IFACEREFDTYPE "<<nodep<<endl);
|
||||
if (nodep->cellp()) nodep->cellName(nodep->cellp()->name());
|
||||
UINFO(8," rename to "<<nodep<<endl);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
BeginRelinkVisitor(AstNetlist* nodep, BeginState*) {
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~BeginRelinkVisitor() {}
|
||||
};
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
|
||||
#ifndef _V3BEGIN_H_
|
||||
#define _V3BEGIN_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
|
||||
+13
-18
@@ -29,15 +29,14 @@
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <unistd.h>
|
||||
#include <map>
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Branch.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
#include <cstdarg>
|
||||
#include <map>
|
||||
|
||||
//######################################################################
|
||||
// Branch state, as a visitor of each AstNode
|
||||
|
||||
@@ -49,7 +48,7 @@ private:
|
||||
AstUser1InUse m_inuser1;
|
||||
|
||||
// TYPES
|
||||
typedef vector<AstCFunc*> CFuncVec;
|
||||
typedef std::vector<AstCFunc*> CFuncVec;
|
||||
|
||||
// STATE
|
||||
int m_likely; // Excuses for branch likely taken
|
||||
@@ -57,11 +56,7 @@ private:
|
||||
CFuncVec m_cfuncsp; // List of all tasks
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void reset() {
|
||||
m_likely = false;
|
||||
@@ -82,12 +77,12 @@ private:
|
||||
{
|
||||
// Do if
|
||||
reset();
|
||||
nodep->ifsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->ifsp());
|
||||
int ifLikely = m_likely;
|
||||
int ifUnlikely = m_unlikely;
|
||||
// Do else
|
||||
reset();
|
||||
nodep->elsesp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
int elseLikely = m_likely;
|
||||
int elseUnlikely = m_unlikely;
|
||||
// Compute
|
||||
@@ -104,16 +99,16 @@ private:
|
||||
virtual void visit(AstCCall* nodep) {
|
||||
checkUnlikely(nodep);
|
||||
nodep->funcp()->user1Inc();
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
checkUnlikely(nodep);
|
||||
m_cfuncsp.push_back(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
checkUnlikely(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
// METHODS
|
||||
@@ -130,7 +125,7 @@ public:
|
||||
// CONSTUCTORS
|
||||
explicit BranchVisitor(AstNetlist* nodep) {
|
||||
reset();
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
calc_tasks();
|
||||
}
|
||||
virtual ~BranchVisitor() {}
|
||||
@@ -139,7 +134,7 @@ public:
|
||||
//######################################################################
|
||||
// Branch class functions
|
||||
|
||||
void V3Branch::branchAll(AstNetlist* rootp) {
|
||||
void V3Branch::branchAll(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
BranchVisitor visitor (rootp);
|
||||
BranchVisitor visitor (nodep);
|
||||
}
|
||||
|
||||
+3
-1
@@ -20,8 +20,10 @@
|
||||
|
||||
#ifndef _V3BRANCH_H_
|
||||
#define _V3BRANCH_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
@@ -30,7 +32,7 @@
|
||||
class V3Branch {
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
static void branchAll(AstNetlist* rootp);
|
||||
static void branchAll(AstNetlist* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+26
-22
@@ -27,11 +27,6 @@
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <unistd.h>
|
||||
#include <algorithm>
|
||||
#include VL_INCLUDE_UNORDERED_MAP
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Broken.h"
|
||||
@@ -40,6 +35,10 @@
|
||||
// This visitor does not edit nodes, and is called at error-exit, so should use constant iterators
|
||||
#include "V3AstConstOnly.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include VL_INCLUDE_UNORDERED_MAP
|
||||
|
||||
//######################################################################
|
||||
|
||||
class BrokenTable : public AstNVisitor {
|
||||
@@ -59,10 +58,11 @@ public:
|
||||
// METHODS
|
||||
static void deleted(const AstNode* nodep) {
|
||||
// Called by operator delete on any node - only if VL_LEAK_CHECKS
|
||||
if (debug()>=9) cout<<"-nodeDel: "<<(void*)(nodep)<<endl;
|
||||
if (debug()>=9) cout<<"-nodeDel: "<<cvtToHex(nodep)<<endl;
|
||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||
if (iter==s_nodes.end() || !(iter->second & FLAG_ALLOCATED)) {
|
||||
((AstNode*)(nodep))->v3fatalSrc("Deleting AstNode object that was never tracked or already deleted");
|
||||
reinterpret_cast<const AstNode*>(nodep)
|
||||
->v3fatalSrc("Deleting AstNode object that was never tracked or already deleted");
|
||||
}
|
||||
if (iter!=s_nodes.end()) s_nodes.erase(iter);
|
||||
}
|
||||
@@ -72,7 +72,7 @@ public:
|
||||
#endif
|
||||
static void addNewed(const AstNode* nodep) {
|
||||
// Called by operator new on any node - only if VL_LEAK_CHECKS
|
||||
if (debug()>=9) cout<<"-nodeNew: "<<(void*)(nodep)<<endl;
|
||||
if (debug()>=9) cout<<"-nodeNew: "<<cvtToHex(nodep)<<endl;
|
||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||
if (iter!=s_nodes.end() || (iter->second & FLAG_ALLOCATED)) {
|
||||
nodep->v3fatalSrc("Newing AstNode object that is already allocated");
|
||||
@@ -155,9 +155,11 @@ public:
|
||||
// Use only AstNode::dump instead of the virtual one, as there
|
||||
// may be varp() and other cross links that are bad.
|
||||
if (v3Global.opt.debugCheck()) {
|
||||
cerr<<"%Error: LeakedNode"<<(it->first->backp()?"Back: ":": ");
|
||||
((AstNode*)(it->first))->AstNode::dump(cerr);
|
||||
cerr<<endl;
|
||||
std::cerr<<"%Error: LeakedNode"<<(it->first->backp()?"Back: ":": ");
|
||||
AstNode* rawp = const_cast<AstNode*>
|
||||
(static_cast<const AstNode*>(it->first));
|
||||
rawp->AstNode::dump(std::cerr);
|
||||
std::cerr<<endl;
|
||||
V3Error::incErrors();
|
||||
}
|
||||
it->second |= FLAG_LEAKED;
|
||||
@@ -193,7 +195,7 @@ private:
|
||||
// METHODS
|
||||
void processAndIterate(AstNode* nodep) {
|
||||
BrokenTable::addInTree(nodep, nodep->maybePointedTo());
|
||||
nodep->iterateChildrenConst(*this);
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
// VISITORS
|
||||
virtual void visit(AstNode* nodep) {
|
||||
@@ -202,7 +204,7 @@ private:
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit BrokenMarkVisitor(AstNetlist* nodep) {
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~BrokenMarkVisitor() {}
|
||||
};
|
||||
@@ -212,7 +214,7 @@ public:
|
||||
|
||||
class BrokenCheckVisitor : public AstNVisitor {
|
||||
private:
|
||||
void checkWidthMin(AstNode* nodep) {
|
||||
void checkWidthMin(const AstNode* nodep) {
|
||||
if (nodep->width() != nodep->widthMin()
|
||||
&& v3Global.widthMinUsage()==VWidthMinUsage::MATCHES_WIDTH) {
|
||||
nodep->v3fatalSrc("Width != WidthMin");
|
||||
@@ -225,9 +227,11 @@ private:
|
||||
}
|
||||
if (nodep->dtypep()) {
|
||||
if (!nodep->dtypep()->brokeExists()) {
|
||||
nodep->v3fatalSrc("Broken link in node->dtypep() to "<<(void*)nodep->dtypep());
|
||||
} else if (!nodep->dtypep()->castNodeDType()) {
|
||||
nodep->v3fatalSrc("Non-dtype link in node->dtypep() to "<<(void*)nodep->dtypep());
|
||||
nodep->v3fatalSrc("Broken link in node->dtypep() to "
|
||||
<<cvtToHex(nodep->dtypep()));
|
||||
} else if (!VN_IS(nodep->dtypep(), NodeDType)) {
|
||||
nodep->v3fatalSrc("Non-dtype link in node->dtypep() to "
|
||||
<<cvtToHex(nodep->dtypep()));
|
||||
}
|
||||
}
|
||||
if (v3Global.assertDTypesResolved()) {
|
||||
@@ -237,18 +241,18 @@ private:
|
||||
if (nodep->dtypep()) nodep->v3fatalSrc("DType on node without hasDType(): "<<nodep->prettyTypeName());
|
||||
}
|
||||
if (nodep->getChildDTypep()) nodep->v3fatalSrc("childDTypep() non-null on node after should have removed");
|
||||
if (AstNodeDType* dnodep = nodep->castNodeDType()) checkWidthMin(dnodep);
|
||||
if (const AstNodeDType* dnodep = VN_CAST(nodep, NodeDType)) checkWidthMin(dnodep);
|
||||
}
|
||||
checkWidthMin(nodep);
|
||||
nodep->iterateChildrenConst(*this);
|
||||
iterateChildrenConst(nodep);
|
||||
BrokenTable::setUnder(nodep,false);
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep) {
|
||||
processAndIterate(nodep);
|
||||
if (v3Global.assertDTypesResolved()
|
||||
&& nodep->brokeLhsMustBeLvalue()
|
||||
&& nodep->lhsp()->castNodeVarRef()
|
||||
&& !nodep->lhsp()->castNodeVarRef()->lvalue()) {
|
||||
&& VN_IS(nodep->lhsp(), NodeVarRef)
|
||||
&& !VN_CAST(nodep->lhsp(), NodeVarRef)->lvalue()) {
|
||||
nodep->v3fatalSrc("Assignment LHS is not an lvalue");
|
||||
}
|
||||
}
|
||||
@@ -258,7 +262,7 @@ private:
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit BrokenCheckVisitor(AstNetlist* nodep) {
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~BrokenCheckVisitor() {}
|
||||
};
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
|
||||
#ifndef _V3BROKEN_H_
|
||||
#define _V3BROKEN_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
|
||||
+15
-14
@@ -27,20 +27,20 @@
|
||||
//
|
||||
// This transformation honors outputSplitCFuncs.
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <unistd.h>
|
||||
#include <cmath>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3CCtors.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdarg>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
class V3CCtorsVisitor {
|
||||
private:
|
||||
string m_basename;
|
||||
@@ -113,9 +113,9 @@ void V3CCtors::evalAsserts() {
|
||||
funcp->ifdef("VL_DEBUG");
|
||||
modp->addStmtp(funcp);
|
||||
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
||||
if (AstVar* varp = np->castVar()) {
|
||||
if (AstVar* varp = VN_CAST(np, Var)) {
|
||||
if (varp->isPrimaryIn() && !varp->isSc()) {
|
||||
if (AstBasicDType* basicp = varp->dtypeSkipRefp()->castBasicDType()) {
|
||||
if (AstBasicDType* basicp = VN_CAST(varp->dtypeSkipRefp(), BasicDType)) {
|
||||
int storedWidth = basicp->widthAlignBytes() * 8;
|
||||
int lastWordWidth = varp->width() % storedWidth;
|
||||
if (lastWordWidth != 0) {
|
||||
@@ -145,13 +145,14 @@ void V3CCtors::evalAsserts() {
|
||||
void V3CCtors::cctorsAll() {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
evalAsserts();
|
||||
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=modp->nextp()->castNodeModule()) {
|
||||
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=VN_CAST(modp->nextp(), NodeModule)) {
|
||||
// Process each module in turn
|
||||
{
|
||||
V3CCtorsVisitor var_reset (modp, "_ctor_var_reset");
|
||||
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
||||
if (AstVar* varp = np->castVar()) {
|
||||
if (!varp->isIfaceParent() && !varp->isIfaceRef()) {
|
||||
if (AstVar* varp = VN_CAST(np, Var)) {
|
||||
if (!varp->isIfaceParent() && !varp->isIfaceRef()
|
||||
&& !varp->noReset()) {
|
||||
var_reset.add(new AstCReset(varp->fileline(), new AstVarRef(varp->fileline(), varp, true)));
|
||||
}
|
||||
}
|
||||
@@ -160,9 +161,9 @@ void V3CCtors::cctorsAll() {
|
||||
if (v3Global.opt.coverage()) {
|
||||
V3CCtorsVisitor configure_coverage
|
||||
(modp, "_configure_coverage", EmitCBaseVisitor::symClassVar()+ ", bool first", "vlSymsp, first",
|
||||
"if (0 && vlSymsp && first) {} // Prevent unused\n");
|
||||
"if (0 && vlSymsp && first) {} // Prevent unused\n");
|
||||
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
||||
if (AstCoverDecl* coverp = np->castCoverDecl()) {
|
||||
if (AstCoverDecl* coverp = VN_CAST(np, CoverDecl)) {
|
||||
AstNode* backp = coverp->backp();
|
||||
coverp->unlinkFrBack();
|
||||
configure_coverage.add(coverp);
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
|
||||
#ifndef _V3CCTORS_H_
|
||||
#define _V3CCTORS_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
|
||||
+45
-48
@@ -30,25 +30,24 @@
|
||||
// Or, converts to a if/else tree.
|
||||
// FUTURES:
|
||||
// Large 16+ bit tables with constants and no masking (address muxes)
|
||||
// Enter all into multimap, sort by value and use a tree of < and == compares.
|
||||
// Enter all into std::multimap, sort by value and use a tree of < and == compares.
|
||||
// "Diagonal" find of {rightmost,leftmost} bit {set,clear}
|
||||
// Ignoring mask, check each value is unique (using multimap as above?)
|
||||
// Ignoring mask, check each value is unique (using std::multimap as above?)
|
||||
// Each branch is then mask-and-compare operation (IE <000000001_000000000 at midpoint.)
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <unistd.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Case.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
|
||||
#define CASE_OVERLAP_WIDTH 12 // Maximum width we can check for overlaps in
|
||||
#define CASE_BARF 999999 // Magic width when non-constant
|
||||
#define CASE_ENCODER_GROUP_DEPTH 8 // Levels of priority to be ORed together in top IF tree
|
||||
@@ -59,19 +58,16 @@ class CaseLintVisitor : public AstNVisitor {
|
||||
private:
|
||||
AstNodeCase* m_caseExprp; // Under a CASE value node, if so the relevant case statement
|
||||
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
virtual void visit(AstNodeCase* nodep) {
|
||||
if (nodep->castCase() && nodep->castCase()->casex()) {
|
||||
if (VN_IS(nodep, Case) && VN_CAST(nodep, Case)->casex()) {
|
||||
nodep->v3warn(CASEX,"Suggest casez (with ?'s) in place of casex (with X's)");
|
||||
}
|
||||
// Detect multiple defaults
|
||||
bool hitDefault = false;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
if (itemp->isDefault()) {
|
||||
if (hitDefault) {
|
||||
nodep->v3error("Multiple default statements in case statement.");
|
||||
@@ -83,9 +79,9 @@ private:
|
||||
// Check for X/Z in non-casex statements
|
||||
{
|
||||
m_caseExprp = nodep;
|
||||
nodep->exprp()->accept(*this);
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
|
||||
itemp->condsp()->iterateAndNext(*this);
|
||||
iterate(nodep->exprp());
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
iterateAndNextNull(itemp->condsp());
|
||||
}
|
||||
m_caseExprp = NULL;
|
||||
}
|
||||
@@ -93,12 +89,12 @@ private:
|
||||
virtual void visit(AstConst* nodep) {
|
||||
// See also neverItem
|
||||
if (m_caseExprp && nodep->num().isFourState()) {
|
||||
if (m_caseExprp->castGenCase()) {
|
||||
if (VN_IS(m_caseExprp, GenCase)) {
|
||||
nodep->v3error("Use of x/? constant in generate case statement, (no such thing as 'generate casez')");
|
||||
} else if (m_caseExprp->castCase() && m_caseExprp->castCase()->casex()) {
|
||||
} else if (VN_IS(m_caseExprp, Case) && VN_CAST(m_caseExprp, Case)->casex()) {
|
||||
// Don't sweat it, we already complained about casex in general
|
||||
} else if (m_caseExprp->castCase() && (m_caseExprp->castCase()->casez()
|
||||
|| m_caseExprp->castCase()->caseInside())) {
|
||||
} else if (VN_IS(m_caseExprp, Case) && (VN_CAST(m_caseExprp, Case)->casez()
|
||||
|| VN_CAST(m_caseExprp, Case)->caseInside())) {
|
||||
if (nodep->num().isUnknown()) {
|
||||
nodep->v3warn(CASEWITHX, "Use of x constant in casez statement, (perhaps intended ?/z in constant)");
|
||||
}
|
||||
@@ -108,13 +104,13 @@ private:
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit CaseLintVisitor(AstNodeCase* nodep) {
|
||||
m_caseExprp = NULL;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CaseLintVisitor() {}
|
||||
};
|
||||
@@ -140,22 +136,18 @@ private:
|
||||
AstNode* m_valueItem[1<<CASE_OVERLAP_WIDTH]; // For each possible value, the case branch we need
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
bool isCaseTreeFast(AstCase* nodep) {
|
||||
int width = 0;
|
||||
bool opaque = false;
|
||||
m_caseItems = 0;
|
||||
m_caseNoOverlapsAllCovered = true;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
|
||||
if (icondp->width() > width) width = icondp->width();
|
||||
if (icondp->isDouble()) opaque = true;
|
||||
if (!icondp->castConst()) width = CASE_BARF; // Can't parse; not a constant
|
||||
if (!VN_IS(icondp, Const)) width = CASE_BARF; // Can't parse; not a constant
|
||||
m_caseItems++;
|
||||
}
|
||||
}
|
||||
@@ -166,14 +158,14 @@ private:
|
||||
}
|
||||
UINFO(8,"Simple case statement: "<<nodep<<endl);
|
||||
// Zero list of items for each value
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); i++) m_valueItem[i] = NULL;
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) m_valueItem[i] = NULL;
|
||||
// Now pick up the values for each assignment
|
||||
// We can cheat and use uint32_t's because we only support narrow case's
|
||||
bool bitched = false;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
|
||||
//if (debug()>=9) icondp->dumpTree(cout," caseitem: ");
|
||||
AstConst* iconstp = icondp->castConst();
|
||||
AstConst* iconstp = VN_CAST(icondp, Const);
|
||||
if (!iconstp) nodep->v3fatalSrc("above 'can't parse' should have caught this");
|
||||
if (neverItem(nodep, iconstp)) {
|
||||
// X in casez can't ever be executed
|
||||
@@ -184,12 +176,12 @@ private:
|
||||
V3Number numval (itemp->fileline(), iconstp->width());
|
||||
numval.opBitsOne(iconstp->num());
|
||||
uint32_t val = numval.toUInt();
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); i++) {
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
|
||||
if ((i & mask) == val) {
|
||||
if (!m_valueItem[i]) {
|
||||
m_valueItem[i] = itemp;
|
||||
} else if (!itemp->ignoreOverlap() && !bitched) {
|
||||
itemp->v3warn(CASEOVERLAP,"Case values overlap (example pattern 0x"<<hex<<i<<")");
|
||||
itemp->v3warn(CASEOVERLAP,"Case values overlap (example pattern 0x"<<std::hex<<i<<")");
|
||||
bitched = true;
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
}
|
||||
@@ -199,14 +191,14 @@ private:
|
||||
}
|
||||
// Defaults were moved to last in the caseitem list by V3LinkDot
|
||||
if (itemp->isDefault()) { // Case statement's default... Fill the table
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); i++) {
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
|
||||
if (!m_valueItem[i]) m_valueItem[i] = itemp;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); i++) {
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
|
||||
if (!m_valueItem[i]) {
|
||||
nodep->v3warn(CASEINCOMPLETE,"Case values incompletely covered (example pattern 0x"<<hex<<i<<")");
|
||||
nodep->v3warn(CASEINCOMPLETE,"Case values incompletely covered (example pattern 0x"<<std::hex<<i<<")");
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
return false;
|
||||
}
|
||||
@@ -214,8 +206,8 @@ private:
|
||||
if (m_caseItems <= 3) return false; // Not worth simplifing
|
||||
// Convert valueItem from AstCaseItem* to the expression
|
||||
// Not done earlier, as we may now have a NULL because it's just a ";" NOP branch
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); i++) {
|
||||
m_valueItem[i] = m_valueItem[i]->castCaseItem()->bodysp();
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
|
||||
m_valueItem[i] = VN_CAST(m_valueItem[i], CaseItem)->bodysp();
|
||||
}
|
||||
return true; // All is fine
|
||||
}
|
||||
@@ -278,9 +270,9 @@ private:
|
||||
AstNode* cexprp = nodep->exprp()->unlinkFrBack();
|
||||
|
||||
if (debug()>=9) {
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); i++) {
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
|
||||
if (AstNode* itemp = m_valueItem[i]) {
|
||||
UINFO(9,"Value "<<hex<<i<<" "<<itemp<<endl);
|
||||
UINFO(9,"Value "<<std::hex<<i<<" "<<itemp<<endl);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -310,7 +302,7 @@ private:
|
||||
// the appropriate IF AND terms.
|
||||
if (debug()>=9) nodep->dumpTree(cout," _comp_IN: ");
|
||||
bool hadDefault = false;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
if (!itemp->condsp()) {
|
||||
// Default clause. Just make true, we'll optimize it away later
|
||||
itemp->condsp(new AstConst(itemp->fileline(), AstConst::LogicTrue()));
|
||||
@@ -324,14 +316,14 @@ private:
|
||||
icondp->unlinkFrBack();
|
||||
|
||||
AstNode* condp = NULL; // Default is to use and1p/and2p
|
||||
AstConst* iconstp = icondp->castConst();
|
||||
AstConst* iconstp = VN_CAST(icondp, Const);
|
||||
if (iconstp && neverItem(nodep, iconstp)) {
|
||||
// X in casez can't ever be executed
|
||||
icondp->deleteTree(); VL_DANGLING(icondp); VL_DANGLING(iconstp);
|
||||
// For simplicity, make expression that is not equal, and let later
|
||||
// optimizations remove it
|
||||
condp = new AstConst(itemp->fileline(), AstConst::LogicFalse());
|
||||
} else if (AstInsideRange* irangep = icondp->castInsideRange()) {
|
||||
} else if (AstInsideRange* irangep = VN_CAST(icondp, InsideRange)) {
|
||||
// Similar logic in V3Width::visit(AstInside)
|
||||
AstNode* ap = AstGte::newTyped(itemp->fileline(),
|
||||
cexprp->cloneTree(false),
|
||||
@@ -387,7 +379,7 @@ private:
|
||||
AstNode* grouprootp = NULL;
|
||||
AstIf* groupnextp = NULL;
|
||||
AstIf* itemnextp = NULL;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
AstNode* istmtsp = itemp->bodysp(); // Maybe null -- no action.
|
||||
if (istmtsp) istmtsp->unlinkFrBackWithNext();
|
||||
// Expressioned clause
|
||||
@@ -453,7 +445,7 @@ private:
|
||||
// VISITORS
|
||||
virtual void visit(AstCase* nodep) {
|
||||
V3Case::caseLint(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (debug()>=9) nodep->dumpTree(cout," case_old: ");
|
||||
if (isCaseTreeFast(nodep) && v3Global.opt.oCase()) {
|
||||
// It's a simple priority encoder or complete statement
|
||||
@@ -468,14 +460,19 @@ private:
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit CaseVisitor(AstNetlist* nodep) {
|
||||
m_caseWidth = 0;
|
||||
m_caseItems = 0;
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
nodep->accept(*this);
|
||||
for (uint32_t i=0; i<(1UL<<CASE_OVERLAP_WIDTH); ++i) {
|
||||
m_valueItem[i] = NULL;
|
||||
}
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CaseVisitor() {
|
||||
V3Stats::addStat("Optimizations, Cases parallelized", m_statCaseFast);
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
|
||||
#ifndef _V3CASE_H_
|
||||
#define _V3CASE_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
|
||||
+18
-23
@@ -43,15 +43,14 @@
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <unistd.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Cast.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
|
||||
//######################################################################
|
||||
// Cast state, as a visitor of each AstNode
|
||||
|
||||
@@ -65,25 +64,21 @@ private:
|
||||
// STATE
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void insertCast(AstNode* nodep, int needsize) { // We'll insert ABOVE passed node
|
||||
UINFO(4," NeedCast "<<nodep<<endl);
|
||||
AstNRelinker relinkHandle;
|
||||
nodep->unlinkFrBack(&relinkHandle);
|
||||
//
|
||||
AstCCast* castp = new AstCCast (nodep->fileline(), nodep, needsize, nodep->widthMin());
|
||||
AstCCast* castp = new AstCCast(nodep->fileline(), nodep, needsize, nodep->widthMin());
|
||||
relinkHandle.relink(castp);
|
||||
//if (debug()>8) castp->dumpTree(cout,"-castins: ");
|
||||
//
|
||||
insureLower32Cast(castp);
|
||||
nodep->user1(1); // Now must be of known size
|
||||
}
|
||||
int castSize (AstNode* nodep) {
|
||||
int castSize(AstNode* nodep) {
|
||||
if (nodep->isQuad()) return VL_QUADSIZE;
|
||||
else if (nodep->width()<=8) return 8;
|
||||
else if (nodep->width()<=16) return 16;
|
||||
@@ -101,26 +96,26 @@ private:
|
||||
// Otherwise a (uint64)(a>b) would return wrong value, as
|
||||
// less than has undeterministic signedness.
|
||||
if (nodep->isQuad() && !nodep->lhsp()->isQuad()
|
||||
&& !nodep->lhsp()->castCCast()) {
|
||||
&& !VN_IS(nodep->lhsp(), CCast)) {
|
||||
insertCast(nodep->lhsp(), VL_WORDSIZE);
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeUniop* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1());
|
||||
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
|
||||
}
|
||||
virtual void visit(AstNodeBiop* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1()
|
||||
| nodep->rhsp()->user1());
|
||||
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
|
||||
if (nodep->sizeMattersRhs()) insureCast(nodep->rhsp());
|
||||
}
|
||||
virtual void visit(AstNodeTriop* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1()
|
||||
| nodep->rhsp()->user1()
|
||||
| nodep->thsp()->user1());
|
||||
@@ -129,12 +124,12 @@ private:
|
||||
if (nodep->sizeMattersThs()) insureCast(nodep->thsp());
|
||||
}
|
||||
virtual void visit(AstCCast* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
insureLower32Cast(nodep);
|
||||
nodep->user1(1);
|
||||
}
|
||||
virtual void visit(AstNegate* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1());
|
||||
if (nodep->lhsp()->widthMin()==1) {
|
||||
// We want to avoid a GCC "converting of negative value" warning
|
||||
@@ -147,14 +142,14 @@ private:
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
if (!nodep->lvalue()
|
||||
&& !nodep->backp()->castCCast()
|
||||
&& nodep->backp()->castNodeMath()
|
||||
&& !nodep->backp()->castArraySel()
|
||||
&& !VN_IS(nodep->backp(), CCast)
|
||||
&& VN_IS(nodep->backp(), NodeMath)
|
||||
&& !VN_IS(nodep->backp(), ArraySel)
|
||||
&& nodep->backp()->width()
|
||||
&& castSize(nodep) != castSize(nodep->varp())) {
|
||||
// Cast vars to IData first, else below has upper bits wrongly set
|
||||
// CData x=3; out = (QData)(x<<30);
|
||||
insertCast (nodep, castSize(nodep));
|
||||
insertCast(nodep, castSize(nodep));
|
||||
}
|
||||
nodep->user1(1);
|
||||
}
|
||||
@@ -171,13 +166,13 @@ private:
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit CastVisitor(AstNetlist* nodep) {
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CastVisitor() {}
|
||||
};
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
|
||||
#ifndef _V3CAST_H_
|
||||
#define _V3CAST_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
|
||||
+61
-65
@@ -27,15 +27,6 @@
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <unistd.h>
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Cdc.h"
|
||||
@@ -45,17 +36,21 @@
|
||||
#include "V3EmitV.h"
|
||||
#include "V3File.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <deque>
|
||||
#include <iomanip>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#define CDC_WEIGHT_ASYNC 0x1000 // Weight for edges that feed async logic
|
||||
|
||||
//######################################################################
|
||||
|
||||
class CdcBaseVisitor : public AstNVisitor {
|
||||
public:
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -77,6 +72,7 @@ public:
|
||||
, m_asyncPath(false) {}
|
||||
virtual ~CdcEitherVertex() {}
|
||||
// ACCESSORS
|
||||
virtual FileLine* fileline() const { return nodep()->fileline(); }
|
||||
AstScope* scopep() const { return m_scopep; }
|
||||
AstNode* nodep() const { return m_nodep; }
|
||||
AstSenTree* srcDomainp() const { return m_srcDomainp; }
|
||||
@@ -101,7 +97,7 @@ public:
|
||||
virtual ~CdcVarVertex() {}
|
||||
// ACCESSORS
|
||||
AstVarScope* varScp() const { return m_varScp; }
|
||||
virtual string name() const { return (cvtToStr((void*)m_varScp)+" "+varScp()->name()); }
|
||||
virtual string name() const { return (cvtToHex(m_varScp)+" "+varScp()->name()); }
|
||||
virtual string dotColor() const { return fromFlop() ? "green" : cntAsyncRst() ? "red" : "blue"; }
|
||||
int cntAsyncRst() const { return m_cntAsyncRst; }
|
||||
void cntAsyncRst(int flag) { m_cntAsyncRst=flag; }
|
||||
@@ -119,7 +115,7 @@ public:
|
||||
{ srcDomainp(sensenodep); dstDomainp(sensenodep); }
|
||||
virtual ~CdcLogicVertex() {}
|
||||
// ACCESSORS
|
||||
virtual string name() const { return (cvtToStr((void*)nodep())+"@"+scopep()->prettyName()); }
|
||||
virtual string name() const { return (cvtToHex(nodep())+"@"+scopep()->prettyName()); }
|
||||
virtual string dotColor() const { return hazard() ? "black" : "yellow"; }
|
||||
bool hazard() const { return m_hazard; }
|
||||
void setHazard(AstNode* nodep) { m_hazard = true; nodep->user3(true); }
|
||||
@@ -135,7 +131,7 @@ private:
|
||||
// NODE STATE
|
||||
//Entire netlist:
|
||||
// {statement}Node::user3 -> bool, indicating not hazard
|
||||
ofstream* m_ofp; // Output file
|
||||
std::ofstream* m_ofp; // Output file
|
||||
string m_prefix;
|
||||
|
||||
virtual void visit(AstNode* nodep) {
|
||||
@@ -145,22 +141,22 @@ private:
|
||||
*m_ofp<<nodep->prettyTypeName()<<" "<<endl;
|
||||
string lastPrefix = m_prefix;
|
||||
m_prefix = lastPrefix + "1:";
|
||||
nodep->op1p()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->op1p());
|
||||
m_prefix = lastPrefix + "2:";
|
||||
nodep->op2p()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->op2p());
|
||||
m_prefix = lastPrefix + "3:";
|
||||
nodep->op3p()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->op3p());
|
||||
m_prefix = lastPrefix + "4:";
|
||||
nodep->op4p()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->op4p());
|
||||
m_prefix = lastPrefix;
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
CdcDumpVisitor(AstNode* nodep, ofstream* ofp, const string& prefix) {
|
||||
CdcDumpVisitor(AstNode* nodep, std::ofstream* ofp, const string& prefix) {
|
||||
m_ofp = ofp;
|
||||
m_prefix = prefix;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CdcDumpVisitor() {}
|
||||
};
|
||||
@@ -173,7 +169,7 @@ private:
|
||||
size_t m_maxFilenameLen;
|
||||
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Keeping line+filename lengths separate is much faster than calling ascii().length()
|
||||
if (nodep->fileline()->lineno() >= m_maxLineno) {
|
||||
m_maxLineno = nodep->fileline()->lineno()+1;
|
||||
@@ -187,7 +183,7 @@ public:
|
||||
explicit CdcWidthVisitor(AstNode* nodep) {
|
||||
m_maxLineno = 0;
|
||||
m_maxFilenameLen = 0;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CdcWidthVisitor() {}
|
||||
// ACCESSORS
|
||||
@@ -197,7 +193,7 @@ public:
|
||||
width += 1; // The :
|
||||
width += cvtToStr(m_maxLineno).length();
|
||||
width += 1; // Final :
|
||||
return (int)width;
|
||||
return static_cast<int>(width);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -225,7 +221,7 @@ private:
|
||||
bool m_inDly; // In delayed assign
|
||||
int m_inSenItem; // Number of senitems
|
||||
string m_ofFilename; // Output filename
|
||||
ofstream* m_ofp; // Output file
|
||||
std::ofstream* m_ofp; // Output file
|
||||
uint32_t m_userGeneration; // Generation count to avoid slow userClearVertices
|
||||
int m_filelineWidth; // Characters in longest fileline
|
||||
|
||||
@@ -241,7 +237,7 @@ private:
|
||||
m_logicVertexp->dstDomainp(m_domainp);
|
||||
m_logicVertexp->dstDomainSet(true);
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_logicVertexp = NULL;
|
||||
|
||||
if (0 && debug()>=9) {
|
||||
@@ -252,7 +248,7 @@ private:
|
||||
}
|
||||
|
||||
CdcVarVertex* makeVarVertex(AstVarScope* varscp) {
|
||||
CdcVarVertex* vertexp = (CdcVarVertex*)(varscp->user1p());
|
||||
CdcVarVertex* vertexp = reinterpret_cast<CdcVarVertex*>(varscp->user1p());
|
||||
if (!vertexp) {
|
||||
UINFO(6,"New vertex "<<varscp<<endl);
|
||||
vertexp = new CdcVarVertex(&m_graph, m_scopep, varscp);
|
||||
@@ -284,7 +280,7 @@ private:
|
||||
nodep->v3warnCode(code,msg);
|
||||
if (!told_file) {
|
||||
told_file = 1;
|
||||
cerr<<V3Error::msgPrefix()<<" See details in "<<m_ofFilename<<endl;
|
||||
std::cerr<<V3Error::msgPrefix()<<" See details in "<<m_ofFilename<<endl;
|
||||
}
|
||||
*m_ofp<<"%Warning-"<<code.ascii()<<": "<<nodep->fileline()<<" "<<msg<<endl;
|
||||
}
|
||||
@@ -380,7 +376,7 @@ private:
|
||||
if (vvertexp->varScp()->varp()->isPrimaryIn()) {
|
||||
// Show the source "input" statement if it exists
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
CdcEitherVertex* eFromVertexp = (CdcEitherVertex*)edgep->fromp();
|
||||
CdcEitherVertex* eFromVertexp = static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
eFromVertexp->asyncPath(true);
|
||||
}
|
||||
return NULL;
|
||||
@@ -388,7 +384,7 @@ private:
|
||||
// Also ok if from flop, but partially trace the flop so more obvious to users
|
||||
if (vvertexp->fromFlop()) {
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
CdcEitherVertex* eFromVertexp = (CdcEitherVertex*)edgep->fromp();
|
||||
CdcEitherVertex* eFromVertexp = static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
eFromVertexp->asyncPath(true);
|
||||
}
|
||||
return NULL;
|
||||
@@ -396,7 +392,7 @@ private:
|
||||
}
|
||||
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
CdcEitherVertex* eFromVertexp = (CdcEitherVertex*)edgep->fromp();
|
||||
CdcEitherVertex* eFromVertexp = static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
CdcEitherVertex* submarkp = traceAsyncRecurse(eFromVertexp, mark);
|
||||
if (submarkp && !mark_outp) mark_outp = submarkp;
|
||||
}
|
||||
@@ -411,7 +407,7 @@ private:
|
||||
*m_ofp<<"\n";
|
||||
CdcEitherVertex* targetp = vertexp; // One example destination flop (of possibly many)
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
CdcEitherVertex* eToVertexp = (CdcEitherVertex*)edgep->top();
|
||||
CdcEitherVertex* eToVertexp = static_cast<CdcEitherVertex*>(edgep->top());
|
||||
if (!eToVertexp) targetp = eToVertexp;
|
||||
if (CdcLogicVertex* vvertexp = dynamic_cast<CdcLogicVertex*>(eToVertexp)) {
|
||||
if (vvertexp->isFlop() // IE the target flop that is upsetting us
|
||||
@@ -438,7 +434,7 @@ private:
|
||||
string cont = prefix+sep;
|
||||
string nextsep = " ";
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
CdcEitherVertex* eFromVertexp = (CdcEitherVertex*)edgep->fromp();
|
||||
CdcEitherVertex* eFromVertexp = static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
if (dumpAsyncRecurse(eFromVertexp, cont, nextsep, level+1)) {
|
||||
nextsep = " | ";
|
||||
}
|
||||
@@ -448,7 +444,7 @@ private:
|
||||
// See also OrderGraph::loopsVertexCb(V3GraphVertex* vertexp)
|
||||
AstNode* nodep = vertexp->nodep();
|
||||
string front = pad(filelineWidth(),nodep->fileline()->ascii()+":")+" "+prefix+" +- ";
|
||||
if (nodep->castVarScope()) {
|
||||
if (VN_IS(nodep, VarScope)) {
|
||||
*m_ofp<<front<<"Variable: "<<nodep->prettyName()<<endl;
|
||||
}
|
||||
else {
|
||||
@@ -496,11 +492,11 @@ private:
|
||||
}
|
||||
|
||||
string filename = v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"__cdc_edges.txt";
|
||||
const vl_unique_ptr<ofstream> ofp (V3File::new_ofstream(filename));
|
||||
if (ofp->fail()) v3fatalSrc("Can't write "<<filename);
|
||||
const vl_unique_ptr<std::ofstream> ofp (V3File::new_ofstream(filename));
|
||||
if (ofp->fail()) v3fatal("Can't write "<<filename);
|
||||
*ofp<<"Edge Report for "<<v3Global.opt.prefix()<<endl;
|
||||
|
||||
deque<string> report; // Sort output by name
|
||||
std::deque<string> report; // Sort output by name
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
||||
if (CdcVarVertex* vvertexp = dynamic_cast<CdcVarVertex*>(itp)) {
|
||||
AstVar* varp = vvertexp->varScp()->varp();
|
||||
@@ -508,26 +504,26 @@ private:
|
||||
const char* whatp = "wire";
|
||||
if (varp->isPrimaryIO()) whatp = (varp->isInout()?"inout":varp->isInput()?"input":"output");
|
||||
|
||||
ostringstream os;
|
||||
os.setf(ios::left);
|
||||
std::ostringstream os;
|
||||
os.setf(std::ios::left);
|
||||
// Module name - doesn't work due to flattening having lost the original
|
||||
// so we assume the modulename matches the filebasename
|
||||
string fname = vvertexp->varScp()->fileline()->filebasename() + ":";
|
||||
os<<" "<<setw(20)<<fname;
|
||||
os<<" "<<setw(8)<<whatp;
|
||||
os<<" "<<setw(40)<<vvertexp->varScp()->prettyName();
|
||||
os<<" "<<std::setw(20)<<fname;
|
||||
os<<" "<<std::setw(8)<<whatp;
|
||||
os<<" "<<std::setw(40)<<vvertexp->varScp()->prettyName();
|
||||
os<<" SRC=";
|
||||
if (vvertexp->srcDomainp()) V3EmitV::verilogForTree(vvertexp->srcDomainp(), os);
|
||||
os<<" DST=";
|
||||
if (vvertexp->dstDomainp()) V3EmitV::verilogForTree(vvertexp->dstDomainp(), os);
|
||||
os<<setw(0);
|
||||
os<<std::setw(0);
|
||||
os<<endl;
|
||||
report.push_back(os.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
stable_sort(report.begin(), report.end());
|
||||
for (deque<string>::iterator it = report.begin(); it!=report.end(); ++it) {
|
||||
for (std::deque<string>::iterator it = report.begin(); it!=report.end(); ++it) {
|
||||
*ofp << *it;
|
||||
}
|
||||
}
|
||||
@@ -541,7 +537,7 @@ private:
|
||||
// Variables from flops already are domained
|
||||
if (traceDests ? vertexp->dstDomainSet() : vertexp->srcDomainSet()) return; // Fully computed
|
||||
|
||||
typedef set<AstSenTree*> SenSet;
|
||||
typedef std::set<AstSenTree*> SenSet;
|
||||
SenSet senouts; // List of all sensitivities for new signal
|
||||
if (CdcLogicVertex* vvertexp = dynamic_cast<CdcLogicVertex*>(vertexp)) {
|
||||
if (vvertexp) {} // Unused
|
||||
@@ -557,13 +553,13 @@ private:
|
||||
// Now combine domains of sources/dests
|
||||
if (traceDests) {
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
CdcEitherVertex* eToVertexp = (CdcEitherVertex*)edgep->top();
|
||||
CdcEitherVertex* eToVertexp = static_cast<CdcEitherVertex*>(edgep->top());
|
||||
edgeDomainRecurse(eToVertexp, traceDests, level+1);
|
||||
if (eToVertexp->dstDomainp()) senouts.insert(eToVertexp->dstDomainp());
|
||||
}
|
||||
} else {
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
CdcEitherVertex* eFromVertexp = (CdcEitherVertex*)edgep->fromp();
|
||||
CdcEitherVertex* eFromVertexp = static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
edgeDomainRecurse(eFromVertexp, traceDests, level+1);
|
||||
if (eFromVertexp->srcDomainp()) senouts.insert(eFromVertexp->srcDomainp());
|
||||
}
|
||||
@@ -584,7 +580,7 @@ private:
|
||||
}
|
||||
// If multiple domains need to do complicated optimizations
|
||||
if (senedited) {
|
||||
senoutp = V3Const::constifyExpensiveEdit(senoutp)->castSenTree();
|
||||
senoutp = VN_CAST(V3Const::constifyExpensiveEdit(senoutp), SenTree);
|
||||
}
|
||||
if (traceDests) {
|
||||
vertexp->dstDomainSet(true); // Note it's set - domainp may be null, so can't use that
|
||||
@@ -606,14 +602,14 @@ private:
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
m_modp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstScope* nodep) {
|
||||
UINFO(4," SCOPE "<<nodep<<endl);
|
||||
m_scopep = nodep;
|
||||
m_logicVertexp = NULL;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_scopep = NULL;
|
||||
}
|
||||
virtual void visit(AstActive* nodep) {
|
||||
@@ -657,14 +653,14 @@ private:
|
||||
}
|
||||
virtual void visit(AstAssignDly* nodep) {
|
||||
m_inDly = true;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_inDly = false;
|
||||
}
|
||||
virtual void visit(AstSenItem* nodep) {
|
||||
// Note we look at only AstSenItems, not AstSenGate's
|
||||
// The gating term of a AstSenGate is normal logic
|
||||
m_inSenItem = true;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_inSenItem = false;
|
||||
}
|
||||
virtual void visit(AstAlways* nodep) {
|
||||
@@ -691,21 +687,21 @@ private:
|
||||
// Math that shouldn't cause us to clear hazard
|
||||
virtual void visit(AstConst* nodep) { }
|
||||
virtual void visit(AstReplicate* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstConcat* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNot* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstSel* nodep) {
|
||||
if (!nodep->lsbp()->castConst()) setNodeHazard(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
if (!VN_IS(nodep->lsbp(), Const)) setNodeHazard(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeSel* nodep) {
|
||||
if (!nodep->bitp()->castConst()) setNodeHazard(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
if (!VN_IS(nodep->bitp(), Const)) setNodeHazard(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
// Ignores
|
||||
@@ -718,10 +714,10 @@ private:
|
||||
// Default
|
||||
virtual void visit(AstNodeMath* nodep) {
|
||||
setNodeHazard(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -739,7 +735,7 @@ public:
|
||||
// Make report of all signal names and what clock edges they have
|
||||
string filename = v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"__cdc.txt";
|
||||
m_ofp = V3File::new_ofstream(filename);
|
||||
if (m_ofp->fail()) v3fatalSrc("Can't write "<<filename);
|
||||
if (m_ofp->fail()) v3fatal("Can't write "<<filename);
|
||||
m_ofFilename = filename;
|
||||
*m_ofp<<"CDC Report for "<<v3Global.opt.prefix()<<endl;
|
||||
*m_ofp<<"Each dump below traces logic from inputs/source flops to destination flop(s).\n";
|
||||
@@ -747,7 +743,7 @@ public:
|
||||
*m_ofp<<"repeating recursively forwards to the destination flop(s).\n";
|
||||
*m_ofp<<"%% Indicates the operator considered potentially hazardous.\n";
|
||||
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
analyze();
|
||||
if (debug()>=1) edgeReport(); // Not useful to users at the moment
|
||||
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
|
||||
#ifndef _V3CDC_H_
|
||||
#define _V3CDC_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
|
||||
+20
-25
@@ -32,17 +32,16 @@
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <unistd.h>
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3Changed.h"
|
||||
#include "V3EmitCBase.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <set>
|
||||
|
||||
//######################################################################
|
||||
|
||||
class ChangedState {
|
||||
@@ -85,7 +84,7 @@ public:
|
||||
if (!m_tlChgFuncp->stmtsp()) {
|
||||
m_tlChgFuncp->addStmtsp(new AstCReturn(m_scopetopp->fileline(), callp));
|
||||
} else {
|
||||
AstCReturn* returnp = m_tlChgFuncp->stmtsp()->castCReturn();
|
||||
AstCReturn* returnp = VN_CAST(m_tlChgFuncp->stmtsp(), CReturn);
|
||||
if (!returnp) m_scopetopp->v3fatalSrc("Lost CReturn in top change function");
|
||||
// This is currently using AstLogOr which will shortcut the evaluation if
|
||||
// any function returns true. This is likely what we want and is similar to the logic already in use
|
||||
@@ -131,13 +130,13 @@ private:
|
||||
}
|
||||
m_statep->maybeCreateChgFuncp();
|
||||
|
||||
AstChangeDet* changep = new AstChangeDet (m_vscp->fileline(),
|
||||
m_varEqnp->cloneTree(true),
|
||||
m_newRvEqnp->cloneTree(true), false);
|
||||
AstChangeDet* changep = new AstChangeDet(m_vscp->fileline(),
|
||||
m_varEqnp->cloneTree(true),
|
||||
m_newRvEqnp->cloneTree(true), false);
|
||||
m_statep->m_chgFuncp->addStmtsp(changep);
|
||||
AstAssign* initp = new AstAssign (m_vscp->fileline(),
|
||||
m_newLvEqnp->cloneTree(true),
|
||||
m_varEqnp->cloneTree(true));
|
||||
AstAssign* initp = new AstAssign(m_vscp->fileline(),
|
||||
m_newLvEqnp->cloneTree(true),
|
||||
m_varEqnp->cloneTree(true));
|
||||
m_statep->m_chgFuncp->addFinalsp(initp);
|
||||
EmitCBaseCounterVisitor visitor(initp);
|
||||
m_statep->m_numStmts += visitor.count();
|
||||
@@ -159,7 +158,7 @@ private:
|
||||
m_newLvEqnp = new AstArraySel(nodep->fileline(), m_newLvEqnp->cloneTree(true), index);
|
||||
m_newRvEqnp = new AstArraySel(nodep->fileline(), m_newRvEqnp->cloneTree(true), index);
|
||||
|
||||
nodep->subDTypep()->skipRefp()->accept(*this);
|
||||
iterate(nodep->subDTypep()->skipRefp());
|
||||
|
||||
m_varEqnp->deleteTree();
|
||||
m_newLvEqnp->deleteTree();
|
||||
@@ -181,7 +180,7 @@ private:
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (debug()) nodep->dumpTree(cout,"-DETECTARRAY-general-");
|
||||
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable"
|
||||
" (probably with UNOPTFLAT warning suppressed): "
|
||||
@@ -200,7 +199,7 @@ public:
|
||||
// ASSIGN(VARREF(_last), VARREF(var))
|
||||
// ...
|
||||
// CHANGEDET(VARREF(_last), VARREF(var))
|
||||
AstVar* newvarp = new AstVar (varp->fileline(), AstVarType::MODULETEMP, newvarname, varp);
|
||||
AstVar* newvarp = new AstVar(varp->fileline(), AstVarType::MODULETEMP, newvarname, varp);
|
||||
m_statep->m_topModp->addStmtp(newvarp);
|
||||
m_newvscp = new AstVarScope(m_vscp->fileline(), m_statep->m_scopetopp, newvarp);
|
||||
m_statep->m_scopetopp->addVarp(m_newvscp);
|
||||
@@ -209,7 +208,7 @@ public:
|
||||
m_newLvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, true);
|
||||
m_newRvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, false);
|
||||
}
|
||||
vscp->dtypep()->skipRefp()->accept(*this);
|
||||
iterate(vscp->dtypep()->skipRefp());
|
||||
m_varEqnp->deleteTree();
|
||||
m_newLvEqnp->deleteTree();
|
||||
m_newRvEqnp->deleteTree();
|
||||
@@ -231,11 +230,7 @@ private:
|
||||
ChangedState* m_statep; // Shared state across visitors
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void genChangeDet(AstVarScope* vscp) {
|
||||
vscp->v3warn(IMPERFECTSCH,"Imperfect scheduling of variable: "<<vscp);
|
||||
@@ -248,7 +243,7 @@ private:
|
||||
if (nodep->isTop()) {
|
||||
m_statep->m_topModp = nodep;
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstTopScope* nodep) {
|
||||
@@ -271,7 +266,7 @@ private:
|
||||
m_statep->maybeCreateChgFuncp();
|
||||
m_statep->m_chgFuncp->addStmtsp(new AstChangeDet(nodep->fileline(), NULL, NULL, false));
|
||||
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstVarScope* nodep) {
|
||||
if (nodep->isCircular()) {
|
||||
@@ -287,14 +282,14 @@ private:
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
ChangedVisitor(AstNetlist* nodep, ChangedState* statep) {
|
||||
m_statep = statep;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~ChangedVisitor() {}
|
||||
};
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
|
||||
#ifndef _V3CHANGED_H_
|
||||
#define _V3CHANGED_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
|
||||
+50
-54
@@ -29,15 +29,14 @@
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <unistd.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Clean.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
|
||||
//######################################################################
|
||||
// Clean state, as a visitor of each AstNode
|
||||
|
||||
@@ -59,11 +58,7 @@ private:
|
||||
AstNodeModule* m_modp;
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// Width resetting
|
||||
int cppWidth(AstNode* nodep) {
|
||||
@@ -71,14 +66,14 @@ private:
|
||||
else if (nodep->width()<=VL_QUADSIZE) return VL_QUADSIZE;
|
||||
else return nodep->widthWords()*VL_WORDSIZE;
|
||||
}
|
||||
void setCppWidth (AstNode* nodep) {
|
||||
void setCppWidth(AstNode* nodep) {
|
||||
nodep->user2(true); // Don't resize it again
|
||||
AstNodeDType* old_dtypep = nodep->dtypep();
|
||||
int width=cppWidth(nodep); // widthMin is unchanged
|
||||
if (old_dtypep->width() != width) {
|
||||
// Since any given dtype's cppWidth() is the same, we can just
|
||||
// remember one convertion for each, and reuse it
|
||||
if (AstNodeDType* new_dtypep = old_dtypep->user3p()->castNodeDType()) {
|
||||
if (AstNodeDType* new_dtypep = VN_CAST(old_dtypep->user3p(), NodeDType)) {
|
||||
nodep->dtypep(new_dtypep);
|
||||
} else {
|
||||
nodep->dtypeChgWidth(width, nodep->widthMin());
|
||||
@@ -88,10 +83,10 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
void computeCppWidth (AstNode* nodep) {
|
||||
void computeCppWidth(AstNode* nodep) {
|
||||
if (!nodep->user2() && nodep->hasDType()) {
|
||||
if (nodep->castVar() || nodep->castNodeDType() // Don't want to change variable widths!
|
||||
|| nodep->dtypep()->skipRefp()->castUnpackArrayDType()) { // Or arrays
|
||||
if (VN_IS(nodep, Var) || VN_IS(nodep, NodeDType) // Don't want to change variable widths!
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)) { // Or arrays
|
||||
} else {
|
||||
setCppWidth(nodep);
|
||||
}
|
||||
@@ -103,7 +98,7 @@ private:
|
||||
nodep->user1(clean);
|
||||
}
|
||||
CleanState getCleanState(AstNode* nodep) {
|
||||
return ((CleanState)nodep->user1());
|
||||
return static_cast<CleanState>(nodep->user1());
|
||||
}
|
||||
bool isClean(AstNode* nodep) {
|
||||
CleanState clstate = getCleanState(nodep);
|
||||
@@ -113,7 +108,7 @@ private:
|
||||
return false;
|
||||
}
|
||||
void setClean(AstNode* nodep, bool isClean) {
|
||||
computeCppWidth (nodep); // Just to be sure it's in widthMin
|
||||
computeCppWidth(nodep); // Just to be sure it's in widthMin
|
||||
bool wholeUint = ((nodep->widthMin() % VL_WORDSIZE) == 0); //32,64,...
|
||||
setCleanState(nodep, ((isClean||wholeUint) ? CS_CLEAN:CS_DIRTY));
|
||||
}
|
||||
@@ -127,9 +122,9 @@ private:
|
||||
computeCppWidth(nodep);
|
||||
V3Number mask (nodep->fileline(), cppWidth(nodep));
|
||||
mask.setMask(nodep->widthMin());
|
||||
AstNode* cleanp = new AstAnd (nodep->fileline(),
|
||||
new AstConst (nodep->fileline(), mask),
|
||||
nodep);
|
||||
AstNode* cleanp = new AstAnd(nodep->fileline(),
|
||||
new AstConst(nodep->fileline(), mask),
|
||||
nodep);
|
||||
cleanp->dtypeFrom(nodep); // Otherwise the AND normally picks LHS
|
||||
relinkHandle.relink(cleanp);
|
||||
}
|
||||
@@ -148,7 +143,7 @@ private:
|
||||
|
||||
// Base type handling methods
|
||||
void operandBiop(AstNodeBiop* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
if (nodep->cleanLhs()) {
|
||||
insureClean(nodep->lhsp());
|
||||
@@ -159,7 +154,7 @@ private:
|
||||
//no setClean.. must do it in each user routine.
|
||||
}
|
||||
void operandTriop(AstNodeTriop* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
if (nodep->cleanLhs()) {
|
||||
insureClean(nodep->lhsp());
|
||||
@@ -176,118 +171,119 @@ private:
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
m_modp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstNodeUniop* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
if (nodep->cleanLhs()) {
|
||||
insureClean(nodep->lhsp());
|
||||
}
|
||||
setClean (nodep, nodep->cleanOut());
|
||||
setClean(nodep, nodep->cleanOut());
|
||||
}
|
||||
virtual void visit(AstNodeBiop* nodep) {
|
||||
operandBiop(nodep);
|
||||
setClean (nodep, nodep->cleanOut());
|
||||
setClean(nodep, nodep->cleanOut());
|
||||
}
|
||||
virtual void visit(AstAnd* nodep) {
|
||||
operandBiop(nodep);
|
||||
setClean (nodep, isClean(nodep->lhsp()) || isClean(nodep->rhsp()));
|
||||
setClean(nodep, isClean(nodep->lhsp()) || isClean(nodep->rhsp()));
|
||||
}
|
||||
virtual void visit(AstXor* nodep) {
|
||||
operandBiop(nodep);
|
||||
setClean (nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp()));
|
||||
setClean(nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp()));
|
||||
}
|
||||
virtual void visit(AstOr* nodep) {
|
||||
operandBiop(nodep);
|
||||
setClean (nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp()));
|
||||
setClean(nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp()));
|
||||
}
|
||||
virtual void visit(AstNodeMath* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
setClean (nodep, nodep->cleanOut());
|
||||
setClean(nodep, nodep->cleanOut());
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
if (nodep->cleanRhs()) {
|
||||
insureClean(nodep->rhsp());
|
||||
}
|
||||
}
|
||||
virtual void visit(AstText* nodep) {
|
||||
setClean (nodep, true);
|
||||
setClean(nodep, true);
|
||||
}
|
||||
virtual void visit(AstScopeName* nodep) {
|
||||
setClean (nodep, true);
|
||||
setClean(nodep, true);
|
||||
}
|
||||
virtual void visit(AstSel* nodep) {
|
||||
operandTriop(nodep);
|
||||
setClean (nodep, nodep->cleanOut());
|
||||
setClean(nodep, nodep->cleanOut());
|
||||
}
|
||||
virtual void visit(AstUCFunc* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
setClean (nodep, false);
|
||||
setClean(nodep, false);
|
||||
// We always clean, as we don't trust those pesky users.
|
||||
if (!nodep->backp()->castAnd()) {
|
||||
if (!VN_IS(nodep->backp(), And)) {
|
||||
insertClean(nodep);
|
||||
}
|
||||
insureCleanAndNext (nodep->bodysp());
|
||||
insureCleanAndNext(nodep->bodysp());
|
||||
}
|
||||
virtual void visit(AstTraceInc* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
insureCleanAndNext (nodep->valuep());
|
||||
iterateChildren(nodep);
|
||||
insureCleanAndNext(nodep->valuep());
|
||||
}
|
||||
virtual void visit(AstTypedef* nodep) {
|
||||
// No cleaning, or would loose pointer to enum
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstParamTypeDType* nodep) {
|
||||
// No cleaning, or would loose pointer to enum
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
// Control flow operators
|
||||
virtual void visit(AstNodeCond* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
insureClean(nodep->condp());
|
||||
setClean(nodep, isClean(nodep->expr1p()) && isClean(nodep->expr2p()));
|
||||
}
|
||||
virtual void visit(AstWhile* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
insureClean(nodep->condp());
|
||||
}
|
||||
virtual void visit(AstNodeIf* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
insureClean(nodep->condp());
|
||||
}
|
||||
virtual void visit(AstSFormatF* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
insureCleanAndNext (nodep->exprsp());
|
||||
iterateChildren(nodep);
|
||||
insureCleanAndNext(nodep->exprsp());
|
||||
setClean(nodep, true); // generates a string, so not relevant
|
||||
}
|
||||
virtual void visit(AstUCStmt* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
insureCleanAndNext (nodep->bodysp());
|
||||
iterateChildren(nodep);
|
||||
insureCleanAndNext(nodep->bodysp());
|
||||
}
|
||||
virtual void visit(AstCCall* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
insureCleanAndNext (nodep->argsp());
|
||||
setClean (nodep, true);
|
||||
iterateChildren(nodep);
|
||||
insureCleanAndNext(nodep->argsp());
|
||||
setClean(nodep, true);
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit CleanVisitor(AstNetlist* nodep) {
|
||||
nodep->accept(*this);
|
||||
m_modp = NULL;
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CleanVisitor() {}
|
||||
};
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
|
||||
#ifndef _V3CLEAN_H_
|
||||
#define _V3CLEAN_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
|
||||
+32
-36
@@ -33,12 +33,6 @@
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <unistd.h>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Ast.h"
|
||||
@@ -46,16 +40,17 @@
|
||||
#include "V3Graph.h"
|
||||
#include "V3ClkGater.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
//######################################################################
|
||||
// Base for debug
|
||||
|
||||
class GaterBaseVisitor : public AstNVisitor {
|
||||
protected:
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -105,7 +100,8 @@ public:
|
||||
: GaterVertex(graphp), m_nodep(nodep) { }
|
||||
virtual ~GaterIfVertex() {}
|
||||
virtual int typeNum() const { return __LINE__; } // C++ typeof() equivelent
|
||||
virtual string name() const { return cvtToStr((void*)m_nodep)+" {"+cvtToStr(m_nodep->fileline()->lineno())+"}"; }
|
||||
virtual string name() const {
|
||||
return cvtToHex(m_nodep)+" {"+cvtToStr(m_nodep->fileline()->lineno())+"}"; }
|
||||
};
|
||||
|
||||
class GaterVarVertex : public GaterVertex {
|
||||
@@ -196,7 +192,7 @@ private:
|
||||
bool m_isSimple; // Set false when we know it isn't simple
|
||||
// METHODS
|
||||
inline void okIterate(AstNode* nodep) {
|
||||
if (m_isSimple) nodep->iterateChildren(*this);
|
||||
if (m_isSimple) iterateChildren(nodep);
|
||||
}
|
||||
// VISITORS
|
||||
virtual void visit(AstOr* nodep) { okIterate(nodep); }
|
||||
@@ -213,13 +209,13 @@ private:
|
||||
|
||||
virtual void visit(AstNode* nodep) {
|
||||
m_isSimple = false;
|
||||
//nodep->iterateChildren(*this);
|
||||
//iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit GaterCondVisitor(AstNode* nodep) {
|
||||
m_isSimple = true;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~GaterCondVisitor() {}
|
||||
// PUBLIC METHODS
|
||||
@@ -283,7 +279,7 @@ class GaterBodyVisitor : public GaterBaseVisitor {
|
||||
uint32_t childstate;
|
||||
{
|
||||
m_state = STATE_UNKNOWN;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
childstate = m_state;
|
||||
}
|
||||
m_state = oldstate;
|
||||
@@ -305,7 +301,7 @@ class GaterBodyVisitor : public GaterBaseVisitor {
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
@@ -315,7 +311,7 @@ public:
|
||||
m_state = STATE_UNKNOWN;
|
||||
m_cloning = false;
|
||||
if (debug()>=9) nodep->dumpTree(cout," GateBodyIn: ");
|
||||
nodep->bodysp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
if (debug()>=9) nodep->dumpTree(cout," GateBodyOut: ");
|
||||
// If there's no statements we shouldn't have had a resulting graph
|
||||
// vertex asking for this creation
|
||||
@@ -380,7 +376,7 @@ class GaterVisitor : public GaterBaseVisitor {
|
||||
m_pliVertexp = new GaterPliVertex(&m_graph);
|
||||
}
|
||||
if (m_stmtVscp) { // Already saw a variable, be sure to mark it!
|
||||
GaterVarVertex* varVtxp = (GaterVarVertex*)(m_stmtVscp->user1p());
|
||||
GaterVarVertex* varVtxp = reinterpret_cast<GaterVarVertex*>(m_stmtVscp->user1p());
|
||||
new GaterEdge(&m_graph, m_pliVertexp, varVtxp, VU_PLI);
|
||||
}
|
||||
m_stmtInPli = true; // Mark all followon variables too
|
||||
@@ -587,9 +583,9 @@ class GaterVisitor : public GaterBaseVisitor {
|
||||
}
|
||||
void nafgMarkRecurse(V3GraphVertex* vertexp, uint32_t generation) {
|
||||
// Backwards mark user() on the path we recurse
|
||||
//UINFO(9," nafgMark: v "<<(void*)(vertexp)<<" "<<vertexp->name()<<endl);
|
||||
//UINFO(9," nafgMark: v "<<cvtToHex(vertexp)<<" "<<vertexp->name()<<endl);
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
//UINFO(9," nafgMark: "<<(void*)(edgep)<<" "<<edgep->name()<<endl);
|
||||
//UINFO(9," nafgMark: "<<cvtToHex(edgep)<<" "<<edgep->name()<<endl);
|
||||
edgep->user(generation);
|
||||
nafgMarkRecurse(edgep->fromp(), generation);
|
||||
}
|
||||
@@ -598,18 +594,18 @@ class GaterVisitor : public GaterBaseVisitor {
|
||||
// Forewards follow user() marked previously and build tree
|
||||
AstNode* nodep = NULL;
|
||||
// OR across all edges found at this level
|
||||
//UINFO(9," nafgEnter: v "<<(void*)(vertexp)<<" "<<vertexp->name()<<endl);
|
||||
//UINFO(9," nafgEnter: v "<<cvtToHex(vertexp)<<" "<<vertexp->name()<<endl);
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
if (edgep->user() == generation) {
|
||||
GaterEdge* cedgep = static_cast<GaterEdge*>(edgep);
|
||||
AstNode* eqnp = NULL;
|
||||
//UINFO(9," nafgFollow: "<<(void*)(edgep)<<" "<<edgep->name()<<endl);
|
||||
//UINFO(9," nafgFollow: "<<cvtToHex(edgep)<<" "<<edgep->name()<<endl);
|
||||
if (dynamic_cast<GaterHeadVertex*>(edgep->fromp())) {
|
||||
// Just OR in all lower terms
|
||||
eqnp = nafgCreateRecurse(edgep->top(), generation);
|
||||
} else if (GaterIfVertex* cVxp = dynamic_cast<GaterIfVertex*>(edgep->fromp())) {
|
||||
// Edges from IFs represent a real IF branch in the equation tree
|
||||
//UINFO(9," ifver "<<(void*)(edgep)<<" cc"<<edgep->dotColor()<<endl);
|
||||
//UINFO(9," ifver "<<cvtToHex(edgep)<<" cc"<<edgep->dotColor()<<endl);
|
||||
eqnp = cVxp->nodep()->condp()->cloneTree(true);
|
||||
if (!eqnp) cVxp->nodep()->v3fatalSrc("null condition");
|
||||
if (cedgep->ifelseFalse()) {
|
||||
@@ -626,7 +622,7 @@ class GaterVisitor : public GaterBaseVisitor {
|
||||
//if (debug()>=9) nodep->dumpTree(cout," followExpr: ");
|
||||
}
|
||||
}
|
||||
//UINFO(9," nafgExit: "<<(void*)(vertexp)<<" "<<vertexp->name()<<endl);
|
||||
//UINFO(9," nafgExit: "<<cvtToHex(vertexp)<<" "<<vertexp->name()<<endl);
|
||||
return nodep;
|
||||
}
|
||||
|
||||
@@ -672,7 +668,7 @@ class GaterVisitor : public GaterBaseVisitor {
|
||||
AstSenTree* sensesp = nodep->sensesp()->cloneTree(true);
|
||||
#else
|
||||
// Make a SenGate
|
||||
AstSenItem* oldsenitemsp = nodep->sensesp()->sensesp()->castSenItem();
|
||||
AstSenItem* oldsenitemsp = VN_CAST(nodep->sensesp()->sensesp(), SenItem);
|
||||
if (!oldsenitemsp) nodep->v3fatalSrc("SenTree doesn't have any SenItem under it");
|
||||
|
||||
AstSenTree* sensesp = new AstSenTree(nodep->fileline(),
|
||||
@@ -693,9 +689,9 @@ class GaterVisitor : public GaterBaseVisitor {
|
||||
nodep->addNextHere(alwp);
|
||||
|
||||
// Blow moved statements from old body
|
||||
GaterBodyVisitor(nodep,exprp,true);
|
||||
{ GaterBodyVisitor vis(nodep,exprp,true); }
|
||||
// Blow old statements from new body
|
||||
GaterBodyVisitor(alwp,exprp,false);
|
||||
{ GaterBodyVisitor vis(alwp,exprp,false); }
|
||||
|
||||
++m_statGaters;
|
||||
if (debug()>=9) alwp->dumpTree(cout," new: ");
|
||||
@@ -768,7 +764,7 @@ class GaterVisitor : public GaterBaseVisitor {
|
||||
}
|
||||
m_stmtVscp = vscp;
|
||||
// Find, or make new Vertex
|
||||
GaterVarVertex* vertexp = (GaterVarVertex*)(vscp->user1p());
|
||||
GaterVarVertex* vertexp = reinterpret_cast<GaterVarVertex*>(vscp->user1p());
|
||||
if (!vertexp) {
|
||||
vertexp = new GaterVarVertex(&m_graph, vscp);
|
||||
vscp->user1p(vertexp);
|
||||
@@ -802,17 +798,17 @@ class GaterVisitor : public GaterBaseVisitor {
|
||||
GaterIfVertex* vertexp = new GaterIfVertex(&m_graph, nodep);
|
||||
new GaterEdge(&m_graph, m_aboveVertexp, vertexp, m_aboveTrue);
|
||||
{
|
||||
nodep->condp()->iterateAndNext(*this); // directlyUnder stays as-is
|
||||
iterateAndNextNull(nodep->condp()); // directlyUnder stays as-is
|
||||
}
|
||||
{
|
||||
m_aboveVertexp = vertexp; // Vars will point at this edge
|
||||
m_aboveTrue = VU_IF;
|
||||
nodep->ifsp()->iterateAndNext(*this); // directlyUnder stays as-is (true)
|
||||
iterateAndNextNull(nodep->ifsp()); // directlyUnder stays as-is (true)
|
||||
}
|
||||
{
|
||||
m_aboveVertexp = vertexp; // Vars will point at this edge
|
||||
m_aboveTrue = VU_ELSE;
|
||||
nodep->elsesp()->iterateAndNext(*this); // directlyUnder stays as-is (true)
|
||||
iterateAndNextNull(nodep->elsesp()); // directlyUnder stays as-is (true)
|
||||
}
|
||||
m_aboveVertexp = lastabovep;
|
||||
m_aboveTrue = lasttrue;
|
||||
@@ -857,7 +853,7 @@ class GaterVisitor : public GaterBaseVisitor {
|
||||
AstVarScope* lastvscp = m_stmtVscp;
|
||||
bool lastpli = m_stmtInPli;
|
||||
m_directlyUnderAlw = under;
|
||||
if (nodep->castNodeStmt()) { // Restored below
|
||||
if (VN_IS(nodep, NodeStmt)) { // Restored below
|
||||
UINFO(9," Stmt: "<<nodep<<endl);
|
||||
m_stmtVscp = NULL;
|
||||
m_stmtInPli = false;
|
||||
@@ -868,10 +864,10 @@ class GaterVisitor : public GaterBaseVisitor {
|
||||
scoreboardPli(nodep);
|
||||
}
|
||||
{
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_directlyUnderAlw = lastdua;
|
||||
if (nodep->castNodeStmt()) { // Reset what set above; else propagate up to above statement
|
||||
if (VN_IS(nodep, NodeStmt)) { // Reset what set above; else propagate up to above statement
|
||||
m_stmtVscp = lastvscp;
|
||||
m_stmtInPli = lastpli;
|
||||
}
|
||||
@@ -882,7 +878,7 @@ public:
|
||||
explicit GaterVisitor(AstNetlist* nodep) {
|
||||
// AstAlways visitor does the real work, so most zeroing needs to be in clear()
|
||||
clear();
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
void clear() {
|
||||
m_nonopt = "";
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
|
||||
#ifndef _V3CLKGATER_H_
|
||||
#define _V3CLKGATER_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
|
||||
+74
-31
@@ -33,16 +33,15 @@
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <unistd.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Clock.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3EmitCBase.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
|
||||
//######################################################################
|
||||
// Clock state, as a visitor of each AstNode
|
||||
|
||||
@@ -51,9 +50,7 @@ private:
|
||||
// NODE STATE
|
||||
// Cleared each Module:
|
||||
// AstVarScope::user1p() -> AstVarScope*. Temporary signal that was created.
|
||||
// AstVarScope::user2p() -> AstVarScope*. Temporary signal for change detects
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
|
||||
// TYPES
|
||||
enum { DOUBLE_OR_RATE = 10 }; // How many | per ||, Determined experimentally as best
|
||||
@@ -68,29 +65,34 @@ private:
|
||||
AstCFunc* m_settleFuncp; // Top settlement function we are creating
|
||||
AstSenTree* m_lastSenp; // Last sensitivity match, so we can detect duplicates.
|
||||
AstIf* m_lastIfp; // Last sensitivity if active to add more under
|
||||
AstMTaskBody* m_mtaskBodyp; // Current mtask body
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
AstVarScope* getCreateLastClk(AstVarScope* vscp) {
|
||||
if (vscp->user1p()) return ((AstVarScope*)vscp->user1p());
|
||||
if (vscp->user1p()) return static_cast<AstVarScope*>(vscp->user1p());
|
||||
AstVar* varp = vscp->varp();
|
||||
if (!varp->width1()) varp->v3error("Unsupported: Clock edge on non-single bit signal: "<<varp->prettyName());
|
||||
string newvarname = ((string)"__Vclklast__"+vscp->scopep()->nameDotless()+"__"+varp->name());
|
||||
AstVar* newvarp = new AstVar (vscp->fileline(), AstVarType::MODULETEMP, newvarname, VFlagLogicPacked(), 1);
|
||||
string newvarname = (string("__Vclklast__")+vscp->scopep()->nameDotless()+"__"+varp->name());
|
||||
AstVar* newvarp = new AstVar(vscp->fileline(), AstVarType::MODULETEMP, newvarname, VFlagLogicPacked(), 1);
|
||||
newvarp->noReset(true); // Reset by below assign
|
||||
m_modp->addStmtp(newvarp);
|
||||
AstVarScope* newvscp = new AstVarScope(vscp->fileline(), m_scopep, newvarp);
|
||||
vscp->user1p(newvscp);
|
||||
m_scopep->addVarp(newvscp);
|
||||
// Add init
|
||||
AstNode* fromp = new AstVarRef(newvarp->fileline(), vscp, false);
|
||||
if (v3Global.opt.xInitialEdge()) fromp = new AstNot(fromp->fileline(), fromp);
|
||||
AstNode* newinitp = new AstAssign(vscp->fileline(),
|
||||
new AstVarRef(newvarp->fileline(), newvscp, true),
|
||||
fromp);
|
||||
addToInitial(newinitp);
|
||||
// At bottom, assign them
|
||||
AstAssign* finalp
|
||||
= new AstAssign (vscp->fileline(),
|
||||
new AstVarRef(vscp->fileline(), newvscp, true),
|
||||
new AstVarRef(vscp->fileline(), vscp, false));
|
||||
= new AstAssign(vscp->fileline(),
|
||||
new AstVarRef(vscp->fileline(), newvscp, true),
|
||||
new AstVarRef(vscp->fileline(), vscp, false));
|
||||
m_evalFuncp->addFinalsp(finalp);
|
||||
//
|
||||
UINFO(4,"New Last: "<<newvscp<<endl);
|
||||
@@ -154,11 +156,11 @@ private:
|
||||
AstNode* createSenseEquation(AstNodeSenItem* nodesp) {
|
||||
// Nodep may be a list of elements; we need to walk it
|
||||
AstNode* senEqnp = NULL;
|
||||
for (AstNodeSenItem* senp = nodesp; senp; senp=senp->nextp()->castNodeSenItem()) {
|
||||
for (AstNodeSenItem* senp = nodesp; senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||
AstNode* senOnep = NULL;
|
||||
if (AstSenItem* itemp = senp->castSenItem()) {
|
||||
if (AstSenItem* itemp = VN_CAST(senp, SenItem)) {
|
||||
senOnep = createSenItemEquation(itemp);
|
||||
} else if (AstSenGate* itemp = senp->castSenGate()) {
|
||||
} else if (AstSenGate* itemp = VN_CAST(senp, SenGate)) {
|
||||
senOnep = createSenGateEquation(itemp);
|
||||
} else {
|
||||
senp->v3fatalSrc("Strange node under sentree");
|
||||
@@ -175,8 +177,7 @@ private:
|
||||
AstIf* makeActiveIf(AstSenTree* sensesp) {
|
||||
AstNode* senEqnp = createSenseEquation(sensesp->sensesp());
|
||||
if (!senEqnp) sensesp->v3fatalSrc("No sense equation, shouldn't be in sequent activation.");
|
||||
AstIf* newifp = new AstIf (sensesp->fileline(),
|
||||
senEqnp, NULL, NULL);
|
||||
AstIf* newifp = new AstIf(sensesp->fileline(), senEqnp, NULL, NULL);
|
||||
return (newifp);
|
||||
}
|
||||
void clearLastSen() {
|
||||
@@ -239,7 +240,7 @@ private:
|
||||
m_settleFuncp = funcp;
|
||||
}
|
||||
// Process the activates
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Done, clear so we can detect errors
|
||||
UINFO(4," TOPSCOPEDONE "<<nodep<<endl);
|
||||
clearLastSen();
|
||||
@@ -249,13 +250,13 @@ private:
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
//UINFO(4," MOD "<<nodep<<endl);
|
||||
m_modp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modp= NULL;
|
||||
}
|
||||
virtual void visit(AstScope* nodep) {
|
||||
//UINFO(4," SCOPE "<<nodep<<endl);
|
||||
m_scopep = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (AstNode* movep = nodep->finalClksp()) {
|
||||
if (!m_topScopep) nodep->v3fatalSrc("Final clocks under non-top scope");
|
||||
movep->unlinkFrBackWithNext();
|
||||
@@ -311,7 +312,7 @@ private:
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Link to global function
|
||||
if (nodep->formCallTree()) {
|
||||
UINFO(4, " formCallTree "<<nodep<<endl);
|
||||
@@ -342,6 +343,30 @@ private:
|
||||
// Only empty blocks should be leftover on the non-top. Killem.
|
||||
if (nodep->stmtsp()) nodep->v3fatalSrc("Non-empty lower active");
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
} else if (m_mtaskBodyp) {
|
||||
UINFO(4," TR ACTIVE "<<nodep<<endl);
|
||||
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
|
||||
if (nodep->hasClocked()) {
|
||||
if (nodep->hasInitial()) nodep->v3fatalSrc("Initial block should not have clock sensitivity");
|
||||
if (m_lastSenp && nodep->sensesp()->sameTree(m_lastSenp)) {
|
||||
UINFO(4," sameSenseTree\n");
|
||||
} else {
|
||||
clearLastSen();
|
||||
m_lastSenp = nodep->sensesp();
|
||||
// Make a new if statement
|
||||
m_lastIfp = makeActiveIf(m_lastSenp);
|
||||
m_mtaskBodyp->addStmtsp(m_lastIfp);
|
||||
}
|
||||
// Move statements to if
|
||||
m_lastIfp->addIfsp(stmtsp);
|
||||
} else if (nodep->hasInitial() || nodep->hasSettle()) {
|
||||
nodep->v3fatalSrc("MTask should not include initial/settle logic.");
|
||||
} else {
|
||||
// Combo logic. Move statements to mtask func.
|
||||
clearLastSen();
|
||||
m_mtaskBodyp->addStmtsp(stmtsp);
|
||||
}
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
} else {
|
||||
UINFO(4," ACTIVE "<<nodep<<endl);
|
||||
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
|
||||
@@ -376,24 +401,42 @@ private:
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstExecGraph* nodep) {
|
||||
for (m_mtaskBodyp = VN_CAST(nodep->op1p(), MTaskBody);
|
||||
m_mtaskBodyp;
|
||||
m_mtaskBodyp = VN_CAST(m_mtaskBodyp->nextp(), MTaskBody)) {
|
||||
clearLastSen();
|
||||
iterate(m_mtaskBodyp);
|
||||
}
|
||||
clearLastSen();
|
||||
// Move the ExecGraph into _eval. Its location marks the
|
||||
// spot where the graph will execute, relative to other
|
||||
// (serial) logic in the cycle.
|
||||
nodep->unlinkFrBack();
|
||||
addToEvalLoop(nodep);
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit ClockVisitor(AstNetlist* nodep) {
|
||||
m_modp=NULL;
|
||||
m_modp = NULL;
|
||||
m_evalFuncp = NULL;
|
||||
m_topScopep=NULL;
|
||||
m_lastSenp=NULL;
|
||||
m_initFuncp = NULL;
|
||||
m_finalFuncp = NULL;
|
||||
m_settleFuncp = NULL;
|
||||
m_topScopep = NULL;
|
||||
m_lastSenp = NULL;
|
||||
m_lastIfp = NULL;
|
||||
m_scopep = NULL;
|
||||
m_mtaskBodyp = NULL;
|
||||
//
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
// Allow downstream modules to find _eval()
|
||||
// easily without iterating through the tree.
|
||||
nodep->evalp(m_evalFuncp);
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
|
||||
#ifndef _V3CLOCK_H_
|
||||
#define _V3CLOCK_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
|
||||
+38
-40
@@ -36,12 +36,6 @@
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <unistd.h>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Combine.h"
|
||||
@@ -49,6 +43,11 @@
|
||||
#include "V3Stats.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
//######################################################################
|
||||
|
||||
#define COMBINE_MIN_STATEMENTS 50 // Min # of statements to be worth making a function
|
||||
@@ -60,13 +59,8 @@ protected:
|
||||
// STATE
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
|
||||
virtual ~CombBaseVisitor() {}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
//***** optimization levels
|
||||
static bool emptyFunctionDeletion() { return true; }
|
||||
@@ -75,7 +69,7 @@ protected:
|
||||
// Also repair it for DPI functions; when make __common need to insure proper
|
||||
// flags get inherited from the old to new AstCFunc, and that AstText doesn't
|
||||
// get split between functions causing the text to have a danginling reference.
|
||||
bool statementCombine() { return false && duplicateFunctionCombine(); }
|
||||
bool statementCombine() { return false; } // duplicateFunctionCombine();
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -86,16 +80,16 @@ class CombCallVisitor : CombBaseVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
bool m_find; // Find mode vs. delete mode
|
||||
typedef multimap<AstCFunc*,AstCCall*> CallMmap;
|
||||
typedef std::multimap<AstCFunc*,AstCCall*> CallMmap;
|
||||
CallMmap m_callMmap; // Associative array of {function}{call}
|
||||
// METHODS
|
||||
public:
|
||||
void replaceFunc (AstCFunc* oldfuncp, AstCFunc* newfuncp) {
|
||||
void replaceFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
|
||||
if (oldfuncp==newfuncp) return;
|
||||
if (newfuncp) {
|
||||
UINFO(4, " Replace "<<oldfuncp<<" -WITH-> "<<newfuncp<<endl);
|
||||
} else UINFO(4, " Remove "<<oldfuncp<<endl);
|
||||
pair <CallMmap::iterator,CallMmap::iterator> eqrange = m_callMmap.equal_range(oldfuncp);
|
||||
std::pair <CallMmap::iterator,CallMmap::iterator> eqrange = m_callMmap.equal_range(oldfuncp);
|
||||
for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) {
|
||||
CallMmap::iterator eqit = nextit++;
|
||||
AstCCall* callp = eqit->second;
|
||||
@@ -121,7 +115,7 @@ public:
|
||||
m_callMmap.insert(make_pair(nodep->funcp(), nodep));
|
||||
}
|
||||
void deleteCall(AstCCall* nodep) {
|
||||
pair <CallMmap::iterator,CallMmap::iterator> eqrange = m_callMmap.equal_range(nodep->funcp());
|
||||
std::pair<CallMmap::iterator,CallMmap::iterator> eqrange = m_callMmap.equal_range(nodep->funcp());
|
||||
for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) {
|
||||
CallMmap::iterator eqit = nextit++;
|
||||
AstCCall* callp = eqit->second;
|
||||
@@ -141,7 +135,7 @@ private:
|
||||
virtual void visit(AstNodeAssign* nodep) {}
|
||||
virtual void visit(AstNodeMath* nodep) {}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -150,7 +144,7 @@ public:
|
||||
}
|
||||
virtual ~CombCallVisitor() {}
|
||||
void main(AstNetlist* nodep) {
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -165,12 +159,12 @@ private:
|
||||
// VISITORS
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->user3(true);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CombMarkVisitor(AstNode* nodep) {
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CombMarkVisitor() {}
|
||||
};
|
||||
@@ -214,18 +208,18 @@ private:
|
||||
CombineState oldState = m_state;
|
||||
{
|
||||
m_state = STATE_HASH;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
m_state = oldState;
|
||||
}
|
||||
void walkEmptyFuncs() {
|
||||
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
|
||||
AstNode* node1p = it->second;
|
||||
AstCFunc* oldfuncp = node1p->castCFunc();
|
||||
AstCFunc* oldfuncp = VN_CAST(node1p, CFunc);
|
||||
if (oldfuncp
|
||||
&& oldfuncp->emptyBody()
|
||||
&& !oldfuncp->dontCombine()) {
|
||||
UINFO(5," EmptyFunc "<<hex<<V3Hash(oldfuncp->user4p())<<" "<<oldfuncp<<endl);
|
||||
UINFO(5," EmptyFunc "<<std::hex<<V3Hash(oldfuncp->user4p())<<" "<<oldfuncp<<endl);
|
||||
// Mark user3p on entire old tree, so we don't process it more
|
||||
CombMarkVisitor visitor(oldfuncp);
|
||||
m_call.replaceFunc(oldfuncp, NULL);
|
||||
@@ -238,7 +232,7 @@ private:
|
||||
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
|
||||
V3Hash hashval = it->first;
|
||||
AstNode* node1p = it->second;
|
||||
if (!node1p->castCFunc()) continue;
|
||||
if (!VN_IS(node1p, CFunc)) continue;
|
||||
if (hashval.isIllegal()) node1p->v3fatalSrc("Illegal (unhashed) nodes");
|
||||
for (V3Hashed::iterator eqit = it; eqit != m_hashed.end(); ++eqit) {
|
||||
AstNode* node2p = eqit->second;
|
||||
@@ -247,16 +241,16 @@ private:
|
||||
if (node1p->user3p() || node2p->user3p()) continue; // Already merged
|
||||
if (node1p->sameTree(node2p)) { // walk of tree has same comparison
|
||||
// Replace AstCCall's that point here
|
||||
replaceFuncWFunc(node2p->castCFunc(), node1p->castCFunc());
|
||||
replaceFuncWFunc(VN_CAST(node2p, CFunc), VN_CAST(node1p, CFunc));
|
||||
// Replacement may promote a slow routine to fast path
|
||||
if (!node2p->castCFunc()->slow()) node1p->castCFunc()->slow(false);
|
||||
if (!VN_CAST(node2p, CFunc)->slow()) VN_CAST(node1p, CFunc)->slow(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void replaceFuncWFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
|
||||
UINFO(5," DupFunc "<<hex<<V3Hash(newfuncp->user4p())<<" "<<newfuncp<<endl);
|
||||
UINFO(5," and "<<hex<<V3Hash(oldfuncp->user4p())<<" "<<oldfuncp<<endl);
|
||||
UINFO(5," DupFunc "<<std::hex<<V3Hash(newfuncp->user4p())<<" "<<newfuncp<<endl);
|
||||
UINFO(5," and "<<std::hex<<V3Hash(oldfuncp->user4p())<<" "<<oldfuncp<<endl);
|
||||
// Mark user3p on entire old tree, so we don't process it more
|
||||
++m_statCombs;
|
||||
CombMarkVisitor visitor(oldfuncp);
|
||||
@@ -265,7 +259,7 @@ private:
|
||||
pushDeletep(oldfuncp); VL_DANGLING(oldfuncp);
|
||||
}
|
||||
void replaceOnlyCallFunc(AstCCall* nodep) {
|
||||
if (AstCFunc* oldfuncp = nodep->backp()->castCFunc()) {
|
||||
if (AstCFunc* oldfuncp = VN_CAST(nodep->backp(), CFunc)) {
|
||||
//oldfuncp->dumpTree(cout,"MAYDEL: ");
|
||||
if (nodep->nextp()==NULL
|
||||
&& oldfuncp->initsp()==NULL
|
||||
@@ -280,7 +274,7 @@ private:
|
||||
}
|
||||
|
||||
void walkDupCodeStart(AstNode* node1p) {
|
||||
V3Hash hashval (node1p->user4p());
|
||||
V3Hash hashval(node1p->user4p());
|
||||
//UINFO(4," STMT "<<hashval<<" "<<node1p<<endl);
|
||||
//
|
||||
int bestDepth = 0; // Best substitution found in the search
|
||||
@@ -288,7 +282,7 @@ private:
|
||||
AstNode* bestLast1p = NULL;
|
||||
AstNode* bestLast2p = NULL;
|
||||
//
|
||||
pair <V3Hashed::iterator,V3Hashed::iterator> eqrange = m_hashed.mmap().equal_range(hashval);
|
||||
std::pair<V3Hashed::iterator,V3Hashed::iterator> eqrange = m_hashed.mmap().equal_range(hashval);
|
||||
for (V3Hashed::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) {
|
||||
AstNode* node2p = eqit->second;
|
||||
if (node1p==node2p) continue;
|
||||
@@ -396,16 +390,17 @@ private:
|
||||
//In V3Hashed AstNode::user4ClearTree(); // user4p() used on entire tree
|
||||
// Iterate modules backwards, in bottom-up order.
|
||||
// Required so that a module instantiating another can benefit from collapsing.
|
||||
nodep->iterateChildrenBackwards(*this);
|
||||
iterateChildrenBackwards(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
UINFO(4," MOD "<<nodep<<endl);
|
||||
m_modp = nodep;
|
||||
m_modNFuncs = 0;
|
||||
m_walkLast2p = NULL;
|
||||
m_hashed.clear();
|
||||
// Compute hash of all statement trees in the function
|
||||
m_state = STATE_HASH;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_state = STATE_IDLE;
|
||||
if (debug()>=9) {
|
||||
m_hashed.dumpFilePrefixed("combine");
|
||||
@@ -421,7 +416,7 @@ private:
|
||||
// Walk the statements looking for large replicated code sections
|
||||
if (statementCombine()) {
|
||||
m_state = STATE_DUP;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_state = STATE_IDLE;
|
||||
}
|
||||
m_modp = NULL;
|
||||
@@ -432,7 +427,7 @@ private:
|
||||
if (m_state == STATE_HASH) {
|
||||
hashStatement(nodep); // Hash the entire function - it might be identical
|
||||
} else if (m_state == STATE_DUP) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
m_funcp = NULL;
|
||||
@@ -452,16 +447,19 @@ private:
|
||||
virtual void visit(AstTraceDecl*) {}
|
||||
virtual void visit(AstTraceInc*) {}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit CombineVisitor(AstNetlist* nodep) {
|
||||
m_modp=NULL;
|
||||
m_funcp = NULL;
|
||||
m_state = STATE_IDLE;
|
||||
nodep->accept(*this);
|
||||
m_modp = NULL;
|
||||
m_funcp = NULL;
|
||||
m_modNFuncs = 0;
|
||||
m_walkLast1p = NULL;
|
||||
m_walkLast2p = NULL;
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CombineVisitor() {
|
||||
V3Stats::addStat("Optimizations, Combined CFuncs", m_statCombs);
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
|
||||
#ifndef _V3COMBINE_H_
|
||||
#define _V3COMBINE_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
|
||||
+7
-6
@@ -20,14 +20,15 @@
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3String.h"
|
||||
#include "V3Config.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
//######################################################################
|
||||
|
||||
class V3ConfigLine {
|
||||
@@ -47,11 +48,11 @@ public:
|
||||
return (m_on>rh.m_on);
|
||||
}
|
||||
};
|
||||
ostream& operator<<(ostream& os, V3ConfigLine rhs) { return os<<rhs.m_lineno<<", "<<rhs.m_code<<", "<<rhs.m_on; }
|
||||
std::ostream& operator<<(std::ostream& os, V3ConfigLine rhs) { return os<<rhs.m_lineno<<", "<<rhs.m_code<<", "<<rhs.m_on; }
|
||||
|
||||
class V3ConfigIgnores {
|
||||
typedef multiset<V3ConfigLine> IgnLines; // list of {line,code,on}
|
||||
typedef map<string,IgnLines> IgnFiles; // {filename} => list of {line,code,on}
|
||||
typedef std::multiset<V3ConfigLine> IgnLines; // list of {line,code,on}
|
||||
typedef std::map<string,IgnLines> IgnFiles; // {filename} => list of {line,code,on}
|
||||
|
||||
// MEMBERS
|
||||
string m_lastFilename; // Last filename looked up
|
||||
|
||||
+2
-1
@@ -20,9 +20,10 @@
|
||||
|
||||
#ifndef _V3CONFIG_H_
|
||||
#define _V3CONFIG_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <string>
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3FileLine.h"
|
||||
|
||||
|
||||
+397
-397
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user