mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 18:13:50 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
935032366f | ||
|
|
1160dddc6d | ||
|
|
293a5f402b | ||
|
|
ca09f73446 | ||
|
|
e814855357 | ||
|
|
692306ef44 | ||
|
|
ffdc0afe5f | ||
|
|
98037cad56 | ||
|
|
61e3536163 | ||
|
|
a1a186a86c | ||
|
|
04e0c7e4f1 | ||
|
|
833686446c | ||
|
|
c6dae40cf6 | ||
|
|
cd737065f2 | ||
|
|
010084201a | ||
|
|
60f7eff701 | ||
|
|
d1862e25eb | ||
|
|
b0018fd680 | ||
|
|
2742a8c813 | ||
|
|
05e12ab60e | ||
|
|
9bda2cb4ad | ||
|
|
393b9e435d | ||
|
|
e87a726989 | ||
|
|
62387a0e32 | ||
|
|
31079ca8b5 | ||
|
|
4184ad1921 | ||
|
|
e7ebe0e280 | ||
|
|
c14bbb9421 | ||
|
|
b1b92b7dd4 | ||
|
|
a14394dbb5 | ||
|
|
96b7831243 | ||
|
|
1e440765c0 | ||
|
|
2ccf49031b | ||
|
|
0abc856be9 | ||
|
|
d2a8fa7440 | ||
|
|
106d635d0f | ||
|
|
cff9f8143e | ||
|
|
cfa401e01d | ||
|
|
d6195e354f | ||
|
|
c0892dd11c | ||
|
|
899de9a282 | ||
|
|
4b593f8eb3 | ||
|
|
28b6e79cf7 | ||
|
|
fe5822ba54 | ||
|
|
c496649c49 | ||
|
|
37e3c6da70 | ||
|
|
4cb5c1e1db | ||
|
|
b7a7504ba5 | ||
|
|
3a5cbd5b67 | ||
|
|
27883b52d6 | ||
|
|
b95ee84343 | ||
|
|
185e5d8f42 | ||
|
|
987ce927eb | ||
|
|
b08c694cd6 | ||
|
|
e69a8e838d | ||
|
|
61612582e6 | ||
|
|
8b00939f0c | ||
|
|
da5644211f | ||
|
|
c1d7bfa617 | ||
|
|
c26ce25cea | ||
|
|
758264dc77 | ||
|
|
304697d133 | ||
|
|
fdbf465eda | ||
|
|
90102d9867 | ||
|
|
a1bb471aca | ||
|
|
4ef37d9411 | ||
|
|
f3b10df454 | ||
|
|
f5a226a183 | ||
|
|
5515eed0b6 | ||
|
|
381c87a1eb | ||
|
|
20e896ec21 | ||
|
|
70603bb752 | ||
|
|
7cb6dc664e | ||
|
|
dae9fa5053 | ||
|
|
aa1a0b0f13 | ||
|
|
371bf01957 | ||
|
|
ef7759a522 | ||
|
|
0ef9087f89 | ||
|
|
00059987a1 | ||
|
|
8f090176a6 | ||
|
|
43e8263d1f | ||
|
|
ac622f9db8 | ||
|
|
749a66b7a2 | ||
|
|
fc80ace6e4 | ||
|
|
b8f6b262e4 | ||
|
|
b64e89f7ed | ||
|
|
c3bec344a4 | ||
|
|
9731ec76e3 | ||
|
|
a9646cf45c | ||
|
|
5f597dd9fc | ||
|
|
7d57c3ca78 | ||
|
|
4739956cfe | ||
|
|
e202a6324a | ||
|
|
66d70c8b37 | ||
|
|
02aa639425 | ||
|
|
55da66164b | ||
|
|
2560fc867f | ||
|
|
f5c9deecb8 | ||
|
|
20af8a36a7 | ||
|
|
1c1c805b07 | ||
|
|
9029da5ab8 | ||
|
|
def7c8fe4d | ||
|
|
9184c4f501 | ||
|
|
44809f1843 | ||
|
|
87ced0cc3e | ||
|
|
741bb5328e | ||
|
|
33650336f5 | ||
|
|
bbce1dc256 | ||
|
|
8ab51dbf22 | ||
|
|
fd4595d6b4 | ||
|
|
204804ae52 | ||
|
|
c2819923c5 | ||
|
|
68f1432a68 | ||
|
|
0defb61a18 | ||
|
|
0fc805202c | ||
|
|
a7374e84a2 | ||
|
|
76681fd931 | ||
|
|
2418df7bb2 | ||
|
|
97d8d32049 | ||
|
|
08c8b0d7d6 | ||
|
|
81fd3e4732 | ||
|
|
d384a69877 | ||
|
|
a8b480559d | ||
|
|
f1b8b1d99b | ||
|
|
f67637144f | ||
|
|
4b274a8d4d | ||
|
|
c678e7ec3e | ||
|
|
2a79e46c46 | ||
|
|
d09b6a7d2c | ||
|
|
35eac0c457 | ||
|
|
cac0b0f316 | ||
|
|
8519dc1383 | ||
|
|
72f198d79e | ||
|
|
1cb8091125 | ||
|
|
18c543f2f2 | ||
|
|
496b9f9c63 | ||
|
|
1ee46ac5e1 | ||
|
|
f37c875be8 | ||
|
|
56dc66d842 | ||
|
|
7ae54ef973 | ||
|
|
4ed00c563c | ||
|
|
a49bfe871c | ||
|
|
6d97fea2f5 | ||
|
|
da833d55fe | ||
|
|
960813cb0f | ||
|
|
cd56dfee1c | ||
|
|
3e03cd5a4d | ||
|
|
cd9e9da4b5 | ||
|
|
f64d9d87f6 | ||
|
|
43aa3229fb | ||
|
|
27d53691bd | ||
|
|
959793cde3 | ||
|
|
c3d64d9743 | ||
|
|
f9806595f2 | ||
|
|
34a0bb448e | ||
|
|
8681861be9 | ||
|
|
fffc63970d | ||
|
|
8f527a0c6e | ||
|
|
c69ddc46f8 | ||
|
|
4ec8bc3589 | ||
|
|
536bdf506e | ||
|
|
d1836b7b6f | ||
|
|
00fe36f44c | ||
|
|
5adc856950 | ||
|
|
bc3e24c8cd | ||
|
|
2c8456ea75 | ||
|
|
519cc7d61c | ||
|
|
0f4daa12e4 | ||
|
|
6bad0e14ce | ||
|
|
3ec3c2c268 | ||
|
|
b8e804f05b | ||
|
|
cdeb6e792f | ||
|
|
deebfa3239 | ||
|
|
f937e3282f | ||
|
|
b46664c70a | ||
|
|
33af14eba8 | ||
|
|
18b0f6387d | ||
|
|
43ecaec9a0 | ||
|
|
275885a797 | ||
|
|
68aa731ce9 | ||
|
|
b90fce55f4 | ||
|
|
44173b40a1 | ||
|
|
ab13a2ebdc | ||
|
|
19875d2e85 | ||
|
|
1de33b9fb7 | ||
|
|
90c917ee83 | ||
|
|
4ab4c0c8ba | ||
|
|
9907d211ff | ||
|
|
13933743ad | ||
|
|
fc25721e55 | ||
|
|
fa64f8fb32 | ||
|
|
ab4063f098 | ||
|
|
8bb77f86ec | ||
|
|
42d918c163 | ||
|
|
e3dd70c373 | ||
|
|
9273fafbbf | ||
|
|
d9c893af11 | ||
|
|
a9c4a96c0f | ||
|
|
32e99ba0d9 | ||
|
|
da8930a57a | ||
|
|
1166728a8d | ||
|
|
30fa5e41be | ||
|
|
cf39331d1f | ||
|
|
bac84726e1 | ||
|
|
ddef61d62e | ||
|
|
4081a1a539 | ||
|
|
1691bbfe25 | ||
|
|
17cc452f79 | ||
|
|
8ecdc85cf7 | ||
|
|
5ad3c4e499 | ||
|
|
8073e8bb46 | ||
|
|
8a9d6d225b | ||
|
|
c7499133b2 | ||
|
|
687dd440a9 | ||
|
|
f1bb0bb725 | ||
|
|
a8168d5d62 | ||
|
|
896b18e024 | ||
|
|
766ad14ae0 | ||
|
|
add3811f46 | ||
|
|
f55177a49f | ||
|
|
bdceb08963 | ||
|
|
825d6b87a8 | ||
|
|
61e2e55ba5 | ||
|
|
e6b75f752b | ||
|
|
a4f5d95648 | ||
|
|
686baaf2cf | ||
|
|
76b3776fa3 | ||
|
|
3ed2af638b |
@@ -108,4 +108,3 @@ Standard: Cpp11
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
...
|
||||
|
||||
|
||||
@@ -13,4 +13,6 @@ Can you attach an example that shows the issue? (Must be openly licensed, ideal
|
||||
|
||||
What 'verilator --version' are you using? Did you try it with the git master version?
|
||||
|
||||
What OS and distribution are you using?
|
||||
|
||||
Would you be willing to try to fix Verilator yourself with assistance?
|
||||
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04]
|
||||
os: [ubuntu-20.04, ubuntu-18.04]
|
||||
compiler:
|
||||
- { cc: clang, cxx: clang++ }
|
||||
- { cc: gcc, cxx: g++ }
|
||||
@@ -37,11 +37,9 @@ jobs:
|
||||
exclude:
|
||||
# Build pull requests only with ubuntu-20.04 and without m32
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-16.04' || 'do-not-exclude' }}
|
||||
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
|
||||
# Build -m32 only on ubuntu-20.04
|
||||
- {os: ubuntu-18.04, m32: 1}
|
||||
- {os: ubuntu-16.04, m32: 1}
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.compiler.cc }} ${{ matrix.m32 && '| -m32' || '' }}
|
||||
env:
|
||||
@@ -92,7 +90,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04]
|
||||
os: [ubuntu-20.04, ubuntu-18.04]
|
||||
compiler:
|
||||
- { cc: clang, cxx: clang++ }
|
||||
- { cc: gcc, cxx: g++ }
|
||||
@@ -101,11 +99,9 @@ jobs:
|
||||
exclude:
|
||||
# Build pull requests only with ubuntu-20.04 and without m32
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-16.04' || 'do-not-exclude' }}
|
||||
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
|
||||
# Build -m32 only on ubuntu-20.04
|
||||
- {os: ubuntu-18.04, m32: 1}
|
||||
- {os: ubuntu-16.04, m32: 1}
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: Test | ${{ matrix.os }} | ${{ matrix.compiler.cc }} | ${{ matrix.suite }} ${{ matrix.m32 && '| -m32' || '' }}
|
||||
env:
|
||||
|
||||
@@ -39,3 +39,4 @@ verilator-config-version.cmake
|
||||
**/__pycache__/*
|
||||
**/_build/*
|
||||
**/obj_dir/*
|
||||
/.vscode/
|
||||
|
||||
@@ -8,6 +8,89 @@ The changes in each Verilator version are described below. The
|
||||
contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
|
||||
Verilator 4.216 2021-12-05
|
||||
==========================
|
||||
|
||||
**Major:**
|
||||
|
||||
* Add --lib-create, similar to --protect-lib but without protections.
|
||||
* Support tracing through --hierarchical/--lib-create libraries (#3200).
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Internal code cleanups and improvements. [Geza Lore]
|
||||
* Improve --thread verilation-time performance.
|
||||
* Support task name in $display %m (#3211). [Julie Schwartz]
|
||||
* Make 'bit', 'logic' and 'time' types unsigned by default. [Geza Lore]
|
||||
* Optimize $random concatenates/selects (#3114).
|
||||
* Fix array method names with parenthesis (#3181) (#3183). [Teng Huang]
|
||||
* Fix split_var assign merging (#3177) (#3179). [Yutetsu TAKATSUKASA]
|
||||
* Fix wrong bit op tree optimization (#3185). [Yutetsu TAKATSUKASA]
|
||||
* Fix some SliceSels not being constants (#3186) (#3218). [Michaël Lefebvre]
|
||||
* Fix nested generate if genblk naming (#3189). [yanx21]
|
||||
* Fix hang on recursive definition error (#3199). [Jonathan Kimmitt]
|
||||
* Fix display of signed without format (#3204). [Julie Schwartz]
|
||||
* Fix display of empty string constant (#3207) (#3215). [Julie Schwartz]
|
||||
* Fix incorrect width after and-or optimization (#3208). [Julie Schwartz]
|
||||
* Fix $fopen etc on integer arrays (#3214). [adrienlemasle]
|
||||
* Fix $size on dynamic strings (#3216).
|
||||
* Fix %0 format on $value$plusargs (#3217).
|
||||
* Fix timescale portability on Arm64 (#3222).
|
||||
|
||||
|
||||
Verilator 4.214 2021-10-17
|
||||
==========================
|
||||
|
||||
**Major:**
|
||||
|
||||
* Add profile-guided optmization of mtasks (#3150).
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Verilator_gantt has removed the ASCII graphics, use the VCD output instead.
|
||||
* Verilator_gantt now shows the predicted mtask times, eval times, and additional statistics.
|
||||
* Verilator_gantt data files now include processor information, to allow later processing.
|
||||
* Support displaying x and z in $display task (#3107) (#3109). [Iru Cai]
|
||||
* Fix verilator_profcfunc profile accounting (#3115).
|
||||
* Fix display has no time units on class function (#3116). [Damien Pretet]
|
||||
* Fix removing if statement with side effect in condition (#3131). [Alexander Grobman]
|
||||
* Fix --waiver-output for multiline warnings (#2429) (#3141). [Keith Colbert]
|
||||
* Fix internal error on bad widths (#3140) (#3145). [Zhanglei Wang]
|
||||
* Fix crash on clang 12/13 (#3148). [Kouping Hsu]
|
||||
* Fix cygwin compile error due to missing -std=gnu++14 (#3149). [Sun Kim]
|
||||
* Fix $urandom_range when the range is 0 ... UINT_MAX (#3161). [Iru Cai]
|
||||
* Fix constructor-parameter argument comma-separation in C++ (#3162). [Matthew Ballance]
|
||||
* Fix missing install of vl_file_copy/vl_hier_graph (#3165). [Popolon]
|
||||
* Fix calling new with arguments in same class (#3166). [Matthew Ballance]
|
||||
* Fix false EOFNEWLINE warning when DOS carriage returns present (#3171).
|
||||
|
||||
|
||||
Verilator 4.212 2021-09-01
|
||||
==========================
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Fix re-evaluation of logic dependent on state set in DPI exports (#3091). [Geza Lore]
|
||||
* Support unpacked array localparams in tasks/functions (#3078). [Geza Lore]
|
||||
* Support timeunit/timeprecision in $unit.
|
||||
* Support assignment patterns as children of pins (#3041). [Krzysztof Bieganski]
|
||||
* Add --instr-count-dpi to tune assumed DPI import cost for multithreaded
|
||||
model scheduling. Default value changed to 200 (#3068). [Yinan Xu]
|
||||
* Output files are split based on the set of headers required
|
||||
in order to aid incremental compilation via ccache (#3071). [Geza Lore]
|
||||
* Parameter values are now emitted as 'static constexpr' instead of enum.
|
||||
C++ direct references to parameters might require updating (#3077). [Geza Lore]
|
||||
* Refactored Verilated include files; include verilated.h not verilated_heavy.h.
|
||||
* Add header guards on Dpi.h generated files (#2979). [Tood Strader]
|
||||
* Add XML ccall, constpool, initarray, and if/while begins (#3080). [Steven Hugg]
|
||||
* Add error when constant function under a generate (#3103). [Don Owen]
|
||||
* Fix -G to treat simple integer literals as signed (#3060). [Anikin1610]
|
||||
* Fix emitted string array initializers (#2895). [Iztok Jeras]
|
||||
* Fix bitop tree optimization dropping necessary & operator (#3096). [Flavien Solt]
|
||||
* Fix internal error on wide -x-initial unique (#3106). [Alexandre Joannou]
|
||||
* Fix traces to show array instances with brackets (#3092) (#3095). [Pieter Kapsenberg]
|
||||
|
||||
|
||||
Verilator 4.210 2021-07-07
|
||||
==========================
|
||||
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
\.ccache/
|
||||
\.clang-format
|
||||
\.clang-tidy
|
||||
\.git/
|
||||
\.git$
|
||||
\.github/
|
||||
\.svn/
|
||||
\.(bak|old)/
|
||||
\.(bak|old)$
|
||||
\.tar\.
|
||||
.*\.tgz
|
||||
.*\.log
|
||||
\..*\.swp
|
||||
.*\.tmp
|
||||
.*\.tidy
|
||||
.*\.tex
|
||||
.*\.key
|
||||
.*\.vcd
|
||||
.*\.1
|
||||
\.codacy\.yml
|
||||
\.travis\.yml
|
||||
/_build/
|
||||
/build/
|
||||
/obj_dbg/
|
||||
/obj_dir/
|
||||
/obj_dist/
|
||||
/obj_iv/
|
||||
/obj_ms/
|
||||
/obj_nc/
|
||||
/obj_opt/
|
||||
/obj_vcs/
|
||||
/obj_vlt/
|
||||
/obj_vltmt/
|
||||
INCA_libs/
|
||||
/cov_work/
|
||||
/logs/
|
||||
^Makefile$
|
||||
README.html
|
||||
bin/verilator_bin.*
|
||||
bin/verilator_coverage_bin.*
|
||||
docs/.*\.html$
|
||||
docs/_build/
|
||||
docs/clang-format.txt$
|
||||
docs/doxygen-doc/.*
|
||||
docs/spelling.txt
|
||||
examples/xml_py/copied/
|
||||
examples/xml_py/graph.*
|
||||
sonar-project.properties
|
||||
src/Makefile$
|
||||
src/Makefile_obj$
|
||||
include/verilated.mk$
|
||||
test_regress/.gdbinit$
|
||||
test_regress/transcript
|
||||
codecov.yml
|
||||
config.cache$
|
||||
config.status$
|
||||
verilator\.log
|
||||
verilator\.tex
|
||||
verilator-config.cmake$
|
||||
verilator-config-version.cmake$
|
||||
verilator.pc$
|
||||
verilator_bin.*
|
||||
verilator_coverage_bin.*
|
||||
.vcsmx_rebuild$
|
||||
ncverilog.history
|
||||
autom4te\.cache/
|
||||
nodist/
|
||||
ci/
|
||||
/simv$
|
||||
/simv.daidir/
|
||||
/vc_hdrs.h$
|
||||
/csrc/
|
||||
obj_dir.*
|
||||
TAGS
|
||||
gmon.out
|
||||
.*~
|
||||
+11
-167
@@ -118,98 +118,6 @@ INFOS = verilator.html verilator.pdf
|
||||
|
||||
INFOS_OLD = README README.html README.pdf
|
||||
|
||||
# Files that can be generated, but should be up to date for a distribution.
|
||||
DISTDEP = info Makefile
|
||||
|
||||
DISTFILES1 = $(INFOS) .gitignore \
|
||||
*.in *.ac \
|
||||
Artistic \
|
||||
Changes \
|
||||
LICENSE \
|
||||
MANIFEST.SKIP \
|
||||
README.rst \
|
||||
verilator-config.cmake.in \
|
||||
verilator-config-version.cmake.in \
|
||||
bin/verilator \
|
||||
bin/verilator_ccache_report \
|
||||
bin/verilator_coverage \
|
||||
bin/verilator_difftree \
|
||||
bin/verilator_gantt \
|
||||
bin/verilator_includer \
|
||||
bin/verilator_profcfunc \
|
||||
docs/.gitignore \
|
||||
docs/CONTRIBUTING.rst \
|
||||
docs/CONTRIBUTORS \
|
||||
docs/Makefile \
|
||||
docs/_static/*.png \
|
||||
docs/_static/css/* \
|
||||
docs/bin/* \
|
||||
docs/gen/* \
|
||||
docs/guide/*.py \
|
||||
docs/guide/*.rst \
|
||||
docs/guide/figures/* \
|
||||
docs/install.rst \
|
||||
docs/internals.rst \
|
||||
docs/internals.rst \
|
||||
docs/verilated.dox \
|
||||
docs/xml.rst \
|
||||
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/config_rev \
|
||||
src/vlcovgen src/mkinstalldirs \
|
||||
src/.gdbinit \
|
||||
src/*.pl src/*.pod \
|
||||
examples/*/.*ignore examples/*/Makefile* \
|
||||
examples/*/*.[chv]* examples/*/vl_* \
|
||||
examples/*/CMakeLists.txt \
|
||||
|
||||
DISTFILES2 = \
|
||||
test_*/.*ignore test_*/Makefile* test_*/*.cpp \
|
||||
test_*/*.pl test_*/*.v test_*/*.vc test_*/*.vh \
|
||||
test_regress/*.pl \
|
||||
test_regress/Makefile \
|
||||
test_regress/Makefile_obj \
|
||||
test_regress/input.vc \
|
||||
test_regress/CMakeLists.txt \
|
||||
test_regress/t/t*/*.sv* \
|
||||
test_regress/t/t*/*.v* \
|
||||
test_regress/t/t*/*/*.sv* \
|
||||
test_regress/t/t*/*/*.v* \
|
||||
test_regress/t/t*/*.cpp \
|
||||
test_regress/t/t*/CMakeLists.txt \
|
||||
test_regress/t/*.cpp \
|
||||
test_regress/t/*.h \
|
||||
test_regress/t/*.dat \
|
||||
test_regress/t/*.mem \
|
||||
test_regress/t/*.out \
|
||||
test_regress/t/*.pl \
|
||||
test_regress/t/*.pf \
|
||||
test_regress/t/*.v* \
|
||||
|
||||
INST_PROJ_FILES = \
|
||||
bin/verilator \
|
||||
bin/verilator_ccache_report \
|
||||
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 = \
|
||||
bin/verilator_bin$(EXEEXT) \
|
||||
bin/verilator_bin_dbg$(EXEEXT) \
|
||||
bin/verilator_coverage_bin_dbg$(EXEEXT) \
|
||||
|
||||
EXAMPLES_FIRST = \
|
||||
examples/make_hello_c \
|
||||
examples/make_hello_sc \
|
||||
@@ -217,7 +125,7 @@ EXAMPLES_FIRST = \
|
||||
EXAMPLES = $(EXAMPLES_FIRST) $(filter-out $(EXAMPLES_FIRST), $(sort $(wildcard examples/*)))
|
||||
|
||||
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
|
||||
VL_INST_MAN_FILES = verilator.1 verilator_coverage.1 verilator_gantt.1 verilator_profcfunc.1
|
||||
VL_INST_MAN_FILES = verilator.1 verilator_coverage.1
|
||||
|
||||
default: all
|
||||
all: all_nomsg msg_test
|
||||
@@ -299,8 +207,10 @@ VL_INST_INC_SRCDIR_FILES = \
|
||||
include/vltstd/*.[chv]* \
|
||||
|
||||
VL_INST_DATA_SRCDIR_FILES = \
|
||||
examples/*/*.[chv]* examples/*/Makefile* \
|
||||
examples/*/CMakeLists.txt
|
||||
examples/*/*.[chv]* \
|
||||
examples/*/CMakeLists.txt \
|
||||
examples/*/Makefile* \
|
||||
examples/*/vl_* \
|
||||
|
||||
installbin:
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(bindir)
|
||||
@@ -386,44 +296,7 @@ uninstall:
|
||||
install: all_nomsg install-all
|
||||
install-all: installbin installman installdata install-msg
|
||||
|
||||
install-here: installman ftp
|
||||
|
||||
ifeq ($(VERILATOR_AUTHOR_SITE),1) # Local... Else don't burden users
|
||||
DISTNAMEREV = $(shell sed -e '/DTVERSION/!d' -e 's/.*verilator_\([^"]*\).*/\1/' -e q ${srcdir}/src/config_rev.h)
|
||||
|
||||
VERILATOR_CAD_DIR = $(CAD_DIR)/verilator/$(DISTNAMEREV)/$(DIRPROJECT_ARCH)
|
||||
INST_PROJ_CVS = cp_if_cvs_diff
|
||||
|
||||
install-cadtools: dist
|
||||
@echo "Install-project to $(CAD_DIR)"
|
||||
strip bin/verilator_bin*
|
||||
strip bin/verilator_coverage_bin*
|
||||
$(MAKE) install-cadtools-quick
|
||||
$(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/man/man1
|
||||
for p in $(VL_INST_MAN_FILES) ; do \
|
||||
$(INSTALL_DATA) $$p $(VERILATOR_CAD_DIR)/man/man1/$$p; \
|
||||
done
|
||||
$(INST_PROJ_CVS) $(DISTNAME).tgz $(VERILATOR_CAD_DIR)/verilator.tgz
|
||||
rm $(DISTNAME).tgz
|
||||
|
||||
install-cadtools-quick:
|
||||
ifeq ($(CFG_WITH_DEFENV),yes)
|
||||
@echo "%Error: Reconfigure with './configure --disable-defenv' to avoid hardcoded paths."
|
||||
false
|
||||
endif
|
||||
@echo "Install-cadtools-quick (no strip) to $(VERILATOR_CAD_DIR)"
|
||||
$(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
|
||||
for p in $(INST_PROJ_BIN_FILES) ; do \
|
||||
$(INST_PROJ_CVS) $$p $(VERILATOR_CAD_DIR)/$$p; \
|
||||
done
|
||||
|
||||
# VERILATOR_AUTHOR_SITE
|
||||
endif
|
||||
install-here: installman info
|
||||
|
||||
# Use --xml flag to see the cppcheck code to use for suppression
|
||||
CPPCHECK_CPP = $(wildcard \
|
||||
@@ -481,6 +354,9 @@ clang-format:
|
||||
|
||||
PY_PROGRAMS = \
|
||||
bin/verilator_ccache_report \
|
||||
bin/verilator_difftree \
|
||||
bin/verilator_gantt \
|
||||
bin/verilator_profcfunc \
|
||||
examples/xml_py/vl_file_copy \
|
||||
examples/xml_py/vl_hier_graph \
|
||||
docs/guide/conf.py \
|
||||
@@ -524,9 +400,6 @@ lint-py:
|
||||
format-pl-exec:
|
||||
-chmod a+x test_regress/t/*.pl
|
||||
|
||||
|
||||
ftp: info
|
||||
|
||||
install-msg:
|
||||
@echo
|
||||
@echo "Installed binaries to $(DESTDIR)$(bindir)/verilator"
|
||||
@@ -597,15 +470,6 @@ TAGS: $(TAGFILES)
|
||||
doxygen:
|
||||
$(MAKE) -C docs doxygen
|
||||
|
||||
######################################################################
|
||||
# Test targets
|
||||
|
||||
dist-file-list:
|
||||
@echo "begin-dist-file-list:"; # Scripts look for this
|
||||
@echo $(wildcard $(DISTFILES1))
|
||||
@echo $(wildcard $(DISTFILES2))
|
||||
@echo "end-dist-file-list:"; # Scripts look for this
|
||||
|
||||
######################################################################
|
||||
# Distributions
|
||||
|
||||
@@ -613,36 +477,16 @@ DISTTITLE := Verilator $(word 1,$(PACKAGE_VERSION))
|
||||
DISTNAME := verilator-$(word 1,$(PACKAGE_VERSION))
|
||||
DISTDATEPRE := $(word 2,$(PACKAGE_VERSION))
|
||||
DISTDATE := $(subst /,-,$(DISTDATEPRE))
|
||||
|
||||
DISTTAGNAME := $(subst .,_,$(subst -,_,$(DISTNAME)))
|
||||
|
||||
tag:
|
||||
svnorcvs tag $(DISTTAGNAME)
|
||||
|
||||
# Don't depend on DISTFILES because there's no rule for "standards.info*".
|
||||
dist: $(DISTDEP) maintainer-copy
|
||||
-rm -fr $(DISTNAME)
|
||||
for file in $(DISTFILES1); do \
|
||||
mkdir -p `dirname $(DISTNAME)/$$file` >/dev/null ; \
|
||||
ln $$file $(DISTNAME)/$$file \
|
||||
|| { echo copying $$file instead; cp -p $$file $(DISTNAME)/$$file;}; \
|
||||
done; true;
|
||||
for file in $(DISTFILES2); do \
|
||||
mkdir -p `dirname $(DISTNAME)/$$file` >/dev/null ; \
|
||||
ln $$file $(DISTNAME)/$$file \
|
||||
|| { echo copying $$file instead; cp -p $$file $(DISTNAME)/$$file;}; \
|
||||
done; true;
|
||||
chmod -R a+r $(DISTNAME)
|
||||
tar chf $(DISTNAME).tar $(DISTNAME)
|
||||
gzip --force --best $(DISTNAME).tar
|
||||
mv $(DISTNAME).tar.gz $(DISTNAME).tgz
|
||||
rm -fr $(DISTNAME)
|
||||
|
||||
maintainer-diff:
|
||||
svnorcvs diff $(DISTTAGNAME)
|
||||
|
||||
preexist:
|
||||
svnorcvs nexists $(DISTTAGNAME)
|
||||
|
||||
maintainer-dist: preexist dist tag
|
||||
svnorcvs release $(DISTNAME).tgz
|
||||
maintainer-dist: preexist tag
|
||||
svnorcvs release $(DISTTAGNAME)
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
:target: https://www.gnu.org/licenses/lgpl-3.0]
|
||||
.. image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg
|
||||
:target: https://opensource.org/licenses/Artistic-2.0
|
||||
.. image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages
|
||||
:target: https://repology.org/project/verilator/versions
|
||||
.. image:: https://api.codacy.com/project/badge/Grade/fa78caa433c84a4ab9049c43e9debc6f
|
||||
:target: https://www.codacy.com/gh/verilator/verilator
|
||||
.. image:: https://codecov.io/gh/verilator/verilator/branch/master/graph/badge.svg
|
||||
@@ -12,6 +14,7 @@
|
||||
.. image:: https://github.com/verilator/verilator/workflows/build/badge.svg
|
||||
:target: https://github.com/verilator/verilator/actions?query=workflow%3Abuild
|
||||
|
||||
|
||||
Welcome to Verilator
|
||||
====================
|
||||
|
||||
|
||||
+36
-34
@@ -34,11 +34,11 @@ my $opt_quiet_exit;
|
||||
|
||||
# No arguments can't do anything useful. Give help
|
||||
if ($#ARGV < 0) {
|
||||
pod2usage(-exitstatus=>2, -verbose=>0);
|
||||
pod2usage(-exitstatus => 2, -verbose => 0);
|
||||
}
|
||||
|
||||
# Insert debugging options up front
|
||||
push @ARGV, (split ' ',$ENV{VERILATOR_TEST_FLAGS}||"");
|
||||
push @ARGV, (split ' ', $ENV{VERILATOR_TEST_FLAGS} || "");
|
||||
|
||||
# We sneak a look at the flags so we can do some pre-environment checks
|
||||
# All flags will hit verilator...
|
||||
@@ -46,7 +46,7 @@ foreach my $sw (@ARGV) {
|
||||
push @Opt_Verilator_Sw, $sw;
|
||||
}
|
||||
|
||||
Getopt::Long::config("no_auto_abbrev","pass_through");
|
||||
Getopt::Long::config("no_auto_abbrev", "pass_through");
|
||||
if (! GetOptions(
|
||||
# Major operating modes
|
||||
"help" => \&usage,
|
||||
@@ -60,7 +60,7 @@ if (! GetOptions(
|
||||
# Additional parameters
|
||||
"<>" => sub {}, # Ignored
|
||||
)) {
|
||||
pod2usage(-exitstatus=>2, -verbose=>0);
|
||||
pod2usage(-exitstatus => 2, -verbose => 0);
|
||||
}
|
||||
|
||||
if ($opt_gdbbt && !gdb_works()) {
|
||||
@@ -73,12 +73,12 @@ if ($opt_gdbbt && !gdb_works()) {
|
||||
# Starting with that, escape all special chars for the shell;
|
||||
# The shell will undo the escapes and the verilator binary should
|
||||
# then see exactly the contents of @Opt_Verilator_Sw.
|
||||
my @quoted_sw = map {sh_escape($_)} @Opt_Verilator_Sw;
|
||||
my @quoted_sw = map { sh_escape($_) } @Opt_Verilator_Sw;
|
||||
if ($opt_gdb) {
|
||||
# Generic GDB interactive
|
||||
run (aslr_off()
|
||||
.($ENV{VERILATOR_GDB}||"gdb")
|
||||
." ".verilator_bin()
|
||||
. ($ENV{VERILATOR_GDB} || "gdb")
|
||||
. " " . verilator_bin()
|
||||
# Note, uncomment to set breakpoints before running:
|
||||
# ." -ex 'break main'"
|
||||
|
||||
@@ -87,42 +87,41 @@ if ($opt_gdb) {
|
||||
# escapes as you would expect in a double-quoted string.
|
||||
# That's not true for a single-quoted string, where \'
|
||||
# actually terminates the string -- not what we want!
|
||||
." -ex \"run ".join(' ', @quoted_sw)."\""
|
||||
." -ex 'set width 0'"
|
||||
." -ex 'bt'");
|
||||
. " -ex \"run " . join(' ', @quoted_sw) . "\""
|
||||
. " -ex 'set width 0'"
|
||||
. " -ex 'bt'");
|
||||
} elsif ($opt_rr) {
|
||||
# Record with rr
|
||||
run (aslr_off()
|
||||
."rr record ".verilator_bin()
|
||||
." ".join(' ', @quoted_sw));
|
||||
. "rr record " . verilator_bin()
|
||||
. " " . join(' ', @quoted_sw));
|
||||
} elsif ($opt_gdbbt && $Debug) {
|
||||
# Run under GDB to get gdbbt
|
||||
run (aslr_off()
|
||||
."gdb"
|
||||
." ".verilator_bin()
|
||||
." --batch --quiet --return-child-result"
|
||||
." -ex \"run ".join(' ', @quoted_sw)."\""
|
||||
." -ex 'set width 0'"
|
||||
." -ex 'bt' -ex 'quit'");
|
||||
. "gdb"
|
||||
. " " . verilator_bin()
|
||||
. " --batch --quiet --return-child-result"
|
||||
. " -ex \"run " . join(' ', @quoted_sw)."\""
|
||||
. " -ex 'set width 0'"
|
||||
. " -ex 'bt' -ex 'quit'");
|
||||
} elsif ($Debug) {
|
||||
# Debug
|
||||
run(aslr_off()
|
||||
.verilator_bin()." ".join(' ',@quoted_sw));
|
||||
run(aslr_off() . verilator_bin() . " " . join(' ', @quoted_sw));
|
||||
} else {
|
||||
# Normal, non gdb
|
||||
run(verilator_bin()." ".join(' ',@quoted_sw));
|
||||
run(verilator_bin() . " " . join(' ', @quoted_sw));
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
sub usage {
|
||||
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
|
||||
pod2usage(-verbose => 2, -exitval => 0, -output => \*STDOUT);
|
||||
}
|
||||
|
||||
sub debug {
|
||||
shift;
|
||||
my $level = shift;
|
||||
$Debug = $level||3;
|
||||
$Debug = $level || 3;
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
@@ -163,13 +162,13 @@ sub verilator_bin {
|
||||
sub gdb_works {
|
||||
$! = undef; # Cleanup -x
|
||||
system("gdb /bin/echo"
|
||||
." --batch-silent --quiet --return-child-result"
|
||||
." -ex 'run -n'" # `echo -n`
|
||||
." -ex 'set width 0'"
|
||||
." -ex 'bt'"
|
||||
." -ex 'quit'");
|
||||
. " --batch-silent --quiet --return-child-result"
|
||||
. " -ex 'run -n'" # `echo -n`
|
||||
. " -ex 'set width 0'"
|
||||
. " -ex 'bt'"
|
||||
. " -ex 'quit'");
|
||||
my $status = $?;
|
||||
return $status==0;
|
||||
return $status == 0;
|
||||
}
|
||||
|
||||
sub aslr_off {
|
||||
@@ -185,7 +184,7 @@ sub run {
|
||||
# Run command, check errors
|
||||
my $command = shift;
|
||||
$! = undef; # Cleanup -x
|
||||
print "\t$command\n" if $Debug>=3;
|
||||
print "\t$command\n" if $Debug >= 3;
|
||||
system($command);
|
||||
my $status = $?;
|
||||
if ($status) {
|
||||
@@ -193,7 +192,7 @@ sub run {
|
||||
warn "%Error: verilator: Misinstalled, or VERILATOR_ROOT might need to be in environment\n";
|
||||
}
|
||||
if ($Debug) { # For easy rerunning
|
||||
warn "%Error: export VERILATOR_ROOT=".($ENV{VERILATOR_ROOT}||"")."\n";
|
||||
warn "%Error: export VERILATOR_ROOT=" . ($ENV{VERILATOR_ROOT} || "") . "\n";
|
||||
warn "%Error: $command\n";
|
||||
}
|
||||
if ($status & 127) {
|
||||
@@ -201,13 +200,13 @@ sub run {
|
||||
|| ($status & 127) == 8 # SIGFPA
|
||||
|| ($status & 127) == 11) { # SIGSEGV
|
||||
warn "%Error: Verilator internal fault, sorry. "
|
||||
."Suggest trying --debug --gdbbt\n" if !$Debug;
|
||||
. "Suggest trying --debug --gdbbt\n" if !$Debug;
|
||||
} elsif (($status & 127) == 6) { # SIGABRT
|
||||
warn "%Error: Verilator aborted. "
|
||||
."Suggest trying --debug --gdbbt\n" if !$Debug;
|
||||
. "Suggest trying --debug --gdbbt\n" if !$Debug;
|
||||
} else {
|
||||
warn "%Error: Verilator threw signal $status. "
|
||||
."Suggest trying --debug --gdbbt\n" if !$Debug;
|
||||
. "Suggest trying --debug --gdbbt\n" if !$Debug;
|
||||
}
|
||||
}
|
||||
if (!$opt_quiet_exit && ($status != 256 || $Debug)) { # i.e. not normal exit(1)
|
||||
@@ -333,9 +332,11 @@ detailed descriptions of these arguments.
|
||||
--if-depth <value> Tune IFDEPTH warning
|
||||
+incdir+<dir> Directory to search for includes
|
||||
--inline-mult <value> Tune module inlining
|
||||
--instr-count-dpi <value> Assumed dynamic instruction count of DPI imports
|
||||
-LDFLAGS <flags> Linker pre-object arguments for makefile
|
||||
--l2-name <value> Verilog scope name of the top module
|
||||
--language <lang> Default language standard to parse
|
||||
--lib-create <name> Create a DPI library
|
||||
+libext+<ext>+[ext]... Extensions for finding modules
|
||||
--lint-only Lint, but do not make output
|
||||
-MAKEFLAGS <flags> Arguments to pass to make during --build
|
||||
@@ -447,6 +448,7 @@ description of these arguments.
|
||||
+verilator+prof+threads+file+<filename> Set profile filename
|
||||
+verilator+prof+threads+start+<value> Set profile starting point
|
||||
+verilator+prof+threads+window+<value> Set profile duration
|
||||
+verilator+prof+vlt+file+<filename> Set profile guided filename
|
||||
+verilator+rand+reset+<value> Set random reset technique
|
||||
+verilator+seed+<value> Set random seed
|
||||
+verilator+V Verbose version and config
|
||||
|
||||
@@ -30,7 +30,7 @@ $Debug = 0;
|
||||
|
||||
# No arguments can't do anything useful. Give help
|
||||
if ($#ARGV < 0) {
|
||||
pod2usage(-exitstatus=>2, -verbose=>0);
|
||||
pod2usage(-exitstatus => 2, -verbose => 0);
|
||||
}
|
||||
|
||||
# We sneak a look at the flags so we can do some pre-environment checks
|
||||
@@ -40,7 +40,7 @@ foreach my $sw (@ARGV) {
|
||||
push @Opt_Verilator_Sw, $sw;
|
||||
}
|
||||
|
||||
Getopt::Long::config("no_auto_abbrev","pass_through");
|
||||
Getopt::Long::config("no_auto_abbrev", "pass_through");
|
||||
if (! GetOptions (
|
||||
# Major operating modes
|
||||
"help" => \&usage,
|
||||
@@ -49,23 +49,23 @@ if (! GetOptions (
|
||||
# Additional parameters
|
||||
"<>" => sub {}, # Ignored
|
||||
)) {
|
||||
pod2usage(-exitstatus=>2, -verbose=>0);
|
||||
pod2usage(-exitstatus => 2, -verbose => 0);
|
||||
}
|
||||
|
||||
# Normal, non gdb
|
||||
run(verilator_coverage_bin()
|
||||
." ".join(' ',@Opt_Verilator_Sw));
|
||||
. " " . join(' ', @Opt_Verilator_Sw));
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
sub usage {
|
||||
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
|
||||
pod2usage(-verbose => 2, -exitval => 0, -output => \*STDOUT);
|
||||
}
|
||||
|
||||
sub debug {
|
||||
shift;
|
||||
my $level = shift;
|
||||
$Debug = $level||3;
|
||||
$Debug = $level || 3;
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
@@ -107,7 +107,7 @@ sub run {
|
||||
# Run command, check errors
|
||||
my $command = shift;
|
||||
$! = undef; # Cleanup -x
|
||||
print "\t$command\n" if $Debug>=3;
|
||||
print "\t$command\n" if $Debug >= 3;
|
||||
system($command);
|
||||
my $status = $?;
|
||||
if ($status) {
|
||||
@@ -115,11 +115,11 @@ sub run {
|
||||
warn "%Error: verilator_coverage: Misinstalled, or VERILATOR_ROOT might need to be in environment\n";
|
||||
}
|
||||
if ($Debug) { # For easy rerunning
|
||||
warn "%Error: export VERILATOR_ROOT=".($ENV{VERILATOR_ROOT}||"")."\n";
|
||||
warn "%Error: export VERILATOR_ROOT=" . ($ENV{VERILATOR_ROOT} || "") . "\n";
|
||||
warn "%Error: $command\n";
|
||||
}
|
||||
if ($status & 127) {
|
||||
if (($status & 127) == 8 || ($status & 127) == 11) { # SIGFPA or SIGSEGV
|
||||
if (($status & 127) == 8 || ($status & 127) == 11) { # SIGFPA or SIGSEGV
|
||||
warn "%Error: Verilator_coverage internal fault, sorry.\n" if !$Debug;
|
||||
} elsif (($status & 127) == 6) { # SIGABRT
|
||||
warn "%Error: Verilator_coverage aborted.\n" if !$Debug;
|
||||
|
||||
+106
-200
@@ -1,233 +1,139 @@
|
||||
#!/usr/bin/env perl
|
||||
# See copyright, etc in below POD section.
|
||||
#!/usr/bin/env python3
|
||||
# pylint: disable=C0103,C0114,C0116
|
||||
######################################################################
|
||||
|
||||
use warnings;
|
||||
use Getopt::Long;
|
||||
use IO::File;
|
||||
use Pod::Usage;
|
||||
use strict;
|
||||
use vars qw($Debug);
|
||||
import argparse
|
||||
import collections
|
||||
import glob
|
||||
import os.path
|
||||
import re
|
||||
import sys
|
||||
|
||||
#======================================================================
|
||||
# main
|
||||
|
||||
$Debug = 0;
|
||||
my $Opt_A;
|
||||
my $Opt_B;
|
||||
my $Opt_Lineno = 1;
|
||||
autoflush STDOUT 1;
|
||||
autoflush STDERR 1;
|
||||
Getopt::Long::config("no_auto_abbrev");
|
||||
if (! GetOptions(
|
||||
"help" => \&usage,
|
||||
"debug" => \&debug,
|
||||
"<>" => \¶meter,
|
||||
"lineno!" => \$Opt_Lineno,
|
||||
)) {
|
||||
die "%Error: Bad usage, try 'verilator_difftree --help'\n";
|
||||
}
|
||||
def diff(a, b):
|
||||
|
||||
defined $Opt_A or die "%Error: No old diff filename\n";
|
||||
defined $Opt_B or die "%Error: No new diff filename\n";
|
||||
if not os.path.exists(a):
|
||||
sys.exit("%Error: No old diff filename found: " + a)
|
||||
if not os.path.exists(b):
|
||||
sys.exit("%Error: No new diff filename found: " + b)
|
||||
|
||||
-e $Opt_A or die "%Error: No old diff filename found: $Opt_A\n";
|
||||
-e $Opt_B or die "%Error: No new diff filename found: $Opt_B\n";
|
||||
if os.path.isdir(a) and os.path.isdir(b):
|
||||
diff_dir(a, b)
|
||||
elif os.path.isfile(a) and os.path.isfile(b):
|
||||
diff_file(a, b)
|
||||
else:
|
||||
sys.exit("%Error: Mix of files and dirs")
|
||||
|
||||
if (-d $Opt_A && -d $Opt_B) {
|
||||
diff_dir($Opt_A, $Opt_B);
|
||||
} elsif (-f $Opt_A && -f $Opt_B) {
|
||||
diff_file($Opt_A, $Opt_B);
|
||||
} else {
|
||||
die "%Error: Mix of files and dirs\n";
|
||||
}
|
||||
|
||||
sub diff_dir {
|
||||
my $a = shift;
|
||||
my $b = shift;
|
||||
def diff_dir(a, b):
|
||||
# Diff all files under two directories
|
||||
my %files;
|
||||
files = collections.defaultdict(lambda: {})
|
||||
|
||||
foreach my $fn (glob("$a/*.tree")) {
|
||||
(my $base = $fn) =~ s!.*/!!;
|
||||
$files{$base}{a} = $fn;
|
||||
}
|
||||
foreach my $fn (glob("$b/*.tree")) {
|
||||
(my $base = $fn) =~ s!.*/!!;
|
||||
$files{$base}{b} = $fn;
|
||||
}
|
||||
my $any;
|
||||
foreach my $base (sort (keys %files)) {
|
||||
my $a = $files{$base}{a};
|
||||
my $b = $files{$base}{b};
|
||||
next if !$a || !$b;
|
||||
print "="x70,"\n";
|
||||
print "= $a <-> $b\n";
|
||||
diff_file($a,$b);
|
||||
$any = 1;
|
||||
}
|
||||
$any or warn("%Warning: No .tree files found that have similar base names:\n "
|
||||
.join("\n ", sort keys %files),"\n");
|
||||
}
|
||||
for fn in glob.glob(a + "/*.tree"):
|
||||
base = re.sub(r'.*/', '', fn)
|
||||
files[base]['a'] = fn
|
||||
for fn in glob.glob(b + "/*.tree"):
|
||||
base = re.sub(r'.*/', '', fn)
|
||||
files[base]['b'] = fn
|
||||
|
||||
sub diff_file {
|
||||
my $a = shift;
|
||||
my $b = shift;
|
||||
anyfile = False
|
||||
for base in sorted(files.keys()):
|
||||
a = files[base]['a']
|
||||
b = files[base]['b']
|
||||
if not a or not b:
|
||||
continue
|
||||
print("=" * 70)
|
||||
print("= %s <-> %s" % (a, b))
|
||||
diff_file(a, b)
|
||||
anyfile = True
|
||||
if not anyfile:
|
||||
sys.stderr.write(
|
||||
"%Warning: No .tree files found that have similar base names\n")
|
||||
|
||||
|
||||
def diff_file(a, b):
|
||||
# Compare the two tree files
|
||||
(my $short_a = $a) =~ s/[^a-zA-Z0-9.]+/_/g;
|
||||
(my $short_b = $b) =~ s/[^a-zA-Z0-9.]+/_/g;
|
||||
my $tmp_a = "/tmp/${$}_${short_a}.a";
|
||||
my $tmp_b = "/tmp/${$}_${short_b}.b";
|
||||
short_a = re.sub(r'[^a-zA-Z0-9.]+', '_', a)
|
||||
short_b = re.sub(r'[^a-zA-Z0-9.]+', '_', b)
|
||||
tmp_a = "/tmp/%s_%s.a" % (os.getpid(), short_a)
|
||||
tmp_b = "/tmp/%s_%s.b" % (os.getpid(), short_b)
|
||||
|
||||
my $vera = version_from($a);
|
||||
my $verb = version_from($b);
|
||||
my $verCvt = (($vera < 0x3900 && $verb >= 0x3900)
|
||||
|| ($vera >= 0x3900 && $verb < 0x3900));
|
||||
# Version conversion deprecated, but for future...
|
||||
# vera = version_from(a)
|
||||
# verb = version_from(b)
|
||||
# verCvt = ((vera < 0x3900 and verb >= 0x3900)
|
||||
# or (vera >= 0x3900 and verb < 0x3900))
|
||||
|
||||
filter($a, $tmp_a, $verCvt);
|
||||
filter($b, $tmp_b, $verCvt);
|
||||
system("diff -u $tmp_a $tmp_b");
|
||||
unlink $tmp_a;
|
||||
unlink $tmp_b;
|
||||
}
|
||||
filterf(a, tmp_a)
|
||||
filterf(b, tmp_b)
|
||||
os.system("diff -u " + tmp_a + " " + tmp_b)
|
||||
os.unlink(tmp_a)
|
||||
os.unlink(tmp_b)
|
||||
|
||||
sub version_from {
|
||||
my $fn = shift;
|
||||
|
||||
def version_from(filename):
|
||||
# Return dump format
|
||||
my $f1 = IO::File->new ($fn) or die "%Error: $! $fn,";
|
||||
while (defined (my $line=$f1->getline())) {
|
||||
last if $. > 10;
|
||||
return hex $1 if $line =~ /\(format (0x[0-9.]+)\)/;
|
||||
}
|
||||
return 1.0;
|
||||
}
|
||||
with open(filename) as fh:
|
||||
lineno = 0
|
||||
for line in fh:
|
||||
if lineno > 10:
|
||||
break
|
||||
match = re.search(r'format (0x[0-9.]+)', line)
|
||||
if match:
|
||||
return hex(match.group(1))
|
||||
return 1.0
|
||||
|
||||
sub filter {
|
||||
my $fn1 = shift;
|
||||
my $fn2 = shift;
|
||||
my $verCvt = shift;
|
||||
|
||||
def filterf(fn1, fn2):
|
||||
# Remove hex numbers before diffing
|
||||
my $f1 = IO::File->new ($fn1) or die "%Error: $! $fn1,";
|
||||
my $f2 = IO::File->new ($fn2,"w") or die "%Error: $! $fn2,";
|
||||
while (defined (my $line=$f1->getline())) {
|
||||
same_line:
|
||||
next if $line =~ / This=/;
|
||||
$line =~ s/0x[a-f0-9]+/0x/g;
|
||||
$line =~ s/<e[0-9]+\#?>/<e>/g;
|
||||
$line =~ s/{[a-z]*\d+}/{}/g if !$Opt_Lineno;
|
||||
if ($verCvt) {
|
||||
next if $line =~ /^ NETLIST/;
|
||||
if ($line =~ /: ([A-Z]+) /) {
|
||||
my $type = $1;
|
||||
next if $type =~ 'DTYPE';
|
||||
if ($type eq 'TYPETABLE' || $type eq 'RANGE') {
|
||||
$line =~ /^(\s+\S+:) /; my $prefix = $1;
|
||||
while (defined ($line=$f1->getline())) {
|
||||
next if $line =~ /^\s+[a-z]/; # Table body
|
||||
next if $line =~ /^${prefix}[0-9]:/;
|
||||
goto same_line;
|
||||
}
|
||||
next;
|
||||
}
|
||||
}
|
||||
}
|
||||
print $f2 $line;
|
||||
}
|
||||
$f1->close;
|
||||
$f2->close;
|
||||
}
|
||||
with open(fn1) as fh1:
|
||||
with open(fn2, "w") as fh2:
|
||||
for line in fh1:
|
||||
if re.search(r' This=', line):
|
||||
continue
|
||||
line = re.sub(r'0x[a-f0-9]+', '0x', line)
|
||||
line = re.sub(r'<e[0-9]+\#?>', '<e>', line)
|
||||
if not Args.no_lineno:
|
||||
line = re.sub(r'{[a-z]*\d+}', '{}', line)
|
||||
fh2.write(line)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
sub usage {
|
||||
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
|
||||
exit(1); # Unreachable
|
||||
}
|
||||
######################################################################
|
||||
######################################################################
|
||||
|
||||
sub debug {
|
||||
$Debug = 1;
|
||||
}
|
||||
|
||||
sub parameter {
|
||||
my $param = shift;
|
||||
if (!defined $Opt_A) {
|
||||
$Opt_A = $param;
|
||||
} elsif (!defined $Opt_B) {
|
||||
$Opt_B = $param;
|
||||
} else {
|
||||
die "%Error: Unknown parameter: $param\n";
|
||||
}
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
|
||||
sub run {
|
||||
# Run a system command, check errors
|
||||
my $command = shift;
|
||||
print "\t$command\n";
|
||||
system "$command";
|
||||
my $status = $?;
|
||||
($status == 0) or die "%Error: Command Failed $command, $status, stopped";
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
verilator_difftree - Compare two Verilator debugging trees
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
verilator_difftree .../a/a.tree .../b/a.tree
|
||||
verilator_difftree .../a .../b
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Verilator_difftree is used for debugging Verilator tree output files. It
|
||||
performs a diff between two files, or all files common between two
|
||||
parser = argparse.ArgumentParser(
|
||||
allow_abbrev=False,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description="""Compare two Verilator debugging trees""",
|
||||
epilog=
|
||||
"""Verilator_difftree is used for debugging Verilator tree output files.
|
||||
It performs a diff between two files, or all files common between two
|
||||
directories, ignoring irrelevant pointer differences.
|
||||
|
||||
=head1 ARGUMENTS
|
||||
|
||||
=over 4
|
||||
|
||||
=item --help
|
||||
|
||||
Displays this message and program version and exits.
|
||||
|
||||
=item --nolineno
|
||||
|
||||
Do not show differences in line numbering.
|
||||
|
||||
=back
|
||||
|
||||
=head1 DISTRIBUTION
|
||||
|
||||
The latest version is available from L<https://verilator.org>.
|
||||
For documentation see
|
||||
https://verilator.org/guide/latest/exe_verilator_difftree.html
|
||||
|
||||
Copyright 2005-2021 by Wilson Snyder. This program is free software; you
|
||||
can redistribute it and/or modify it under the terms of either the GNU
|
||||
Lesser General Public License Version 3 or the Perl Artistic License
|
||||
Version 2.0.
|
||||
|
||||
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""")
|
||||
|
||||
=head1 AUTHORS
|
||||
parser.add_argument('--debug',
|
||||
action='store_const',
|
||||
const=9,
|
||||
help='enable debug')
|
||||
parser.add_argument('--no-lineno',
|
||||
action='store_false',
|
||||
help='do not show differences in line numbering')
|
||||
parser.add_argument('filea', help='input file a to diff')
|
||||
parser.add_argument('fileb', help='input file b to diff')
|
||||
|
||||
Wilson Snyder <[email protected]>
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
C<verilator>
|
||||
|
||||
and L<https://verilator.org/verilator_doc.html> for detailed documentation.
|
||||
|
||||
=cut
|
||||
Args = parser.parse_args()
|
||||
diff(Args.filea, Args.fileb)
|
||||
|
||||
######################################################################
|
||||
### Local Variables:
|
||||
### compile-command: "$V4/bin/verilator_difftree {$V4D,$V4}/test_regress/obj_dir/t_EXAMPLE/V*_03_*.tree"
|
||||
### End:
|
||||
# Local Variables:
|
||||
# compile-command: "./verilator_difftree ../test_regress/t/t_difftree.{a,b}.tree"
|
||||
# End:
|
||||
|
||||
+428
-511
File diff suppressed because it is too large
Load Diff
@@ -10,11 +10,11 @@
|
||||
|
||||
require 5.005;
|
||||
use warnings;
|
||||
print "// DESCR"."IPTION: Generated by verilator_includer via makefile\n";
|
||||
print "// DESCR" . "IPTION: Generated by verilator_includer via makefile\n";
|
||||
foreach my $param (@ARGV) {
|
||||
if ($param =~ /^-D([^=]+)=(.*)/) {
|
||||
print "#define $1 $2\n"
|
||||
print "#define $1 $2\n";
|
||||
} else {
|
||||
print "#include \"$param\"\n"
|
||||
print "#include \"$param\"\n";
|
||||
}
|
||||
}
|
||||
|
||||
+173
-217
@@ -1,248 +1,204 @@
|
||||
#!/usr/bin/env perl
|
||||
# See copyright, etc in below POD section.
|
||||
#!/usr/bin/env python3
|
||||
# pylint: disable=C0103,C0114,C0116,R0914,R0912,R0915,eval-used
|
||||
######################################################################
|
||||
|
||||
require 5.006_001;
|
||||
use warnings;
|
||||
use Getopt::Long;
|
||||
use IO::File;
|
||||
use Pod::Usage;
|
||||
eval { use Data::Dumper; $Data::Dumper::Indent = 1; }; # Debug, ok if missing
|
||||
use strict;
|
||||
use vars qw($Debug);
|
||||
import argparse
|
||||
import collections
|
||||
import re
|
||||
# from pprint import pprint
|
||||
|
||||
#======================================================================
|
||||
######################################################################
|
||||
|
||||
|
||||
#======================================================================
|
||||
# main
|
||||
def profcfunc(filename):
|
||||
funcs = {}
|
||||
|
||||
$Debug = 0;
|
||||
my $Opt_File;
|
||||
autoflush STDOUT 1;
|
||||
autoflush STDERR 1;
|
||||
Getopt::Long::config("no_auto_abbrev");
|
||||
if (! GetOptions(
|
||||
"help" => \&usage,
|
||||
"debug" => \&debug,
|
||||
"<>" => \¶meter,
|
||||
)) {
|
||||
die "%Error: Bad usage, try 'verilator_profcfunc --help'\n";
|
||||
}
|
||||
with open(filename) as fh:
|
||||
|
||||
defined $Opt_File or die "%Error: No filename given\n";
|
||||
for line in fh:
|
||||
# %time cumesec selfsec calls {stuff} name
|
||||
match = re.match(
|
||||
r'^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$',
|
||||
line)
|
||||
if match:
|
||||
pct = float(match.group(1))
|
||||
sec = float(match.group(2))
|
||||
calls = float(match.group(3))
|
||||
func = match.group(4)
|
||||
if func not in funcs:
|
||||
funcs[func] = {'pct': 0, 'sec': 0, 'calls': 0}
|
||||
funcs[func]['pct'] += pct
|
||||
funcs[func]['sec'] += sec
|
||||
funcs[func]['calls'] += calls
|
||||
continue
|
||||
|
||||
profcfunc($Opt_File);
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
sub usage {
|
||||
pod2usage(-verbose=>2, -exitval=>0, -output=>\*STDOUT);
|
||||
exit(1); # Unreachable
|
||||
}
|
||||
|
||||
sub debug {
|
||||
$Debug = 1;
|
||||
}
|
||||
|
||||
sub parameter {
|
||||
my $param = shift;
|
||||
if (!defined $Opt_File) {
|
||||
$Opt_File = $param;
|
||||
} else {
|
||||
die "%Error: Unknown parameter: $param\n";
|
||||
}
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
|
||||
sub profcfunc {
|
||||
my $filename = shift;
|
||||
# Remove hex numbers before diffing
|
||||
my $fh = IO::File->new ($filename) or die "%Error: $! $filename,";
|
||||
|
||||
my %funcs;
|
||||
|
||||
while (defined (my $line=$fh->getline())) {
|
||||
# %time cumesec selfsec calls {stuff} name
|
||||
if ($line =~ /^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$/) {
|
||||
my $pct=$1; my $sec=$2; my $calls=$3; my $func=$4;
|
||||
$funcs{$func}{pct} += $pct;
|
||||
$funcs{$func}{sec} += $sec;
|
||||
$funcs{$func}{calls} += $calls;
|
||||
}
|
||||
# Older gprofs have no call column for single-call functions
|
||||
# %time cumesec selfsec {stuff} name
|
||||
elsif ($line =~ /^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$/) {
|
||||
my $pct=$1; my $sec=$2; my $calls=1; my $func=$3;
|
||||
$funcs{$func}{pct} += $pct;
|
||||
$funcs{$func}{sec} += $sec;
|
||||
$funcs{$func}{calls} += $calls;
|
||||
}
|
||||
}
|
||||
$fh->close;
|
||||
# Older gprofs have no call column for single-call functions
|
||||
# %time cumesec selfsec {stuff} name
|
||||
match = re.match(
|
||||
r'^\s*([0-9.]+)\s+[0-9.]+\s+([0-9.]+)\s+[^a-zA-Z_]*([a-zA-Z_].*)$',
|
||||
line)
|
||||
if match:
|
||||
pct = float(match.group(1))
|
||||
sec = float(match.group(2))
|
||||
calls = 1
|
||||
func = match.group(3)
|
||||
if func not in funcs:
|
||||
funcs[func] = {'pct': 0, 'sec': 0, 'calls': 0}
|
||||
funcs[func]['pct'] += pct
|
||||
funcs[func]['sec'] += sec
|
||||
funcs[func]['calls'] += calls
|
||||
continue
|
||||
|
||||
# Find modules
|
||||
my %pointer_mods;
|
||||
my %verilated_mods;
|
||||
foreach my $func (keys %funcs) {
|
||||
if ($func =~ /(.*)::_eval\(([a-zA-Z_0-9]+__Syms).*\)$/) {
|
||||
$verilated_mods{$1} = qr/^$1/;
|
||||
$pointer_mods{$2} = $1;
|
||||
}
|
||||
}
|
||||
#print Dumper(\%pointer_mods, \%verilated_mods);
|
||||
verilated_mods = {}
|
||||
for func in funcs:
|
||||
match = re.search(r'(.*)::eval(_step)?\(', func)
|
||||
if match:
|
||||
prefix = match.group(1)
|
||||
if Args.debug:
|
||||
print("-got _eval %s prefix=%s" % (func, prefix))
|
||||
verilated_mods[prefix] = re.compile(r'^' + prefix)
|
||||
# pprint(verilated_mods)
|
||||
|
||||
# Resort by Verilog name
|
||||
my %vfuncs;
|
||||
my %groups;
|
||||
foreach my $func (keys %funcs) {
|
||||
my $pct = $funcs{$func}{pct};
|
||||
my $vfunc = $func;
|
||||
my $design;
|
||||
# Sort by Verilog name
|
||||
vfuncs = {}
|
||||
groups = {}
|
||||
groups['type'] = collections.defaultdict(lambda: 0)
|
||||
groups['design'] = collections.defaultdict(lambda: 0)
|
||||
groups['module'] = collections.defaultdict(lambda: 0)
|
||||
|
||||
if ($func =~ /\(([a-zA-Z_0-9]+__Syms)/) {
|
||||
$design = $pointer_mods{$1};
|
||||
}
|
||||
for func in funcs:
|
||||
pct = funcs[func]['pct']
|
||||
vfunc = func
|
||||
|
||||
foreach my $vde (keys %verilated_mods) {
|
||||
last if $design;
|
||||
if ($func =~ /$verilated_mods{$vde}/) {
|
||||
$design=$vde;
|
||||
last;
|
||||
}
|
||||
}
|
||||
funcarg = re.sub(r'^.*\(', '', func)
|
||||
|
||||
if ($vfunc =~ /__PROF__([a-zA-Z_0-9]+)__l?([0-9]+)\(/) {
|
||||
$vfunc = sprintf("VBlock %s:%d", $1, $2);
|
||||
$groups{type}{"Verilog Blocks under $design"} += $pct;
|
||||
$groups{design}{$design} += $pct;
|
||||
$groups{module}{$1} += $pct;
|
||||
} else {
|
||||
if ($design) {
|
||||
$vfunc = sprintf("VCommon %s", $func);
|
||||
$groups{type}{"Common code under $design"} += $pct;
|
||||
$groups{design}{$design} += $pct;
|
||||
$groups{module}{$design." common code"} += $pct;
|
||||
} elsif ($func =~ /^VL_[A-Z0-9_]+/
|
||||
|| $func =~ /^_?vl_[a-zA-Z0-9_]+/
|
||||
|| $func =~ /^verilated/i) {
|
||||
$vfunc = sprintf("VLib %s", $func);
|
||||
$groups{type}{'VLib'} += $pct;
|
||||
$groups{design}{'VLib'} += $pct;
|
||||
$groups{module}{'VLib'} += $pct;
|
||||
} elsif ($func =~ /^_mcount_private/) {
|
||||
$vfunc = sprintf("Prof %s", $func);
|
||||
$groups{type}{'Prof'} += $pct;
|
||||
$groups{design}{'Prof'} += $pct;
|
||||
$groups{module}{'Prof'} += $pct;
|
||||
} else {
|
||||
$vfunc = sprintf("C++ %s", $func);
|
||||
$groups{type}{'C++'} += $pct;
|
||||
$groups{design}{'C++'} += $pct;
|
||||
$groups{module}{'C++'} += $pct;
|
||||
}
|
||||
}
|
||||
$vfuncs{$vfunc} = $funcs{$func};
|
||||
}
|
||||
design = None
|
||||
for vde in verilated_mods:
|
||||
if verilated_mods[vde].match(func) or verilated_mods[vde].match(
|
||||
funcarg):
|
||||
design = vde
|
||||
break
|
||||
|
||||
vdesign = "-"
|
||||
|
||||
prof_match = re.search(r'__PROF__([a-zA-Z_0-9]+)__l?([0-9]+)\(', vfunc)
|
||||
if design and prof_match:
|
||||
linefunc = prof_match.group(1)
|
||||
lineno = int(prof_match.group(2))
|
||||
vfunc = "VBlock %s:%d" % (linefunc, lineno)
|
||||
vdesign = design
|
||||
groups['type']["Verilog Blocks under " + design] += pct
|
||||
groups['design'][design] += pct
|
||||
groups['module'][linefunc] += pct
|
||||
elif design:
|
||||
vfunc = "VCommon " + func
|
||||
vdesign = design
|
||||
groups['type']["Common code under " + design] += pct
|
||||
groups['design'][design] += pct
|
||||
groups['module'][design + " common code"] += pct
|
||||
elif re.match(r'(VL_[A-Z0-9_]+|_?vl_[a-zA-Z0-9_]+|Verilated)', vfunc):
|
||||
vfunc = "VLib " + func
|
||||
groups['type']['VLib'] += pct
|
||||
groups['design']['VLib'] += pct
|
||||
groups['module']['VLib'] += pct
|
||||
elif re.match(r'^_mcount_private', vfunc):
|
||||
vfunc = "Prof " + func
|
||||
groups['type']['Prof'] += pct
|
||||
groups['design']['Prof'] += pct
|
||||
groups['module']['Prof'] += pct
|
||||
else:
|
||||
vfunc = "C++ " + func
|
||||
groups['type']['C++'] += pct
|
||||
groups['design']['C++'] += pct
|
||||
groups['module']['C++'] += pct
|
||||
|
||||
if vfunc not in vfuncs:
|
||||
vfuncs[vfunc] = funcs[func]
|
||||
vfuncs[vfunc]['design'] = vdesign
|
||||
else:
|
||||
vfuncs[vfunc]['pct'] += funcs[func]['pct']
|
||||
vfuncs[vfunc]['calls'] += funcs[func]['calls']
|
||||
vfuncs[vfunc]['sec'] += funcs[func]['sec']
|
||||
|
||||
for ftype in ['type', 'design', 'module']:
|
||||
missing = 100
|
||||
for item in groups[ftype].keys():
|
||||
missing -= groups[ftype][item]
|
||||
groups[ftype]["\377Unaccounted for/rounding error"] = missing
|
||||
|
||||
print("Overall summary by %s:" % ftype)
|
||||
print(" %-6s %s" % ("% time", ftype))
|
||||
for what in sorted(groups[ftype].keys()):
|
||||
# \377 used to establish sort order
|
||||
pwhat = re.sub(r'^\377', '', what)
|
||||
print(" %6.2f %s" % (groups[ftype][what], pwhat))
|
||||
print()
|
||||
|
||||
design_width = 1
|
||||
for func in vfuncs:
|
||||
if design_width < len(vfuncs[func]['design']):
|
||||
design_width = len(vfuncs[func]['design'])
|
||||
|
||||
print("Verilog code profile:")
|
||||
print(" These are split into three categories:")
|
||||
print(" C++: Time in non-Verilated C++ code")
|
||||
print(" Prof: Time in profile overhead")
|
||||
print(" VBlock: Time attributable to a block in a" +
|
||||
" Verilog file and line")
|
||||
print(" VCommon: Time in a Verilated module," +
|
||||
" due to all parts of the design")
|
||||
print(" VLib: Time in Verilated common libraries," +
|
||||
" called by the Verilated code")
|
||||
print()
|
||||
|
||||
print(" % cumulative self ")
|
||||
print((" time seconds seconds calls %-" + str(design_width) +
|
||||
"s type filename and line number") % "design")
|
||||
|
||||
cume = 0
|
||||
for func in sorted(vfuncs.keys(),
|
||||
key=lambda f: vfuncs[f]['sec'],
|
||||
reverse=True):
|
||||
cume += vfuncs[func]['sec']
|
||||
print(("%6.2f %9.2f %8.2f %10d %-" + str(design_width) + "s %s") %
|
||||
(vfuncs[func]['pct'], cume, vfuncs[func]['sec'],
|
||||
vfuncs[func]['calls'], vfuncs[func]['design'], func))
|
||||
|
||||
|
||||
foreach my $type (qw(type design module)) {
|
||||
my $missing = 100;
|
||||
foreach (sort (keys %{$groups{$type}})) {
|
||||
$missing -= $groups{$type}{$_};
|
||||
}
|
||||
if ($missing) {
|
||||
$groups{$type}{"\377Unaccounted for/rounding error"} = $missing;
|
||||
}
|
||||
######################################################################
|
||||
######################################################################
|
||||
|
||||
print("Overall summary by $type:\n");
|
||||
printf(" %-6s %s\n","% time",$type);
|
||||
foreach my $what (sort (keys %{$groups{$type}})) {
|
||||
(my $pwhat = $what) =~ s/^\377//; # Just used to establish sort order
|
||||
printf(" %6.2f %s\n", $groups{$type}{$what}, $pwhat);
|
||||
}
|
||||
print("\n");
|
||||
}
|
||||
|
||||
print("Verilog code profile:\n");
|
||||
print(" These are split into three categories:\n");
|
||||
print(" C++: Time in non-Verilated C++ code\n");
|
||||
print(" Prof: Time in profile overhead\n");
|
||||
print(" VBlock: Time attributable to a block in a Verilog file and line\n");
|
||||
print(" VCommon: Time in a Verilated module, due to all parts of the design\n");
|
||||
print(" VLib: Time in Verilated common libraries, called by the Verilated code\n");
|
||||
print("\n");
|
||||
|
||||
print(" % cumulative self \n");
|
||||
print(" time seconds seconds calls type filename and line number\n");
|
||||
|
||||
my $cume = 0;
|
||||
foreach my $func (sort {$vfuncs{$b}{sec} <=> $vfuncs{$a}{sec}
|
||||
|| $a cmp $b}
|
||||
(keys %vfuncs)) {
|
||||
$cume += $vfuncs{$func}{sec};
|
||||
printf +("%6.2f %9.2f %8.2f %8d %s\n",
|
||||
$vfuncs{$func}{pct},
|
||||
$cume, $vfuncs{$func}{sec},
|
||||
$vfuncs{$func}{calls},
|
||||
$func);
|
||||
}
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
verilator_profcfunc - Read gprof report created with --prof-cfuncs
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
verilator --prof-cfuncs ....
|
||||
gcc ....
|
||||
{run executable}
|
||||
gprof
|
||||
verilator_profcfuncs gprof.out
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Verilator_profcfunc reads a profile report created by gprof. The names of
|
||||
parser = argparse.ArgumentParser(
|
||||
allow_abbrev=False,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description="""Read gprof report created with --prof-cfuncs""",
|
||||
epilog=
|
||||
"""Verilator_profcfunc reads a profile report created by gprof. The names of
|
||||
the functions are then transformed, assuming the user used Verilator's
|
||||
--prof-cfuncs, and a report printed showing the percentage of time, etc,
|
||||
in each Verilog block.
|
||||
|
||||
For documentation see
|
||||
L<https://verilator.org/guide/latest/exe_verilator_profcfuncs.html>.
|
||||
https://verilator.org/guide/latest/exe_verilator_profcfunc.html
|
||||
|
||||
=head1 ARGUMENT SUMMARY
|
||||
|
||||
<filename> Input file (gprof.out)
|
||||
--help Display this help
|
||||
|
||||
=head1 DISTRIBUTION
|
||||
|
||||
The latest version is available from L<https://verilator.org>.
|
||||
|
||||
Copyright 2007-2021 by Wilson Snyder. This program is free software; you
|
||||
Copyright 2002-2021 by Wilson Snyder. This program is free software; you
|
||||
can redistribute it and/or modify it under the terms of either the GNU
|
||||
Lesser General Public License Version 3 or the Perl Artistic License
|
||||
Version 2.0.
|
||||
|
||||
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""")
|
||||
|
||||
=head1 SEE ALSO
|
||||
parser.add_argument('--debug',
|
||||
action='store_const',
|
||||
const=9,
|
||||
help='enable debug')
|
||||
parser.add_argument('filename', help='input gprof output to process')
|
||||
|
||||
C<verilator>
|
||||
|
||||
and L<https://verilator.org/guide/latest/exe_verilator_profcfuncs.html> for
|
||||
detailed documentation.
|
||||
|
||||
=cut
|
||||
Args = parser.parse_args()
|
||||
profcfunc(Args.filename)
|
||||
|
||||
######################################################################
|
||||
### Local Variables:
|
||||
### compile-command: "$V4/bin/verilator_profcfunc $V4/test_c/obj_dir/V*_03_*.tree $V4N/test_c/obj_dir/V*_03_*.tree"
|
||||
### End:
|
||||
# Local Variables:
|
||||
# compile-command: "./verilator_profcfunc ../test_regress/t/t_profcfunc.gprof"
|
||||
# End:
|
||||
|
||||
+8
-22
@@ -7,7 +7,7 @@
|
||||
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[4.210 2021-07-07],
|
||||
AC_INIT([Verilator],[4.216 2021-12-05],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
# When releasing, also update header of Changes file
|
||||
@@ -346,33 +346,19 @@ AC_SUBST(CFG_CXXFLAGS_PROFILE)
|
||||
|
||||
# Flag to select newest language standard supported
|
||||
# Macros work such that first option that passes is the one we take
|
||||
# Currently enabled gnu++14/c++14 due to packaged SystemC dependency
|
||||
# gnu++17 is the newest that Verilator supports
|
||||
# std++03 is the oldest that Verilator supports
|
||||
# Currently enabled c++14 due to packaged SystemC dependency
|
||||
# c++14 is the newest that Verilator is regressed to support
|
||||
# c++11 is the oldest that Verilator supports
|
||||
# gnu is requried for Cygwin to compile verilated.h successfully
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++20)
|
||||
#_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++20)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++17)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++17)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++14)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++14)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++11)
|
||||
_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=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=c++17)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++20)
|
||||
_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++20)
|
||||
AC_SUBST(CFG_CXXFLAGS_STD_OLDEST)
|
||||
|
||||
# Flags for compiling Verilator internals including parser, and Verilated files
|
||||
# These turn on extra warnings and are only used with 'configure --enable-ccwarn'
|
||||
|
||||
@@ -11,6 +11,7 @@ Andreas Kuster
|
||||
Chris Randall
|
||||
Conor McCullough
|
||||
Dan Petrisko
|
||||
Daniel Bates
|
||||
David Horton
|
||||
David Metz
|
||||
David Stanford
|
||||
@@ -30,6 +31,8 @@ Harald Heckmann
|
||||
Howard Su
|
||||
Huang Rui
|
||||
HyungKi Jeong
|
||||
Iru Cai
|
||||
Ivan Vnučec
|
||||
Iztok Jeras
|
||||
James Hanlon
|
||||
James Hutchinson
|
||||
@@ -42,9 +45,11 @@ John Coiner
|
||||
John Demme
|
||||
Jonathan Drolet
|
||||
Josh Redford
|
||||
Julie Schwartz
|
||||
Julien Margetts
|
||||
Kaleb Barrett
|
||||
Kanad Kanhere
|
||||
Keith Colbert
|
||||
Kevin Kiningham
|
||||
Krzysztof Bieganski
|
||||
Kuba Ober
|
||||
@@ -59,6 +64,7 @@ Marshal Qiao
|
||||
Martin Schmidt
|
||||
Matthew Ballance
|
||||
Michael Killough
|
||||
Michaël Lefebvre
|
||||
Mike Popoloski
|
||||
Miodrag Milanović
|
||||
Morten Borup Petersen
|
||||
@@ -83,6 +89,8 @@ Sebastien Van Cauwenberghe
|
||||
Sergi Granell
|
||||
Stefan Wallentowitz
|
||||
Stephen Henry
|
||||
Steven Hugg
|
||||
Teng Huang
|
||||
Tim Snyder
|
||||
Tobias Rosenkranz
|
||||
Tobias Wölfel
|
||||
@@ -99,3 +107,4 @@ Yossi Nivin
|
||||
Yuri Victorovich
|
||||
Yutetsu TAKATSUKASA
|
||||
Yves Mathieu
|
||||
Zhanglei Wang
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
.. comment: generated by t_lint_didnotconverge_bad
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
|
||||
always_comb b = ~a;
|
||||
always_comb a = b;
|
||||
@@ -0,0 +1,7 @@
|
||||
.. comment: generated by t_lint_didnotconverge_bad
|
||||
.. code-block::
|
||||
|
||||
-V{t#,#}+ Vt_lint_didnotconverge_bad___024root___change_request
|
||||
-V{t#,#}+ Vt_lint_didnotconverge_bad___024root___change_request_1
|
||||
-V{t#,#} CHANGE: t/t_lint_didnotconverge_bad.v:14: a
|
||||
%Error: t/t_lint_didnotconverge_bad.v:7: Verilated model didn't converge
|
||||
@@ -0,0 +1,4 @@
|
||||
.. comment: generated by t_lint_didnotconverge_nodbg_bad
|
||||
.. code-block::
|
||||
|
||||
%Error: t/t_lint_didnotconverge_bad.v:7: Verilated model didn't converge
|
||||
@@ -0,0 +1,5 @@
|
||||
.. comment: generated by t_lint_stmtdly_bad
|
||||
.. code-block:: sv
|
||||
:emphasize-lines: 1
|
||||
|
||||
#100 $finish; //<--- Warning
|
||||
@@ -0,0 +1,4 @@
|
||||
.. comment: generated by t_lint_stmtdly_bad
|
||||
.. code-block::
|
||||
|
||||
%Warning-STMTDLY: example.v:1:8 Unsupported: Ignoring delay on this delayed statement.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# pylint: disable=C0103,C0114,C0116,E0402,W0622
|
||||
# pylint: disable=C0103,C0114,C0116,C0301,E0402,W0622
|
||||
#
|
||||
# Configuration file for Verilator's Sphinx documentation builder.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
@@ -13,7 +13,8 @@ When possible, please instead report bugs at `Verilator Issues
|
||||
|
||||
Primary author is Wilson Snyder <[email protected]>.
|
||||
|
||||
Major concepts by Paul Wasson, Duane Galbi, John Coiner and Jie Xu.
|
||||
Major concepts by Paul Wasson, Duane Galbi, John Coiner, Geza Lore, Yutetsu
|
||||
Takatsukasa, and Jie Xu.
|
||||
|
||||
|
||||
Contributors
|
||||
|
||||
@@ -9,7 +9,12 @@ The following deprecated items are scheduled for future removal:
|
||||
C++11 compiler support
|
||||
Verilator currently requires C++11 or newer compilers. Verilator will
|
||||
require C++14 or newer compilers for both compiling Verilator and
|
||||
compiling Verilated models no sooner than January 2022.
|
||||
compiling Verilated models no sooner than January 2023.
|
||||
|
||||
Verilated_heavy.h
|
||||
The legacy "verilated_heavy.h" include was replaced with just including
|
||||
"verilated.h". Verilated_heavy.h is planned for removal no sooner than
|
||||
July 2022.
|
||||
|
||||
Configuration File -msg
|
||||
The :vlopt:`lint_off` "-msg" option has been replaced with the "-rule"
|
||||
|
||||
@@ -62,6 +62,12 @@ Summary:
|
||||
makes sense for a single-clock-domain module where it's typical to want
|
||||
to capture one posedge eval() and one negedge eval().
|
||||
|
||||
.. option:: +verilator+prof+vlt+file+<filename>
|
||||
|
||||
When a model was Verilated using :vlopt:`--prof-threads`, sets the
|
||||
profile-guided optimization data runtime filename to dump to. Defaults
|
||||
to :file:`profile.vlt`.
|
||||
|
||||
.. option:: +verilator+rand+reset+<value>
|
||||
|
||||
When a model was Verilated using :vlopt:`--x-initial unique
|
||||
|
||||
@@ -158,41 +158,26 @@ Summary:
|
||||
|
||||
.. option:: --clk <signal-name>
|
||||
|
||||
Sometimes it is quite difficult for Verilator to distinguish clock
|
||||
signals from other data signals. Occasionally the clock signals can end
|
||||
up in the checking list of signals which determines if further
|
||||
evaluation is needed. This will heavily degrade the performance of a
|
||||
Verilated model.
|
||||
|
||||
With :vlopt:`--clk`, the specified signal-name is taken as a root clock
|
||||
into the model, then Verilator will mark the signal as clocker and
|
||||
propagate the clocker attribute automatically to other signals derived
|
||||
from that. In this way, Verilator will try to avoid taking the clocker
|
||||
signal into checking list.
|
||||
into the model; Verilator will mark the signal as clocker and
|
||||
propagate the clocker attribute automatically to other signals downstream in
|
||||
that clock tree.
|
||||
|
||||
Note signal-name is specified by the RTL hierarchy path. For example,
|
||||
v.foo.bar. If the signal is the input to top-module, the directly the
|
||||
signal name. If you find it difficult to find the exact name, try to use
|
||||
a :option:`/*verilator&32;clocker*/` metacomment in RTL file to mark the
|
||||
The provided signal-name is specified using a RTL hierarchy path. For
|
||||
example, v.foo.bar. If the signal is the input to top-module, then
|
||||
directly provide the signal name. Alternatively, use a
|
||||
:option:`/*verilator&32;clocker*/` metacomment in RTL file to mark the
|
||||
signal directly.
|
||||
|
||||
If clock signals are assigned to vectors and then later used
|
||||
individually, Verilator will attempt to decompose the vector and connect
|
||||
the single-bit clock signals directly. This should be transparent to
|
||||
the user.
|
||||
If clock signals are assigned to vectors and then later used as
|
||||
individual bits, Verilator will attempt to decompose the vector and
|
||||
connect the single-bit clock signals.
|
||||
|
||||
.. option:: --make <build-tool>
|
||||
|
||||
Generates a script for the specified build tool.
|
||||
|
||||
Supported values are ``gmake`` for GNU Make and ``cmake`` for CMake.
|
||||
Both can be specified together. If no build tool is specified, gmake is
|
||||
assumed. The executable of gmake can be configured via environment
|
||||
variable "MAKE".
|
||||
|
||||
When using :vlopt:`--build` Verilator takes over the responsibility of
|
||||
building the model library/executable. For this reason :option:`--make`
|
||||
cannot be specified when using :vlopt:`--build`.
|
||||
The clocker attribute is useful in cases where Verilator does not
|
||||
properly distinguish clock signals from other data signals. Using
|
||||
clocker will cause the signal indicated to be considered a clock, and
|
||||
remove it from the combinatorial logic reevaluation checking code. This
|
||||
may greatly improve performance.
|
||||
|
||||
.. option:: --compiler <compiler-name>
|
||||
|
||||
@@ -536,6 +521,14 @@ Summary:
|
||||
times, but potentially faster simulation speed. This setting is ignored
|
||||
for very small modules; they will always be inlined, if allowed.
|
||||
|
||||
.. option:: --instr-count-dpi <value>
|
||||
|
||||
Assumed dynamic instruction count of the average DPI import. This is used
|
||||
by the partitioning algorithm when creating a multithread model. The
|
||||
default value is 200. Adjusting this to an appropriate value can yield
|
||||
performance improvements in multithreaded models. Ignored when creating a
|
||||
single threaded model.
|
||||
|
||||
.. option:: -j [<value>]
|
||||
|
||||
Specify the level of parallelism for :vlopt:`--build`. The <value> must
|
||||
@@ -582,6 +575,23 @@ Summary:
|
||||
"+libext+" is fairly standard across Verilog tools. Defaults to
|
||||
".v+.sv".
|
||||
|
||||
.. option:: --lib-create <name>
|
||||
|
||||
Produces C++, Verilog wrappers and a Makefile which can in turn produce
|
||||
a DPI library which can be used by Verilator or other simulators along
|
||||
with the corresponding Verilog wrapper. The Makefile will build both a
|
||||
static and dynamic version of the library named :file:`lib<name>.a` and
|
||||
:file:`lib<name>.so` respectively. This is done because some simulators
|
||||
require a dynamic library, but the static library is arguably easier to
|
||||
use if possible. :vlopt:`--protect-lib` implies :vlopt:`--protect-ids`.
|
||||
|
||||
When using :vlopt:`--lib-create` it is advised to also use
|
||||
:vlopt:`--timescale-override /1fs <--timescale-override>` to ensure the
|
||||
model has a time resolution that is always compatible with the time
|
||||
precision of the upper instantiating module.
|
||||
|
||||
See also :vlopt:`--protect-lib`.
|
||||
|
||||
.. option:: --lint-only
|
||||
|
||||
Check the files for lint violations only, do not create any other
|
||||
@@ -593,6 +603,19 @@ Summary:
|
||||
If the design is not to be completely Verilated see also the
|
||||
:vlopt:`--bbox-sys` and :vlopt:`--bbox-unsup` options.
|
||||
|
||||
.. option:: --make <build-tool>
|
||||
|
||||
Generates a script for the specified build tool.
|
||||
|
||||
Supported values are ``gmake`` for GNU Make and ``cmake`` for CMake.
|
||||
Both can be specified together. If no build tool is specified, gmake is
|
||||
assumed. The executable of gmake can be configured via environment
|
||||
variable "MAKE".
|
||||
|
||||
When using :vlopt:`--build` Verilator takes over the responsibility of
|
||||
building the model library/executable. For this reason :option:`--make`
|
||||
cannot be specified when using :vlopt:`--build`.
|
||||
|
||||
.. option:: -MAKEFLAGS <string>
|
||||
|
||||
When using :vlopt:`--build`, add the specified argument to the invoked
|
||||
@@ -808,7 +831,7 @@ Summary:
|
||||
When compiling the C++ code, enable the compiler's profiling flag
|
||||
(e.g. :code:`g++ -pg`). See :ref:`Profiling`.
|
||||
|
||||
Using :vlopt:`--prof-cfuncs` also enables :vlopt:`prof-c`.
|
||||
Using :vlopt:`--prof-cfuncs` also enables :vlopt:`--prof-c`.
|
||||
|
||||
.. option:: --prof-cfuncs
|
||||
|
||||
@@ -820,12 +843,12 @@ Summary:
|
||||
came from. This allows gprof or oprofile reports to be correlated with
|
||||
the original Verilog source statements. See :ref:`Profiling`.
|
||||
|
||||
Using :vlopt:`--prof-cfuncs` also enables :vlopt:`prof-c`.
|
||||
Using :vlopt:`--prof-cfuncs` also enables :vlopt:`--prof-c`.
|
||||
|
||||
.. option:: --prof-threads
|
||||
|
||||
Enable gantt chart data collection for threaded builds. See :ref:`Thread
|
||||
Profiling`.
|
||||
Profiling` and :ref:`Thread PGO`.
|
||||
|
||||
.. option:: --protect-key <key>
|
||||
|
||||
@@ -866,24 +889,15 @@ Summary:
|
||||
|
||||
.. option:: --protect-lib <name>
|
||||
|
||||
Produces C++, Verilog wrappers and a Makefile which can in turn produce
|
||||
a DPI library which can be used by Verilator or other simulators along
|
||||
with the corresponding Verilog wrapper. The Makefile will build both a
|
||||
static and dynamic version of the library named :file:`lib<name>.a` and
|
||||
:file:`lib<name>.so` respectively. This is done because some simulators
|
||||
require a dynamic library, but the static library is arguably easier to
|
||||
use if possible. :vlopt:`--protect-lib` implies :vlopt:`--protect-ids`.
|
||||
Produces a DPI library similar to :vlopt:`--lib-create`, but hides
|
||||
internal design details. :vlopt:`--protect-lib` implies
|
||||
:vlopt:`--protect-ids`, and :vlopt:`--lib-create`.
|
||||
|
||||
This allows for the secure delivery of sensitive IP without the need for
|
||||
encrypted RTL (i.e. IEEE P1735). See :file:`examples/make_protect_lib`
|
||||
in the distribution for a demonstration of how to build and use the DPI
|
||||
library.
|
||||
|
||||
When using :vlopt:`--protect-lib` it is advised to also use
|
||||
:vlopt:`--timescale-override /1fs <--timescale-override>` to ensure the
|
||||
model has a time resolution that is always compatible with the time
|
||||
precision of the upper instantiating module.
|
||||
|
||||
.. option:: --private
|
||||
|
||||
Opposite of :vlopt:`--public`. Is the default; this option exists for
|
||||
@@ -1044,6 +1058,8 @@ Summary:
|
||||
Verilator assumes DPI pure imports are threadsafe, but non-pure DPI
|
||||
imports are not.
|
||||
|
||||
See also :vlopt:`--instr-count-dpi` option.
|
||||
|
||||
.. option:: --threads-max-mtasks <value>
|
||||
|
||||
Rarely needed. When using :vlopt:`--threads`, specify the number of
|
||||
@@ -1157,9 +1173,9 @@ Summary:
|
||||
|
||||
.. option:: --trace-underscore
|
||||
|
||||
Enable tracing of signals that start with an underscore. Normally, these
|
||||
signals are not output during tracing. See also
|
||||
:vlopt:`--coverage-underscore` option.
|
||||
Enable tracing of signals or modules that start with an
|
||||
underscore. Normally, these signals are not output during tracing. See
|
||||
also :vlopt:`--coverage-underscore` option.
|
||||
|
||||
.. option:: -U<var>
|
||||
|
||||
@@ -1496,9 +1512,9 @@ The grammar of configuration commands is as follows:
|
||||
|
||||
.. option:: no_clocker -module "<modulename>" [-function "<funcname>"] -var "<signame>"
|
||||
|
||||
Indicate the signal is used as clock or not. This information is used by
|
||||
Verilator to mark the signal as clocker and propagate the clocker
|
||||
attribute automatically to derived signals. See :vlopt:`--clk`.
|
||||
Indicates that the signal is used as clock or not. This information is
|
||||
used by Verilator to mark the signal and any derrived signals as
|
||||
clocker. See :vlopt:`--clk`.
|
||||
|
||||
Same as :option:`/*verilator&32;clocker*/` metacomment.
|
||||
|
||||
@@ -1602,6 +1618,12 @@ The grammar of configuration commands is as follows:
|
||||
:option:`/*verilator&32;public_flat*/`, etc, metacomments. See
|
||||
e.g. :ref:`VPI Example`.
|
||||
|
||||
.. option:: profile_data -mtask "<mtask_hash>" -cost <cost_value>
|
||||
|
||||
Feeds profile-guided optimization data into the Verilator algorithms in
|
||||
order to improve model runtime performance. This option is not expected
|
||||
to be used by users directly. See :ref:`Thread PGO`.
|
||||
|
||||
.. option:: sc_bv -module "<modulename>" [-task "<taskname>"] -var "<signame>"
|
||||
|
||||
.. option:: sc_bv -module "<modulename>" [-function "<funcname>"] -var "<signame>"
|
||||
|
||||
@@ -10,42 +10,60 @@ starts and ends, and showing when each thread is busy or idle.
|
||||
|
||||
For an overview of use of verilator_gantt, see :ref:`Profiling`.
|
||||
|
||||
Gantt Chart Contents
|
||||
--------------------
|
||||
Gantt Chart VCD
|
||||
---------------
|
||||
|
||||
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.
|
||||
Verilated_gnatt creates a value change dump (VCD) format dump file which
|
||||
may be viewed in a waveform viewer (e.g. C<GTKWave>):
|
||||
|
||||
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.
|
||||
.. figure:: figures/fig_gantt_min.png
|
||||
|
||||
Example verilator_gantt output, as viewed with GTKWave.
|
||||
|
||||
The viewed waveform chart has time on the X-axis, with one unit for each
|
||||
time tick of the system's high-performance counter.
|
||||
|
||||
Also creates a value change dump (VCD) format dump file which may be viewed
|
||||
in a waveform viewer (e.g. C<GTKWave>). See below.
|
||||
|
||||
Gantt Chart 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.
|
||||
In waveforms there are the following signals. In GTKWave, using a data
|
||||
format of "decimal" will remove the leading zeros and make the traces
|
||||
easier to read.
|
||||
|
||||
parallelism
|
||||
evals
|
||||
Increments each time when eval_step was measured to be active. This
|
||||
allow visualization of how much time eval_step was active.
|
||||
|
||||
eval_loop
|
||||
Increments each time when the evaluation loop within eval_step was
|
||||
measured to be active. For best performance there is only a single
|
||||
evaluation loop within each eval_step call, that is the eval_loop
|
||||
waveform looks identical to the evals waveform.
|
||||
|
||||
measured_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
|
||||
match the thread count. In GTKWave, use a data format of "analog step" to
|
||||
view this signal.
|
||||
|
||||
predicted_parallelism
|
||||
The number of mtasks Verilator predicted would be active at this time,
|
||||
for best performance this will match the thread count. In GTKWave, use a
|
||||
data format of "analog step" to view this signal.
|
||||
|
||||
cpu#_thread
|
||||
For the given CPU number, the thread number executing.
|
||||
For the given CPU number, the thread number measured to be executing.
|
||||
|
||||
mtask#_cpu
|
||||
For the given mtask id, the CPU it is executing on.
|
||||
For the given mtask id, the CPU it was measured to execute on.
|
||||
|
||||
thread#_mtask
|
||||
For the given thread number, the mtask id executing.
|
||||
For the given thread number, the mtask id it was executing.
|
||||
|
||||
predicted_thread#_mtask
|
||||
For the given thread number, the mtask id Verilator predicted would be
|
||||
executing.
|
||||
|
||||
|
||||
verilator_gantt Arguments
|
||||
-------------------------
|
||||
@@ -60,13 +78,6 @@ The filename to read data from, defaults to "profile_threads.dat".
|
||||
|
||||
Displays a help summary, the program version, and exits.
|
||||
|
||||
.. option:: --scale <n>
|
||||
|
||||
Sets the number of time units per character on the X-axis of the generated
|
||||
Gantt chart. (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.
|
||||
|
||||
.. option:: --no-vcd
|
||||
|
||||
Disables creating a .vcd file.
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
.. Copyright 2003-2021 by Wilson Snyder.
|
||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
verilator_profcfuncs
|
||||
====================
|
||||
verilator_profcfunc
|
||||
===================
|
||||
|
||||
Verilator_profcfunc reads a profile report created by gprof. The names of
|
||||
the functions are then transformed, assuming the user used Verilator's
|
||||
--prof-cfuncs, and a report printed showing the percentage of time, etc, in
|
||||
each Verilog block.
|
||||
|
||||
For an overview of use of verilator_profcfuncs, see :ref:`Profiling`.
|
||||
Due to rounding errors in gprof reports, the input report's percentages may
|
||||
not total to 100%. In the verilator_profcfunc report this will get
|
||||
reported as a rounding error.
|
||||
|
||||
verilator_profcfuncs Arguments
|
||||
------------------------------
|
||||
For an overview of use of verilator_profcfunc, see :ref:`Profiling`.
|
||||
|
||||
.. program:: verilator_profcfuncs
|
||||
verilator_profcfunc Arguments
|
||||
-----------------------------
|
||||
|
||||
.. program:: verilator_profcfunc
|
||||
|
||||
.. option:: <filename>
|
||||
|
||||
@@ -15,5 +15,5 @@ options to each executable.
|
||||
exe_verilator.rst
|
||||
exe_verilator_coverage.rst
|
||||
exe_verilator_gantt.rst
|
||||
exe_verilator_profcfuncs.rst
|
||||
exe_verilator_profcfunc.rst
|
||||
exe_sim.rst
|
||||
|
||||
@@ -48,6 +48,14 @@ or "`ifdef`"'s may break other tools.
|
||||
Verilog functions where the variable is flattened out, and also enable
|
||||
other optimizations.
|
||||
|
||||
Verilator does not use any text inside the quotes for
|
||||
ordering/scheduling. If you need the $c to be called at a specific
|
||||
time, e.g. when a variable changes, then the $c must be under an
|
||||
appropriate sensitivity statement, e.g. :code:`always @(posedge clk)
|
||||
$c("func()")` to call it on every edge, or e.g. :code:`always @*
|
||||
c("func(",a,")")` to call it when :code:`a` changes (the latter working
|
||||
because :code:`a` is outside the quotes).
|
||||
|
||||
If you will be reading or writing any Verilog variables inside the C++
|
||||
functions, the Verilog signals must be declared with
|
||||
:option:`/*verilator&32;public*/` metacomments.
|
||||
@@ -176,10 +184,9 @@ or "`ifdef`"'s may break other tools.
|
||||
|
||||
.. option:: /*verilator&32;no_clocker*/
|
||||
|
||||
Used after a signal declaration to indicate the signal is used as clock
|
||||
or not. This information is used by Verilator to mark the signal as
|
||||
clocker and propagate the clocker attribute automatically to derived
|
||||
signals. See :vlopt:`--clk`.
|
||||
Specifies that the signal is used as clock or not. This information is
|
||||
used by Verilator to mark the signal and any derrived signals as
|
||||
clocker. See :vlopt:`--clk`.
|
||||
|
||||
Same as :option:`clocker` and :option:`no_clocker` in configuration
|
||||
files.
|
||||
@@ -482,6 +489,13 @@ or "`ifdef`"'s may break other tools.
|
||||
Verilator) text that should be passed through to the XML output as a tag,
|
||||
for use by downstream applications.
|
||||
|
||||
.. option:: /*verilator&32;trace_init_task*/
|
||||
|
||||
Attached to a DPI import to indicate that function should be called when
|
||||
initializing tracing. This attribute is indented only to be used
|
||||
internally in code that Verilator generates when :vlopt:`--lib-create`
|
||||
or :vlopt:`--hierarchical` is used along with :vlopt:`--trace`.
|
||||
|
||||
.. option:: /*verilator&32;tracing_off*/
|
||||
|
||||
Disable waveform tracing for all future signals that are declared in
|
||||
|
||||
+4
-2
@@ -35,8 +35,10 @@ simulators.
|
||||
Does Verilator run under Windows?
|
||||
"""""""""""""""""""""""""""""""""
|
||||
|
||||
Yes, using Cygwin. Verilated output also compiles under Microsoft Visual
|
||||
C++, but this is not tested every release.
|
||||
Yes, ideally run Ubuntu under Windows Subsystem for Linux (WSL2).
|
||||
Alternatively use Cygwin, though this tends to be slower and is not
|
||||
regurally tested. Verilated output also compiles under Microsoft Visual
|
||||
C++, but this is also not regurally tested.
|
||||
|
||||
|
||||
Can you provide binaries?
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 37 KiB |
+21
-1
@@ -74,7 +74,7 @@ For --cc/--sc, it creates:
|
||||
- DPI import and export declarations (from --dpi)
|
||||
* - *{prefix}*\ __Dpi.cpp
|
||||
- Global DPI export wrappers (from --dpi)
|
||||
* - *{prefix}*\ __Dpi_Export\ *{__n}\ .cpp
|
||||
* - *{prefix}*\ __Dpi_Export\ *{__n}*\ .cpp
|
||||
- DPI export wrappers scoped to this particular model (from --dpi)
|
||||
* - *{prefix}*\ __Inlines.h
|
||||
- Inline support functions
|
||||
@@ -145,3 +145,23 @@ After running Make, the C++ compiler may produce the following:
|
||||
- Intermediate dependencies
|
||||
* - *{prefix}{misc}*\ .o
|
||||
- Intermediate objects
|
||||
|
||||
The Verilated executable may produce the following:
|
||||
|
||||
.. list-table::
|
||||
|
||||
* - coverage.dat
|
||||
- Code coverage output, and default input filename for :command:`verilator_coverage`
|
||||
* - gmon.out
|
||||
- GCC/clang code profiler output, often fed into :command:`verilator_profcfunc`
|
||||
* - profile.vlt
|
||||
- -profile data file for :ref:`Thread PGO`
|
||||
* - profile_threads.dat
|
||||
- -profile-threads data file for :command:`verilator_gnatt`
|
||||
|
||||
Verilator_gantt may produce the following:
|
||||
|
||||
.. list-table::
|
||||
|
||||
* - profile_threads.vcd
|
||||
- Gantt report waveform output
|
||||
|
||||
+12
-11
@@ -23,6 +23,8 @@ package:
|
||||
|
||||
apt-get install verilator # On Ubuntu
|
||||
|
||||
For other distributions refer to `Repology Verilator Distro Packages
|
||||
<https://repology.org/project/verilator>`__.
|
||||
|
||||
.. _Git Install:
|
||||
|
||||
@@ -44,21 +46,20 @@ In brief, to install from git:
|
||||
#sudo apt-get install zlibc zlib1g zlib1g-dev # Ubuntu only (ignore if gives error)
|
||||
|
||||
git clone https://github.com/verilator/verilator # Only first time
|
||||
## Note the URL above is not a page you can see with a browser, it's for git only
|
||||
|
||||
# Every time you need to build:
|
||||
unsetenv VERILATOR_ROOT # For csh; ignore error if on bash
|
||||
unset VERILATOR_ROOT # For bash
|
||||
cd verilator
|
||||
git pull # Make sure git repository is up-to-date
|
||||
git tag # See what versions exist
|
||||
git pull # Make sure git repository is up-to-date
|
||||
git tag # See what versions exist
|
||||
#git checkout master # Use development branch (e.g. recent bug fixes)
|
||||
#git checkout stable # Use most recent stable release
|
||||
#git checkout v{version} # Switch to specified release version
|
||||
|
||||
autoconf # Create ./configure script
|
||||
./configure # Configure and create Makefile
|
||||
make -j # Build Verilator itself
|
||||
autoconf # Create ./configure script
|
||||
./configure # Configure and create Makefile
|
||||
make -j `nproc` # Build Verilator itself (if error, try just 'make')
|
||||
sudo make install
|
||||
|
||||
|
||||
@@ -77,10 +78,10 @@ OS Requirements
|
||||
|
||||
Verilator is developed and has primary testing on Ubuntu, with additional
|
||||
testing on FreeBSD and Apple OS-X. Versions have also built on Redhat
|
||||
Linux, HPUX and Solaris. It should run with minor porting on any
|
||||
GNU/Linux-ish platform. Verilator also works on Windows under Cygwin, and
|
||||
Windows under MinGW (gcc -mno-cygwin). Verilated output (not Verilator
|
||||
itself) compiles under all the options above, plus using MSVC++.
|
||||
Linux, and other flavors of GNU/Linux-ish platforms. Verilator also works
|
||||
on Windows Subsystem for Linux (WSL2), Windows under Cygwin, and Windows
|
||||
under MinGW (gcc -mno-cygwin). Verilated output (not Verilator itself)
|
||||
compiles under all the options above, plus using MSVC++.
|
||||
|
||||
|
||||
Install Prerequisites
|
||||
@@ -286,7 +287,7 @@ Compile Verilator:
|
||||
|
||||
::
|
||||
|
||||
make -j
|
||||
make -j `nproc` # Or if error on `nproc`, the number of CPUs in system
|
||||
|
||||
|
||||
Test
|
||||
|
||||
+126
-6
@@ -26,7 +26,8 @@ risk of reset bugs in trade for performance; see the above documentation
|
||||
for these options.
|
||||
|
||||
If using Verilated multithreaded, use ``numactl`` to ensure you are using
|
||||
non-conflicting hardware resources. See :ref:`Multithreading`.
|
||||
non-conflicting hardware resources. See :ref:`Multithreading`. Also
|
||||
consider using profile-guided optimization, see :ref:`Thread PGO`.
|
||||
|
||||
Minor Verilog code changes can also give big wins. You should not have any
|
||||
UNOPTFLAT warnings from Verilator. Fixing these warnings can result in
|
||||
@@ -93,9 +94,7 @@ cases, for example regressions, it is usually worth spending extra
|
||||
compilation time to reduce total CPU time.
|
||||
|
||||
If you will be running many simulations on a single model, you can
|
||||
investigate profile guided optimization. With GCC, using GCC's
|
||||
"-fprofile-arcs", then GCC's "-fbranch-probabilities" will yield another
|
||||
15% or so.
|
||||
investigate profile guided optimization. See :ref:`Compiler PGO`.
|
||||
|
||||
Modern compilers also support link-time optimization (LTO), which can help
|
||||
especially if you link in DPI code. To enable LTO on GCC, pass "-flto" in
|
||||
@@ -230,7 +229,7 @@ coverage point insertions into the model and collect the coverage data.
|
||||
|
||||
To get the coverage data from the model, in the user wrapper code,
|
||||
typically at the end once a test passes, call
|
||||
:code:`Verilated::coveragep()->write` with an argument of the filename for
|
||||
:code:`Verilated::threadContextp()->coveragep()->write` with an argument of the filename for
|
||||
the coverage data file to write coverage data to (typically
|
||||
"logs/coverage.dat").
|
||||
|
||||
@@ -298,6 +297,9 @@ With the :vlopt:`--prof-threads` option, Verilator will:
|
||||
* Add code to save profiling data in non-human-friendly form to the file
|
||||
specified with :vlopt:`+verilator+prof+threads+file+\<filename\>`.
|
||||
|
||||
* Add code to save profiling data for thread profile-guided
|
||||
optimization. See :ref:`Thread PGO`.
|
||||
|
||||
The :command:`verilator_gantt` program may then be run to transform the
|
||||
saved profiling file into a nicer visual format and produce some related
|
||||
statistics.
|
||||
@@ -306,7 +308,7 @@ statistics.
|
||||
|
||||
Example verilator_gantt output, as viewed with GTKWave.
|
||||
|
||||
The parallelism shows the number of CPUs being used at a given moment.
|
||||
The measured_parallelism shows the number of CPUs being used at a given moment.
|
||||
|
||||
The cpu_thread section shows which thread is executing on each of the physical CPUs.
|
||||
|
||||
@@ -314,6 +316,7 @@ statistics.
|
||||
|
||||
For more information see :command:`verilator_gantt`.
|
||||
|
||||
|
||||
.. _Profiling ccache efficiency:
|
||||
|
||||
Profiling ccache efficiency
|
||||
@@ -377,3 +380,120 @@ For example:
|
||||
os >> main_time;
|
||||
os >> *topp;
|
||||
}
|
||||
|
||||
|
||||
Profile-Guided Optimization
|
||||
===========================
|
||||
|
||||
Profile-guided optimization is the technique where profiling data is
|
||||
collected by running your simulation executable, then this information is
|
||||
used to guide the next Verilation or compilation.
|
||||
|
||||
There are two forms of profile-guided optimizations. Unfortunately for
|
||||
best results they must each be performed from the highest level code to the
|
||||
lowest, which means performing them separately and in this order:
|
||||
|
||||
* :ref:`Thread PGO`
|
||||
* :ref:`Compiler PGO`
|
||||
|
||||
Other forms of PGO may be supported in the future, such as clock and reset
|
||||
toggle rate PGO, branch prediction PGO, statement execution time PGO, or
|
||||
others as they prove beneficial.
|
||||
|
||||
|
||||
.. _Thread PGO:
|
||||
|
||||
Thread Profile-Guided Optimization
|
||||
----------------------------------
|
||||
|
||||
Verilator supports thread profile-guided optimization (Thread PGO) to
|
||||
improve multithreaded performance.
|
||||
|
||||
When using multithreading, Verilator computes how long macro tasks take and
|
||||
tries to balance those across threads. (What is a macro-task? See the
|
||||
Verilator internals document (:file:`docs/internals.rst` in the
|
||||
distribution.) If the estimations are incorrect, the threads will not be
|
||||
balanced, leading to decreased performance. Thread PGO allows collecting
|
||||
profiling data to replace the estimates and better optimize these
|
||||
decisions.
|
||||
|
||||
To use Thread PGO, Verilate the model with the :vlopt:`--prof-threads`
|
||||
option.
|
||||
|
||||
Run the model executable. When the executable exits, it will create a
|
||||
profile.vlt file.
|
||||
|
||||
Rerun Verilator, optionally omitting the :vlopt:`--prof-threads` option,
|
||||
and adding the profile.vlt generated earlier to the command line.
|
||||
|
||||
Note there is no Verilator equivalent to GCC's --fprofile-use. Verilator's
|
||||
profile data file (profile.vlt) can be placed on the verilator command line
|
||||
directly without any prefix.
|
||||
|
||||
If results from multiple simulations are to be used in generating the
|
||||
optimization, multiple simulation's profile.vlt may be concatenated
|
||||
externally, or each of the files may be fed as separate command line
|
||||
options into Verilator. Verilator will simply sum the profile results, so
|
||||
a longer running test will have proportionally more weight for optimization
|
||||
than a shorter running test.
|
||||
|
||||
If you provide any profile feedback data to Verilator, and it cannot use
|
||||
it, it will issue the :option:`PROFOUTOFDATE` warning that threads were
|
||||
scheduled using estimated costs. This usually indicates that the profile
|
||||
data was generated from different Verilog source code than Verilator is
|
||||
currently running against. Therefore, repeat the data collection phase to
|
||||
create new profiling data, then rerun Verilator with the same input source
|
||||
files and that new profiling data.
|
||||
|
||||
|
||||
.. _Compiler PGO:
|
||||
|
||||
Compiler Profile-Guided Optimization
|
||||
------------------------------------
|
||||
|
||||
GCC and Clang support compiler profile-guided optimization (PGO). This
|
||||
optimizes any C/C++ program including Verilated code. Using compiler PGO
|
||||
typically yields improvements of 5-15% on both single-threaded and
|
||||
multi-threaded models.
|
||||
|
||||
To use compiler PGO with GCC or Clang, please see the appropriate compiler
|
||||
documentation. The process in GCC 10 was as follows:
|
||||
|
||||
1. Compile the Verilated model with the compiler's "-fprofile-generate"
|
||||
flag:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
verilator [whatever_flags] --make \
|
||||
-CFLAGS -fprofile-generate -LDFLAGS -fprofile-generate
|
||||
|
||||
or, if calling make yourself, add -fprofile-generate appropriately to your
|
||||
Makefile.
|
||||
|
||||
2. Run your simulation. This will create \*.gcda file(s) in the same
|
||||
directory as the source files.
|
||||
|
||||
3. Recompile the model with -fprofile-use. The compiler will read the
|
||||
\*.gcda file(s).
|
||||
|
||||
For GCC:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
verilator [whatever_flags] --build \
|
||||
-CFLAGS "-fprofile-use -fprofile-correction"
|
||||
|
||||
For Clang:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
llvm-profdata merge -output default.profdata *.profraw
|
||||
verilator [whatever_flags] --build \
|
||||
-CFLAGS "-fprofile-use -fprofile-correction"
|
||||
|
||||
or, if calling make yourself, add these CFLAGS switches appropriately to
|
||||
your Makefile.
|
||||
|
||||
Clang and GCC also support -fauto-profile which uses sample-based
|
||||
feedback-directed optimization. See the appropriate compiler
|
||||
documentation.
|
||||
|
||||
@@ -70,10 +70,9 @@ Verilator is run in hierarchical mode on the whole SoC. Verilator will
|
||||
make two models, one for the CPU hierarchy block, and one for the SoC. The
|
||||
Verialted code for the SoC will automatically call the CPU Verilated model.
|
||||
|
||||
The current hierarchical Verilation is based on protect-lib. Each hierarchy
|
||||
block is Verilated to a protect-lib. User modules of the hierarchy blocks
|
||||
will see a tiny wrapper generated by protect-lib instead of the actual
|
||||
design.
|
||||
The current hierarchical Verilation is based on :vlopt:`--lib-create`. Each
|
||||
hierarchy block is Verilated into a library. User modules of the hierarchy
|
||||
blocks will see a tiny wrapper generated by :vlopt:`--lib-create`.
|
||||
|
||||
|
||||
Usage
|
||||
@@ -216,6 +215,12 @@ 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 making frequent use of DPI imported functions in a multi-threaded
|
||||
model, it may be beneficial to performance to adjust the
|
||||
:vlopt:`--instr-count-dpi` option based on some experimentation. This
|
||||
influences the partitioning of the model by adjusting the assumed execution
|
||||
time of DPI imports.
|
||||
|
||||
The :vlopt:`--trace-threads` options can be used to produce trace dumps
|
||||
using multiple threads. If :vlopt:`--trace-threads` is set without
|
||||
:vlopt:`--threads`, then :vlopt:`--trace-threads` will imply
|
||||
|
||||
+85
-36
@@ -130,9 +130,10 @@ List Of Warnings
|
||||
|
||||
.. option:: BADSTDPRAGMA
|
||||
|
||||
Error that a pragma is badly formed, when that pragma is defined by IEEE1800-2017.
|
||||
For example, an empty `pragma line, or an incorrect specified '`pragma protect'.
|
||||
Note that 3rd party pragmas not defined by IEEE1800-2017 are ignored.
|
||||
Error that a pragma is badly formed, when that pragma is defined by IEEE
|
||||
1800-2017. For example, an empty pragma line, or an incorrect specified
|
||||
'pragma protect'. Note that 3rd party pragmas not defined by IEEE
|
||||
1800-2017 are ignored.
|
||||
|
||||
|
||||
.. option:: BLKANDNBLK
|
||||
@@ -481,39 +482,44 @@ List Of Warnings
|
||||
|
||||
Faulty example:
|
||||
|
||||
.. code-block:: sv
|
||||
.. include:: ../../docs/gen/ex_DIDNOTCONVERGE_faulty.rst
|
||||
|
||||
always_comb b = ~a;
|
||||
always_comb a = b
|
||||
Results in at runtime (not when Verilated):
|
||||
|
||||
This code will toggle forever, and thus to prevent an infinite loop, the
|
||||
executable will give the didn't converge error.
|
||||
.. include:: ../../docs/gen/ex_DIDNOTCONVERGE_nodbg_msg.rst
|
||||
|
||||
To debug this, first review any UNOPTFLAT warnings that were ignored.
|
||||
Though typically it is safe to ignore UNOPTFLAT (at a performance cost),
|
||||
at the time of issuing a UNOPTFLAT Verilator did not know if the logic
|
||||
would eventually converge and assumed it would.
|
||||
This is because the signals keep toggling even with out time
|
||||
passing. Thus to prevent an infinite loop, the Verilated executable
|
||||
gives the DIDNOTCONVERGE error.
|
||||
|
||||
To debug this, first review any UNOPT or UNOPTFLAT warnings that were
|
||||
ignored. Though typically it is safe to ignore UNOPTFLAT (at a
|
||||
performance cost), at the time of issuing a UNOPTFLAT Verilator did not
|
||||
know if the logic would eventually converge and assumed it would.
|
||||
|
||||
Next, run Verilator with :vlopt:`--prof-cfuncs -CFLAGS -DVL_DEBUG
|
||||
<--prof-cfuncs>`. Rerun the test. Now just before the convergence
|
||||
error you should see additional output similar to this:
|
||||
|
||||
.. code-block::
|
||||
.. include:: ../../docs/gen/ex_DIDNOTCONVERGE_msg.rst
|
||||
|
||||
CHANGE: filename.v:1: b
|
||||
CHANGE: filename.v:2: a
|
||||
|
||||
This means that signal b and signal a keep changing, inspect the code
|
||||
that modifies these signals. Note if many signals are getting printed
|
||||
then most likely all of them are oscillating. It may also be that
|
||||
e.g. "a" may be oscillating, then "a" feeds signal "c" which then is
|
||||
also reported as oscillating.
|
||||
The CHANGE line means that on the given filename and line number that
|
||||
drove a signal, the signal 'a' kept changing. Inspect the code that
|
||||
modifies these signals. Note if many signals are getting printed then
|
||||
most likely all of them are oscillating. It may also be that e.g. "a"
|
||||
may be oscillating, then "a" feeds signal "c" which then is also
|
||||
reported as oscillating.
|
||||
|
||||
One way DIDNOTCONVERGE may occur is flops are built out of gate
|
||||
primitives. error. Verilator does not support building flops or latches
|
||||
out of gate primitives, and any such code must change to use behavioral
|
||||
primitives. Verilator does not support building flops or latches out of
|
||||
gate primitives, and any such code must change to use behavioral
|
||||
constructs (e.g. always_ff and always_latch).
|
||||
|
||||
Another way DIDNOTCONVERGE may occur is if # delays are used to generate
|
||||
clocks. Verilator ignores the delays and gives an :option:`ASSIGNDLY`
|
||||
or :option:`STMTDLY` warning. If these were suppressed, due to the
|
||||
absense of the delay, the code may now oscillate.
|
||||
|
||||
Finally, rare, more difficult cases can be debugged like a C++ program;
|
||||
either enter :command:`gdb` and use its tracing facilities, or edit the
|
||||
generated C++ code to add appropriate prints to see what is going on.
|
||||
@@ -572,7 +578,7 @@ List Of Warnings
|
||||
must end in newline, as otherwise for example :command:`cat` with the
|
||||
file as an argument may produce undesirable results.
|
||||
|
||||
Repair by adding a newline to the end of the file.
|
||||
Repair by appending a newline to the end of the file.
|
||||
|
||||
Disabled by default as this is a code style warning; it will simulate
|
||||
correctly.
|
||||
@@ -792,8 +798,8 @@ List Of Warnings
|
||||
.. TODO better example
|
||||
|
||||
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 (and only put each file once on the command
|
||||
a coding error, or a mistake in a library file, and it's good practice
|
||||
to have 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.
|
||||
|
||||
@@ -1005,9 +1011,25 @@ List Of Warnings
|
||||
a var/reg must be used as the target of procedural assignments.
|
||||
|
||||
|
||||
.. option:: PROFOUTOFDATE
|
||||
|
||||
Warns that threads were scheduled using estimated costs, despite the
|
||||
fact that data was provided from profile-guided optimization (see
|
||||
:ref:`Thread PGO`) as fed into Verilator using the
|
||||
:option:`profile_data` configuration file option. This usually
|
||||
indicates that the profile data was generated from different Verilog
|
||||
source code than Verilator is currently running against.
|
||||
|
||||
It is recommended to create new profiling data, then rerun Verilator
|
||||
with the same input source files and that new profiling data.
|
||||
|
||||
Ignoring this warning may only slow simulations, it will simulate
|
||||
correctly.
|
||||
|
||||
|
||||
.. option:: PROTECTED
|
||||
|
||||
Warning that a '`pragma protected' section was encountered. The code
|
||||
Warning that a 'pragma protected' section was encountered. The code
|
||||
inside the protected region will be partly checked for correctness, but is
|
||||
otherwise ignored.
|
||||
|
||||
@@ -1183,9 +1205,25 @@ List Of Warnings
|
||||
|
||||
Faulty example:
|
||||
|
||||
.. code-block:: sv
|
||||
.. include:: ../../docs/gen/ex_STMTDLY_faulty.rst
|
||||
|
||||
#100 $finish; //<--- Warning
|
||||
Results in:
|
||||
|
||||
.. include:: ../../docs/gen/ex_STMTDLY_msg.rst
|
||||
|
||||
This is a warning because Verilator does not support delayed statements.
|
||||
It will simply ignore all such delays. In many cases ignoring a delay
|
||||
might be harmless, but if the delayed statement is, as in this example,
|
||||
used to cause some important action at a later time, it might be an
|
||||
important difference.
|
||||
|
||||
Some possible work arounds:
|
||||
|
||||
* Move the delayed statement into the C++ wrapper file, where the
|
||||
stimulus and clock generation can be done in C++.
|
||||
|
||||
* Convert the statement into a FSM, or other statement that tests
|
||||
against $time.
|
||||
|
||||
|
||||
.. option:: SYMRSVDWORD
|
||||
@@ -1353,7 +1391,9 @@ List Of Warnings
|
||||
|
||||
Often UNOPTFLAT is caused by logic that isn't truly circular as viewed by
|
||||
synthesis which analyzes interconnection per-bit, but is circular to
|
||||
simulation which analyzes per-bus:
|
||||
simulation which analyzes per-bus.
|
||||
|
||||
Faulty example:
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
@@ -1466,9 +1506,16 @@ List Of Warnings
|
||||
Error that a construct might be legal according to IEEE but is not
|
||||
currently supported by Verilator.
|
||||
|
||||
A typical workaround is to recode the construct into a simpler and more
|
||||
common alternative language construct.
|
||||
|
||||
Alternatively, check if the construct is supported by other tools, and
|
||||
if so please consider submitting a github pull request against the
|
||||
Verilator sources to implement the missing unsupported feature.
|
||||
|
||||
This error may be ignored with :vlopt:`--bbox-unsup`, however this will
|
||||
make the design simulate incorrectly; see the details under
|
||||
:vlopt:`--bbox-unsup`.
|
||||
make the design simulate incorrectly and is only intended for lint
|
||||
usage; see the details under :vlopt:`--bbox-unsup`.
|
||||
|
||||
|
||||
.. option:: UNUSED
|
||||
@@ -1598,7 +1645,7 @@ List Of Warnings
|
||||
* A part select has a different size then needed to index into the
|
||||
packed or unpacked array (etc).
|
||||
|
||||
Verilator for attempts to track the minimum width of unsized constants
|
||||
Verilator attempts to track the minimum width of unsized constants
|
||||
and will suppress the warning when the minimum width is appropriate to
|
||||
fit the required size.
|
||||
|
||||
@@ -1607,11 +1654,13 @@ List Of Warnings
|
||||
|
||||
The recommendation is to fix these issues by:
|
||||
|
||||
* Resizing the variable or constant. E.g. :code:`2'd2` instead of :code:`3'd2`.
|
||||
* Resizing the variable or constant to match the needed size for the
|
||||
expression. E.g. :code:`2'd2` instead of :code:`3'd2`.
|
||||
|
||||
* Using :code:`'0` or :code:`'1`.
|
||||
* Using :code:`'0` or :code:`'1` which automatically resize in an
|
||||
expression.
|
||||
|
||||
* Using part select to narrow a variable. E.g. :code:`too_wide[1:0]`.
|
||||
* Using part selects to narrow a variable. E.g. :code:`too_wide[1:0]`.
|
||||
|
||||
* Using concatenate to widen a variable. E.g. :code:`{1'b1, too_narrow}`.
|
||||
|
||||
|
||||
@@ -405,6 +405,9 @@ 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.
|
||||
|
||||
Profile-guided optimization make this a bit better, by adjusting mtask
|
||||
scheduling, but this does not yet guide the packing into mtasks.
|
||||
|
||||
|
||||
Performance Regression
|
||||
""""""""""""""""""""""
|
||||
|
||||
@@ -676,7 +676,6 @@ prev
|
||||
printf
|
||||
printtimescale
|
||||
profcfunc
|
||||
profcfuncs
|
||||
prototyptes
|
||||
ps
|
||||
pthread
|
||||
|
||||
@@ -99,7 +99,7 @@ run:
|
||||
|
||||
@echo "-- DONE --------------------"
|
||||
@echo "Note: Once this example is understood, see examples/cmake_tracing_sc."
|
||||
@echo "Note: Also see the EXAMPLE section in the verilator manpage/document."
|
||||
@echo "Note: See also https://verilator.org/guide/latest/examples.html"
|
||||
|
||||
clean mostlyclean distclean maintainer-clean:
|
||||
@rm -rf build logs
|
||||
|
||||
@@ -40,7 +40,7 @@ default:
|
||||
obj_dir/Vtop
|
||||
@echo "-- DONE --------------------"
|
||||
@echo "Note: Once this example is understood, see examples/make_tracing_c."
|
||||
@echo "Note: Also see the EXAMPLE section in the verilator manpage/document."
|
||||
@echo "Note: See also https://verilator.org/guide/latest/examples.html"
|
||||
|
||||
######################################################################
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
// any use, without warranty, 2017 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// See also the EXAMPLE section in the verilator manpage/document.
|
||||
// See also https://verilator.org/guide/latest/examples.html"
|
||||
|
||||
module top;
|
||||
initial begin
|
||||
$display("Hello World!");
|
||||
|
||||
@@ -49,7 +49,7 @@ run:
|
||||
obj_dir/Vtop
|
||||
@echo "-- DONE --------------------"
|
||||
@echo "Note: Once this example is understood, see examples/make_tracing_sc."
|
||||
@echo "Note: Also see the EXAMPLE section in the verilator manpage/document."
|
||||
@echo "Note: See also https://verilator.org/guide/latest/examples.html"
|
||||
|
||||
######################################################################
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
// any use, without warranty, 2017 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// See also the EXAMPLE section in the verilator manpage/document.
|
||||
// See also https://verilator.org/guide/latest/examples.html"
|
||||
|
||||
module top;
|
||||
initial begin
|
||||
$display("Hello World!");
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
// any use, without warranty, 2019 by Todd Strader.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// See also the EXAMPLE section in the verilator manpage/document.
|
||||
// See also https://verilator.org/guide/latest/examples.html"
|
||||
|
||||
module top (input clk);
|
||||
|
||||
integer cyc = 0;
|
||||
|
||||
@@ -4989,9 +4989,7 @@ unsigned int secnum = 0;
|
||||
int blocks_skipped = 0;
|
||||
fst_off_t blkpos = 0;
|
||||
uint64_t seclen, beg_tim;
|
||||
#ifdef FST_DEBUG
|
||||
uint64_t end_tim;
|
||||
#endif
|
||||
uint64_t frame_uclen, frame_clen, frame_maxhandle, vc_maxhandle;
|
||||
fst_off_t vc_start;
|
||||
fst_off_t indx_pntr, indx_pos;
|
||||
@@ -5058,14 +5056,11 @@ for(;;)
|
||||
if(!seclen) break;
|
||||
|
||||
beg_tim = fstReaderUint64(xc->f);
|
||||
#ifdef FST_DEBUG
|
||||
end_tim =
|
||||
#endif
|
||||
fstReaderUint64(xc->f);
|
||||
end_tim = fstReaderUint64(xc->f);
|
||||
|
||||
if(xc->limit_range_valid)
|
||||
{
|
||||
if(beg_tim < xc->limit_range_start)
|
||||
if(end_tim < xc->limit_range_start)
|
||||
{
|
||||
blocks_skipped++;
|
||||
blkpos += seclen;
|
||||
|
||||
+154
-149
@@ -25,7 +25,7 @@
|
||||
//=========================================================================
|
||||
// Internal note:
|
||||
//
|
||||
// verilated.o may exist both in protect-lib (incrementally linked .a/.so)
|
||||
// verilated.o may exist both in --lib-create (incrementally linked .a/.so)
|
||||
// and the main module. Both refer the same instance of static
|
||||
// variables/VL_THREAD_LOCAL in verilated.o such as Verilated, or
|
||||
// VerilatedImpData. This is important to share that state, but the
|
||||
@@ -173,9 +173,9 @@ void vl_stop_maybe(const char* filename, int linenum, const char* hier, bool may
|
||||
|
||||
void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAFE {
|
||||
#ifdef VL_THREADED
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { //
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
||||
vl_finish(filename, linenum, hier);
|
||||
}));
|
||||
}});
|
||||
#else
|
||||
vl_finish(filename, linenum, hier);
|
||||
#endif
|
||||
@@ -183,9 +183,9 @@ void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAF
|
||||
|
||||
void VL_STOP_MT(const char* filename, int linenum, const char* hier, bool maybe) VL_MT_SAFE {
|
||||
#ifdef VL_THREADED
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { //
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
||||
vl_stop_maybe(filename, linenum, hier, maybe);
|
||||
}));
|
||||
}});
|
||||
#else
|
||||
vl_stop_maybe(filename, linenum, hier, maybe);
|
||||
#endif
|
||||
@@ -193,9 +193,9 @@ void VL_STOP_MT(const char* filename, int linenum, const char* hier, bool maybe)
|
||||
|
||||
void VL_FATAL_MT(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_SAFE {
|
||||
#ifdef VL_THREADED
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { //
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
||||
vl_fatal(filename, linenum, hier, msg);
|
||||
}));
|
||||
}});
|
||||
#else
|
||||
vl_fatal(filename, linenum, hier, msg);
|
||||
#endif
|
||||
@@ -208,14 +208,14 @@ void VL_FATAL_MT(const char* filename, int linenum, const char* hier, const char
|
||||
std::string _vl_string_vprintf(const char* formatp, va_list ap) VL_MT_SAFE {
|
||||
va_list aq;
|
||||
va_copy(aq, ap);
|
||||
int len = VL_VSNPRINTF(nullptr, 0, formatp, aq);
|
||||
const size_t len = VL_VSNPRINTF(nullptr, 0, formatp, aq);
|
||||
va_end(aq);
|
||||
if (VL_UNLIKELY(len < 1)) return "";
|
||||
|
||||
char* bufp = new char[len + 1];
|
||||
char* const bufp = new char[len + 1];
|
||||
VL_VSNPRINTF(bufp, len + 1, formatp, ap);
|
||||
|
||||
const std::string out = std::string(bufp, len);
|
||||
std::string out{bufp, len}; // Not const to allow move optimization
|
||||
delete[] bufp;
|
||||
return out;
|
||||
}
|
||||
@@ -263,9 +263,9 @@ void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE {
|
||||
va_start(ap, formatp);
|
||||
const std::string out = _vl_string_vprintf(formatp, ap);
|
||||
va_end(ap);
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { //
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
||||
VL_PRINTF("%s", out.c_str());
|
||||
}));
|
||||
}});
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -276,7 +276,7 @@ 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.
|
||||
static VerilatedMutex s_mutex;
|
||||
const VerilatedLockGuard lock(s_mutex); // Otherwise rand is unsafe
|
||||
const VerilatedLockGuard lock{s_mutex}; // Otherwise rand is unsafe
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
// Windows doesn't have lrand48(), although Cygwin does.
|
||||
return (std::rand() << 16) ^ std::rand();
|
||||
@@ -308,27 +308,22 @@ vluint64_t vl_rand64() VL_MT_SAFE {
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifndef VL_NO_LEGACY
|
||||
// VL_RANDOM_W currently unused as $random always 32 bits, left for backwards compatibility
|
||||
// LCOV_EXCL_START
|
||||
WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
||||
for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = vl_rand64();
|
||||
outwp[VL_WORDS_I(obits) - 1] = vl_rand64() & VL_MASK_E(obits);
|
||||
for (int i = 0; i < VL_WORDS_I(obits); ++i) outwp[i] = vl_rand64();
|
||||
// Last word is unclean
|
||||
return outwp;
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
#endif
|
||||
|
||||
IData VL_RANDOM_SEEDED_II(int obits, IData seed) VL_MT_SAFE {
|
||||
IData VL_RANDOM_SEEDED_II(IData seed) VL_MT_SAFE {
|
||||
Verilated::threadContextp()->randSeed(static_cast<int>(seed));
|
||||
return VL_RANDOM_I(obits);
|
||||
return VL_RANDOM_I();
|
||||
}
|
||||
|
||||
IData VL_RAND_RESET_I(int obits) VL_MT_SAFE {
|
||||
if (Verilated::threadContextp()->randReset() == 0) return 0;
|
||||
IData data = ~0;
|
||||
if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
|
||||
data = VL_RANDOM_I(obits);
|
||||
data = VL_RANDOM_I();
|
||||
}
|
||||
data &= VL_MASK_I(obits);
|
||||
return data;
|
||||
@@ -337,7 +332,7 @@ QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE {
|
||||
if (Verilated::threadContextp()->randReset() == 0) return 0;
|
||||
QData data = ~0ULL;
|
||||
if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
|
||||
data = VL_RANDOM_Q(obits);
|
||||
data = VL_RANDOM_Q();
|
||||
}
|
||||
data &= VL_MASK_Q(obits);
|
||||
return data;
|
||||
@@ -387,7 +382,7 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, const WDataInP lwp, const WData
|
||||
if (vw == 1) { // Single divisor word breaks rest of algorithm
|
||||
vluint64_t k = 0;
|
||||
for (int j = uw - 1; j >= 0; --j) {
|
||||
vluint64_t unw64 = ((k << 32ULL) + static_cast<vluint64_t>(lwp[j]));
|
||||
const vluint64_t unw64 = ((k << 32ULL) + static_cast<vluint64_t>(lwp[j]));
|
||||
owp[j] = unw64 / static_cast<vluint64_t>(rwp[0]);
|
||||
k = unw64 - static_cast<vluint64_t>(owp[j]) * static_cast<vluint64_t>(rwp[0]);
|
||||
}
|
||||
@@ -444,7 +439,7 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, const WDataInP lwp, const WData
|
||||
vlsint64_t t = 0; // Must be signed
|
||||
vluint64_t k = 0;
|
||||
for (int i = 0; i < vw; ++i) {
|
||||
vluint64_t p = qhat * vn[i]; // Multiply by estimate
|
||||
const vluint64_t p = qhat * vn[i]; // Multiply by estimate
|
||||
t = un[i + j] - k - (p & 0xFFFFFFFFULL); // Subtract
|
||||
un[i + j] = t;
|
||||
k = (p >> 32ULL) - (t >> 32ULL);
|
||||
@@ -698,8 +693,8 @@ std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t w
|
||||
}
|
||||
}
|
||||
} else {
|
||||
double shiftd = vl_time_multiplier(shift);
|
||||
double scaled = ld * shiftd;
|
||||
const double shiftd = vl_time_multiplier(shift);
|
||||
const double scaled = ld * shiftd;
|
||||
const double fracDiv = vl_time_multiplier(fracDigits);
|
||||
const double whole = scaled / fracDiv;
|
||||
if (!fracDigits) {
|
||||
@@ -742,14 +737,14 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
|
||||
} else if (!inPct) { // Normal text
|
||||
// Fast-forward to next escape and add to output
|
||||
const char* ep = pos;
|
||||
while (ep[0] && ep[0] != '%') ep++;
|
||||
while (ep[0] && ep[0] != '%') ++ep;
|
||||
if (ep != pos) {
|
||||
output.append(pos, ep - pos);
|
||||
pos += ep - pos - 1;
|
||||
}
|
||||
} else { // Format character
|
||||
inPct = false;
|
||||
char fmt = pos[0];
|
||||
const char fmt = pos[0];
|
||||
switch (fmt) {
|
||||
case '0': // FALLTHRU
|
||||
case '1': // FALLTHRU
|
||||
@@ -776,7 +771,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
|
||||
output += '%';
|
||||
break;
|
||||
case 'N': { // "C" string with name of module, add . if needed
|
||||
const char* cstrp = va_arg(ap, const char*);
|
||||
const char* const cstrp = va_arg(ap, const char*);
|
||||
if (VL_LIKELY(*cstrp)) {
|
||||
output += cstrp;
|
||||
output += '.';
|
||||
@@ -784,13 +779,13 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
|
||||
break;
|
||||
}
|
||||
case 'S': { // "C" string
|
||||
const char* cstrp = va_arg(ap, const char*);
|
||||
const char* const cstrp = va_arg(ap, const char*);
|
||||
output += cstrp;
|
||||
break;
|
||||
}
|
||||
case '@': { // Verilog/C++ string
|
||||
va_arg(ap, int); // # bits is ignored
|
||||
const std::string* cstrp = va_arg(ap, const std::string*);
|
||||
const std::string* const cstrp = va_arg(ap, const std::string*);
|
||||
std::string padding;
|
||||
if (width > cstrp->size()) padding.append(width - cstrp->size(), ' ');
|
||||
output += left ? (*cstrp + padding) : (padding + *cstrp);
|
||||
@@ -808,7 +803,8 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
|
||||
const int timeunit = va_arg(ap, int);
|
||||
output += _vl_vsformat_time(t_tmp, d, timeunit, left, width);
|
||||
} else {
|
||||
std::string fmts(pctp, pos - pctp + 1);
|
||||
const size_t len = pos - pctp + 1;
|
||||
const std::string fmts{pctp, len};
|
||||
VL_SNPRINTF(t_tmp, VL_VALUE_STRING_MAX_WIDTH, fmts.c_str(), d);
|
||||
output += t_tmp;
|
||||
}
|
||||
@@ -862,13 +858,13 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
|
||||
if (VL_SIGN_E(lbits, lwp[VL_WORDS_I(lbits) - 1])) {
|
||||
VlWide<VL_VALUE_STRING_MAX_WIDTH / 4 + 2> neg;
|
||||
VL_NEGATE_W(VL_WORDS_I(lbits), neg, lwp);
|
||||
append = std::string("-") + VL_DECIMAL_NW(lbits, neg);
|
||||
append = std::string{"-"} + VL_DECIMAL_NW(lbits, neg);
|
||||
} else {
|
||||
append = VL_DECIMAL_NW(lbits, lwp);
|
||||
}
|
||||
digits = append.length();
|
||||
}
|
||||
int needmore = width - digits;
|
||||
const int needmore = width - digits;
|
||||
std::string padding;
|
||||
if (needmore > 0) {
|
||||
if (pctp && pctp[0] && pctp[1] == '0') { // %0
|
||||
@@ -891,7 +887,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
|
||||
append = VL_DECIMAL_NW(lbits, lwp);
|
||||
digits = append.length();
|
||||
}
|
||||
int needmore = width - digits;
|
||||
const int needmore = width - digits;
|
||||
std::string padding;
|
||||
if (needmore > 0) {
|
||||
if (pctp && pctp[0] && pctp[1] == '0') { // %0
|
||||
@@ -952,12 +948,12 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
|
||||
case 'x':
|
||||
for (; lsb >= 0; --lsb) {
|
||||
lsb = (lsb / 4) * 4; // Next digit
|
||||
IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xf;
|
||||
const IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xf;
|
||||
output += "0123456789abcdef"[charval];
|
||||
}
|
||||
break;
|
||||
default: { // LCOV_EXCL_START
|
||||
const std::string msg = std::string("Unknown _vl_vsformat code: ") + pos[0];
|
||||
const std::string msg = std::string{"Unknown _vl_vsformat code: "} + pos[0];
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
|
||||
break;
|
||||
} // LCOV_EXCL_STOP
|
||||
@@ -969,14 +965,14 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
|
||||
}
|
||||
|
||||
static inline bool _vl_vsss_eof(FILE* fp, int floc) VL_MT_SAFE {
|
||||
if (fp) {
|
||||
if (VL_LIKELY(fp)) {
|
||||
return std::feof(fp) ? true : false; // true : false to prevent MSVC++ warning
|
||||
} else {
|
||||
return floc < 0;
|
||||
}
|
||||
}
|
||||
static inline void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE {
|
||||
if (fp) {
|
||||
if (VL_LIKELY(fp)) {
|
||||
std::fgetc(fp);
|
||||
} else {
|
||||
floc -= 8;
|
||||
@@ -985,7 +981,7 @@ static inline void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE {
|
||||
static inline int _vl_vsss_peek(FILE* fp, int& floc, const WDataInP fromp,
|
||||
const std::string& fstr) VL_MT_SAFE {
|
||||
// Get a character without advancing
|
||||
if (fp) {
|
||||
if (VL_LIKELY(fp)) {
|
||||
const int data = std::fgetc(fp);
|
||||
if (data == EOF) return EOF;
|
||||
ungetc(data, fp);
|
||||
@@ -1026,7 +1022,7 @@ static inline void _vl_vsss_read_str(FILE* fp, int& floc, const WDataInP fromp,
|
||||
}
|
||||
static inline char* _vl_vsss_read_bin(FILE* fp, int& floc, const WDataInP fromp,
|
||||
const std::string& fstr, char* beginp, std::size_t n,
|
||||
bool inhibit = false) {
|
||||
const bool inhibit = false) {
|
||||
// Variant of _vl_vsss_read_str using the same underlying I/O functions but optimized
|
||||
// specifically for block reads of N bytes (read operations are not demarcated by
|
||||
// whitespace). In the fp case, except descriptor to have been opened in binary mode.
|
||||
@@ -1040,9 +1036,7 @@ static inline char* _vl_vsss_read_bin(FILE* fp, int& floc, const WDataInP fromp,
|
||||
}
|
||||
static inline void _vl_vsss_setbit(WDataOutP owp, int obits, int lsb, int nbits,
|
||||
IData ld) VL_MT_SAFE {
|
||||
for (; nbits && lsb < obits; nbits--, lsb++, ld >>= 1) {
|
||||
VL_ASSIGNBIT_WI(0, lsb, owp, ld & 1);
|
||||
}
|
||||
for (; nbits && lsb < obits; nbits--, lsb++, ld >>= 1) { VL_ASSIGNBIT_WI(lsb, owp, ld & 1); }
|
||||
}
|
||||
static inline void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp,
|
||||
size_t posstart, size_t posend) VL_MT_SAFE {
|
||||
@@ -1099,16 +1093,16 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
|
||||
_vl_vsss_skipspace(fp, floc, fromp, fstr);
|
||||
} else if (!inPct) { // Expected Format
|
||||
_vl_vsss_skipspace(fp, floc, fromp, fstr);
|
||||
int c = _vl_vsss_peek(fp, floc, fromp, fstr);
|
||||
const int c = _vl_vsss_peek(fp, floc, fromp, fstr);
|
||||
if (c != pos[0]) goto done;
|
||||
_vl_vsss_advance(fp, floc);
|
||||
} else { // Format character
|
||||
// Skip loading spaces
|
||||
inPct = false;
|
||||
char fmt = pos[0];
|
||||
const char fmt = pos[0];
|
||||
switch (fmt) {
|
||||
case '%': {
|
||||
int c = _vl_vsss_peek(fp, floc, fromp, fstr);
|
||||
const int c = _vl_vsss_peek(fp, floc, fromp, fstr);
|
||||
if (c != '%') goto done;
|
||||
_vl_vsss_advance(fp, floc);
|
||||
break;
|
||||
@@ -1138,7 +1132,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
|
||||
for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = 0;
|
||||
switch (fmt) {
|
||||
case 'c': {
|
||||
int c = _vl_vsss_peek(fp, floc, fromp, fstr);
|
||||
const int c = _vl_vsss_peek(fp, floc, fromp, fstr);
|
||||
if (c == EOF) goto done;
|
||||
_vl_vsss_advance(fp, floc);
|
||||
owp[0] = c;
|
||||
@@ -1217,7 +1211,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
|
||||
case 'u': {
|
||||
// Read packed 2-value binary data
|
||||
const int bytes = VL_BYTES_I(obits);
|
||||
char* out = reinterpret_cast<char*>(owp);
|
||||
char* const out = reinterpret_cast<char*>(owp);
|
||||
if (!_vl_vsss_read_bin(fp, floc, fromp, fstr, out, bytes)) goto done;
|
||||
const int last = bytes % 4;
|
||||
if (last != 0
|
||||
@@ -1242,7 +1236,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
|
||||
break;
|
||||
}
|
||||
default: { // LCOV_EXCL_START
|
||||
const std::string msg = std::string("Unknown _vl_vsscanf code: ") + pos[0];
|
||||
const std::string msg = std::string{"Unknown _vl_vsscanf code: "} + pos[0];
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
|
||||
break;
|
||||
} // LCOV_EXCL_STOP
|
||||
@@ -1253,19 +1247,19 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
|
||||
// Reload data if non-wide (if wide, we put it in the right place directly)
|
||||
if (obits == 0) { // Due to inIgnore
|
||||
} else if (obits == -1) { // string
|
||||
std::string* p = va_arg(ap, std::string*);
|
||||
std::string* const p = va_arg(ap, std::string*);
|
||||
*p = t_tmp;
|
||||
} else if (obits <= VL_BYTESIZE) {
|
||||
CData* p = va_arg(ap, CData*);
|
||||
CData* const p = va_arg(ap, CData*);
|
||||
*p = owp[0];
|
||||
} else if (obits <= VL_SHORTSIZE) {
|
||||
SData* p = va_arg(ap, SData*);
|
||||
SData* const p = va_arg(ap, SData*);
|
||||
*p = owp[0];
|
||||
} else if (obits <= VL_IDATASIZE) {
|
||||
IData* p = va_arg(ap, IData*);
|
||||
IData* const p = va_arg(ap, IData*);
|
||||
*p = owp[0];
|
||||
} else if (obits <= VL_QUADSIZE) {
|
||||
QData* p = va_arg(ap, QData*);
|
||||
QData* const p = va_arg(ap, QData*);
|
||||
*p = VL_SET_QW(owp);
|
||||
}
|
||||
}
|
||||
@@ -1291,7 +1285,7 @@ void _vl_vint_to_string(int obits, char* destoutp, const WDataInP sourcep) VL_MT
|
||||
char* destp = destoutp;
|
||||
for (; lsb >= 0; --lsb) {
|
||||
lsb = (lsb / 8) * 8; // Next digit
|
||||
IData charval = VL_BITRSHIFT_W(sourcep, lsb) & 0xff;
|
||||
const IData charval = VL_BITRSHIFT_W(sourcep, lsb) & 0xff;
|
||||
if (!start || charval) {
|
||||
*destp++ = (charval == 0) ? ' ' : charval;
|
||||
start = false; // Drop leading 0s
|
||||
@@ -1337,7 +1331,7 @@ IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE {
|
||||
|
||||
if (VL_UNLIKELY(str.empty())) return 0;
|
||||
|
||||
// V3Emit has static check that bytes < VL_TO_STRING_MAX_WORDS, but be safe
|
||||
// V3Emit has static check that bytes < VL_VALUE_STRING_MAX_WORDS, but be safe
|
||||
if (VL_UNCOVERABLE(bytes < str.size())) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: fgets buffer overrun"); // LCOV_EXCL_LINE
|
||||
}
|
||||
@@ -1346,7 +1340,6 @@ IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE {
|
||||
return got;
|
||||
}
|
||||
|
||||
// declared in verilated_heavy.h
|
||||
IData VL_FGETS_NI(std::string& dest, IData fpi) VL_MT_SAFE {
|
||||
return getLine(dest, fpi, std::numeric_limits<size_t>::max());
|
||||
}
|
||||
@@ -1354,7 +1347,7 @@ IData VL_FGETS_NI(std::string& dest, IData fpi) VL_MT_SAFE {
|
||||
IData VL_FERROR_IN(IData, std::string& outputr) VL_MT_SAFE {
|
||||
// We ignore lhs/fpi - IEEE says "most recent error" so probably good enough
|
||||
const IData ret = errno;
|
||||
outputr = std::string(::std::strerror(ret));
|
||||
outputr = std::string{::std::strerror(ret)};
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1530,30 +1523,30 @@ IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi
|
||||
// Prep for reading
|
||||
IData read_count = 0;
|
||||
IData read_elements = 0;
|
||||
int start_shift = (width - 1) & ~7; // bit+7:bit gets first character
|
||||
const int start_shift = (width - 1) & ~7; // bit+7:bit gets first character
|
||||
int shift = start_shift;
|
||||
// Read the data
|
||||
// We process a character at a time, as then we don't need to deal
|
||||
// with changing buffer sizes dynamically, etc.
|
||||
while (true) {
|
||||
int c = std::fgetc(fp);
|
||||
const int c = std::fgetc(fp);
|
||||
if (VL_UNLIKELY(c == EOF)) break;
|
||||
// Shift value in
|
||||
IData entry = read_elements + start - array_lsb;
|
||||
const IData entry = read_elements + start - array_lsb;
|
||||
if (width <= 8) {
|
||||
CData* datap = &(reinterpret_cast<CData*>(memp))[entry];
|
||||
CData* const datap = &(reinterpret_cast<CData*>(memp))[entry];
|
||||
if (shift == start_shift) *datap = 0;
|
||||
*datap |= (c << shift) & VL_MASK_I(width);
|
||||
} else if (width <= 16) {
|
||||
SData* datap = &(reinterpret_cast<SData*>(memp))[entry];
|
||||
SData* const datap = &(reinterpret_cast<SData*>(memp))[entry];
|
||||
if (shift == start_shift) *datap = 0;
|
||||
*datap |= (c << shift) & VL_MASK_I(width);
|
||||
} else if (width <= VL_IDATASIZE) {
|
||||
IData* datap = &(reinterpret_cast<IData*>(memp))[entry];
|
||||
IData* const datap = &(reinterpret_cast<IData*>(memp))[entry];
|
||||
if (shift == start_shift) *datap = 0;
|
||||
*datap |= (c << shift) & VL_MASK_I(width);
|
||||
} else if (width <= VL_QUADSIZE) {
|
||||
QData* datap = &(reinterpret_cast<QData*>(memp))[entry];
|
||||
QData* const datap = &(reinterpret_cast<QData*>(memp))[entry];
|
||||
if (shift == start_shift) *datap = 0;
|
||||
*datap |= ((static_cast<QData>(c) << static_cast<QData>(shift)) & VL_MASK_Q(width));
|
||||
} else {
|
||||
@@ -1579,7 +1572,7 @@ IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE {
|
||||
return VL_SYSTEM_IW(VL_WQ_WORDS_E, lhsw);
|
||||
}
|
||||
IData VL_SYSTEM_IW(int lhswords, const WDataInP lhsp) VL_MT_SAFE {
|
||||
char filenamez[VL_TO_STRING_MAX_WORDS * VL_EDATASIZE + 1];
|
||||
char filenamez[VL_VALUE_STRING_MAX_CHARS + 1];
|
||||
_vl_vint_to_string(lhswords * VL_EDATASIZE, filenamez, lhsp);
|
||||
const int code = std::system(filenamez); // Yes, std::system() is threadsafe
|
||||
return code >> 8; // Want exit status
|
||||
@@ -1600,6 +1593,7 @@ IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_M
|
||||
inPct = true;
|
||||
} else if (!inPct) { // Normal text
|
||||
prefix += *posp;
|
||||
} else if (inPct && posp[0] == '0') { // %0
|
||||
} else { // Format character
|
||||
switch (std::tolower(*posp)) {
|
||||
case '%':
|
||||
@@ -1615,7 +1609,7 @@ IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_M
|
||||
}
|
||||
|
||||
const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefix.c_str());
|
||||
const char* dp = match.c_str() + 1 /*leading + */ + prefix.length();
|
||||
const char* const dp = match.c_str() + 1 /*leading + */ + prefix.length();
|
||||
if (match.empty()) return 0;
|
||||
|
||||
VL_ZERO_RESET_W(rbits, rwp);
|
||||
@@ -1684,9 +1678,9 @@ IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_S
|
||||
}
|
||||
}
|
||||
const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefix.c_str());
|
||||
const char* dp = match.c_str() + 1 /*leading + */ + prefix.length();
|
||||
const char* const dp = match.c_str() + 1 /*leading + */ + prefix.length();
|
||||
if (match.empty()) return 0;
|
||||
rdr = std::string(dp);
|
||||
rdr = std::string{dp};
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1726,22 +1720,22 @@ std::string VL_TOUPPER_NN(const std::string& ld) VL_MT_SAFE {
|
||||
|
||||
std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_MT_SAFE {
|
||||
// See also _vl_vint_to_string
|
||||
char destout[VL_TO_STRING_MAX_WORDS * VL_EDATASIZE + 1];
|
||||
int obits = lwords * VL_EDATASIZE;
|
||||
char destout[VL_VALUE_STRING_MAX_CHARS + 1];
|
||||
const int obits = lwords * VL_EDATASIZE;
|
||||
int lsb = obits - 1;
|
||||
bool start = true;
|
||||
char* destp = destout;
|
||||
int len = 0;
|
||||
size_t len = 0;
|
||||
for (; lsb >= 0; --lsb) {
|
||||
lsb = (lsb / 8) * 8; // Next digit
|
||||
IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xff;
|
||||
const IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xff;
|
||||
if (!start || charval) {
|
||||
*destp++ = (charval == 0) ? ' ' : charval;
|
||||
len++;
|
||||
++len;
|
||||
start = false; // Drop leading 0s
|
||||
}
|
||||
}
|
||||
return std::string(destout, len);
|
||||
return std::string{destout, len};
|
||||
}
|
||||
|
||||
std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE {
|
||||
@@ -1924,19 +1918,19 @@ void VlReadMem::setData(void* valuep, const std::string& rhs) {
|
||||
const int value
|
||||
= (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c - 'a' + 10)) : (c - '0'));
|
||||
if (m_bits <= 8) {
|
||||
CData* datap = reinterpret_cast<CData*>(valuep);
|
||||
CData* const datap = reinterpret_cast<CData*>(valuep);
|
||||
if (!innum) *datap = 0;
|
||||
*datap = ((*datap << shift) + value) & VL_MASK_I(m_bits);
|
||||
} else if (m_bits <= 16) {
|
||||
SData* datap = reinterpret_cast<SData*>(valuep);
|
||||
SData* const datap = reinterpret_cast<SData*>(valuep);
|
||||
if (!innum) *datap = 0;
|
||||
*datap = ((*datap << shift) + value) & VL_MASK_I(m_bits);
|
||||
} else if (m_bits <= VL_IDATASIZE) {
|
||||
IData* datap = reinterpret_cast<IData*>(valuep);
|
||||
IData* const datap = reinterpret_cast<IData*>(valuep);
|
||||
if (!innum) *datap = 0;
|
||||
*datap = ((*datap << shift) + value) & VL_MASK_I(m_bits);
|
||||
} else if (m_bits <= VL_QUADSIZE) {
|
||||
QData* datap = reinterpret_cast<QData*>(valuep);
|
||||
QData* const datap = reinterpret_cast<QData*>(valuep);
|
||||
if (!innum) *datap = 0;
|
||||
*datap = ((*datap << static_cast<QData>(shift)) + static_cast<QData>(value))
|
||||
& VL_MASK_Q(m_bits);
|
||||
@@ -1979,7 +1973,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
|
||||
}
|
||||
m_addr = addr + 1;
|
||||
if (m_bits <= 8) {
|
||||
const CData* datap = reinterpret_cast<const CData*>(valuep);
|
||||
const CData* const datap = reinterpret_cast<const CData*>(valuep);
|
||||
if (m_hex) {
|
||||
fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap);
|
||||
fprintf(m_fp, "\n");
|
||||
@@ -1987,7 +1981,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
|
||||
fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap));
|
||||
}
|
||||
} else if (m_bits <= 16) {
|
||||
const SData* datap = reinterpret_cast<const SData*>(valuep);
|
||||
const SData* const datap = reinterpret_cast<const SData*>(valuep);
|
||||
if (m_hex) {
|
||||
fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap);
|
||||
fprintf(m_fp, "\n");
|
||||
@@ -1995,7 +1989,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
|
||||
fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap));
|
||||
}
|
||||
} else if (m_bits <= 32) {
|
||||
const IData* datap = reinterpret_cast<const IData*>(valuep);
|
||||
const IData* const datap = reinterpret_cast<const IData*>(valuep);
|
||||
if (m_hex) {
|
||||
fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap);
|
||||
fprintf(m_fp, "\n");
|
||||
@@ -2003,7 +1997,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
|
||||
fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap));
|
||||
}
|
||||
} else if (m_bits <= 64) {
|
||||
const QData* datap = reinterpret_cast<const QData*>(valuep);
|
||||
const QData* const datap = reinterpret_cast<const QData*>(valuep);
|
||||
const vluint64_t value = VL_MASK_Q(m_bits) & *datap;
|
||||
const vluint32_t lo = value & 0xffffffff;
|
||||
const vluint32_t hi = value >> 32;
|
||||
@@ -2038,7 +2032,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
|
||||
fprintf(m_fp, "%s", formatBinary(32, data));
|
||||
}
|
||||
}
|
||||
word_idx--;
|
||||
--word_idx;
|
||||
first = false;
|
||||
}
|
||||
fprintf(m_fp, "\n");
|
||||
@@ -2057,7 +2051,7 @@ void VL_READMEM_N(bool hex, // Hex format, else binary
|
||||
) VL_MT_SAFE {
|
||||
if (start < static_cast<QData>(array_lsb)) start = array_lsb;
|
||||
|
||||
VlReadMem rmem(hex, bits, filename, start, end);
|
||||
VlReadMem rmem{hex, bits, filename, start, end};
|
||||
if (VL_UNLIKELY(!rmem.isOpen())) return;
|
||||
while (true) {
|
||||
QData addr = 0;
|
||||
@@ -2068,18 +2062,18 @@ void VL_READMEM_N(bool hex, // Hex format, else binary
|
||||
VL_FATAL_MT(filename.c_str(), rmem.linenum(), "",
|
||||
"$readmem file address beyond bounds of array");
|
||||
} else {
|
||||
QData entry = addr - array_lsb;
|
||||
const QData entry = addr - array_lsb;
|
||||
if (bits <= 8) {
|
||||
CData* datap = &(reinterpret_cast<CData*>(memp))[entry];
|
||||
CData* const datap = &(reinterpret_cast<CData*>(memp))[entry];
|
||||
rmem.setData(datap, value);
|
||||
} else if (bits <= 16) {
|
||||
SData* datap = &(reinterpret_cast<SData*>(memp))[entry];
|
||||
SData* const datap = &(reinterpret_cast<SData*>(memp))[entry];
|
||||
rmem.setData(datap, value);
|
||||
} else if (bits <= VL_IDATASIZE) {
|
||||
IData* datap = &(reinterpret_cast<IData*>(memp))[entry];
|
||||
IData* const datap = &(reinterpret_cast<IData*>(memp))[entry];
|
||||
rmem.setData(datap, value);
|
||||
} else if (bits <= VL_QUADSIZE) {
|
||||
QData* datap = &(reinterpret_cast<QData*>(memp))[entry];
|
||||
QData* const datap = &(reinterpret_cast<QData*>(memp))[entry];
|
||||
rmem.setData(datap, value);
|
||||
} else {
|
||||
WDataOutP datap
|
||||
@@ -2103,26 +2097,26 @@ void VL_WRITEMEM_N(bool hex, // Hex format, else binary
|
||||
QData start, // First array row address to write
|
||||
QData end // Last address to write, or ~0 when not specified
|
||||
) VL_MT_SAFE {
|
||||
QData addr_max = array_lsb + depth - 1;
|
||||
const QData addr_max = array_lsb + depth - 1;
|
||||
if (start < static_cast<QData>(array_lsb)) start = array_lsb;
|
||||
if (end > addr_max) end = addr_max;
|
||||
|
||||
VlWriteMem wmem(hex, bits, filename, start, end);
|
||||
VlWriteMem wmem{hex, bits, filename, start, end};
|
||||
if (VL_UNLIKELY(!wmem.isOpen())) return;
|
||||
|
||||
for (QData addr = start; addr <= end; ++addr) {
|
||||
const QData row_offset = addr - array_lsb;
|
||||
if (bits <= 8) {
|
||||
const CData* datap = &(reinterpret_cast<const CData*>(memp))[row_offset];
|
||||
const CData* const datap = &(reinterpret_cast<const CData*>(memp))[row_offset];
|
||||
wmem.print(addr, false, datap);
|
||||
} else if (bits <= 16) {
|
||||
const SData* datap = &(reinterpret_cast<const SData*>(memp))[row_offset];
|
||||
const SData* const datap = &(reinterpret_cast<const SData*>(memp))[row_offset];
|
||||
wmem.print(addr, false, datap);
|
||||
} else if (bits <= 32) {
|
||||
const IData* datap = &(reinterpret_cast<const IData*>(memp))[row_offset];
|
||||
const IData* const datap = &(reinterpret_cast<const IData*>(memp))[row_offset];
|
||||
wmem.print(addr, false, datap);
|
||||
} else if (bits <= 64) {
|
||||
const QData* datap = &(reinterpret_cast<const QData*>(memp))[row_offset];
|
||||
const QData* const datap = &(reinterpret_cast<const QData*>(memp))[row_offset];
|
||||
wmem.print(addr, false, datap);
|
||||
} else {
|
||||
const WDataInP memDatap = reinterpret_cast<WDataInP>(memp);
|
||||
@@ -2142,8 +2136,8 @@ int VL_TIME_STR_CONVERT(const char* strp) VL_PURE {
|
||||
if (!strp) return 0;
|
||||
if (*strp++ != '1') return 0;
|
||||
while (*strp == '0') {
|
||||
scale++;
|
||||
strp++;
|
||||
++scale;
|
||||
++strp;
|
||||
}
|
||||
switch (*strp++) {
|
||||
case 's': break;
|
||||
@@ -2258,8 +2252,9 @@ VerilatedContext::VerilatedContext()
|
||||
Verilated::lastContextp(this);
|
||||
Verilated::threadContextp(this);
|
||||
m_ns.m_profThreadsFilename = "profile_threads.dat";
|
||||
m_ns.m_profVltFilename = "profile.vlt";
|
||||
m_fdps.resize(31);
|
||||
std::fill(m_fdps.begin(), m_fdps.end(), (FILE*)0);
|
||||
std::fill(m_fdps.begin(), m_fdps.end(), static_cast<FILE*>(nullptr));
|
||||
m_fdFreeMct.resize(30);
|
||||
for (std::size_t i = 0, id = 1; i < m_fdFreeMct.size(); ++i, ++id) m_fdFreeMct[i] = id;
|
||||
}
|
||||
@@ -2273,19 +2268,19 @@ VerilatedContext::Serialized::Serialized() {
|
||||
}
|
||||
|
||||
void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_assertOn = flag;
|
||||
}
|
||||
void VerilatedContext::calcUnusedSigs(bool flag) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_calcUnusedSigs = flag;
|
||||
}
|
||||
void VerilatedContext::dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
|
||||
const VerilatedLockGuard lock(m_timeDumpMutex);
|
||||
const VerilatedLockGuard lock{m_timeDumpMutex};
|
||||
m_dumpfile = flag;
|
||||
}
|
||||
std::string VerilatedContext::dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
|
||||
const VerilatedLockGuard lock(m_timeDumpMutex);
|
||||
const VerilatedLockGuard lock{m_timeDumpMutex};
|
||||
return m_dumpfile;
|
||||
}
|
||||
std::string VerilatedContext::dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
|
||||
@@ -2297,61 +2292,69 @@ std::string VerilatedContext::dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDu
|
||||
return out;
|
||||
}
|
||||
void VerilatedContext::errorCount(int val) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_errorCount = val;
|
||||
}
|
||||
void VerilatedContext::errorCountInc() VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
++m_s.m_errorCount;
|
||||
}
|
||||
void VerilatedContext::errorLimit(int val) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_errorLimit = val;
|
||||
}
|
||||
void VerilatedContext::fatalOnError(bool flag) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_fatalOnError = flag;
|
||||
}
|
||||
void VerilatedContext::fatalOnVpiError(bool flag) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_fatalOnVpiError = flag;
|
||||
}
|
||||
void VerilatedContext::gotError(bool flag) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_gotError = flag;
|
||||
}
|
||||
void VerilatedContext::gotFinish(bool flag) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_gotFinish = flag;
|
||||
}
|
||||
void VerilatedContext::profThreadsStart(vluint64_t flag) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_ns.m_profThreadsStart = flag;
|
||||
}
|
||||
void VerilatedContext::profThreadsWindow(vluint64_t flag) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_ns.m_profThreadsWindow = flag;
|
||||
}
|
||||
void VerilatedContext::profThreadsFilename(const std::string& flag) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_ns.m_profThreadsFilename = flag;
|
||||
}
|
||||
std::string VerilatedContext::profThreadsFilename() const VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
return m_ns.m_profThreadsFilename;
|
||||
}
|
||||
void VerilatedContext::profVltFilename(const std::string& flag) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_ns.m_profVltFilename = flag;
|
||||
}
|
||||
std::string VerilatedContext::profVltFilename() const VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
return m_ns.m_profVltFilename;
|
||||
}
|
||||
void VerilatedContext::randReset(int val) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_randReset = val;
|
||||
}
|
||||
void VerilatedContext::timeunit(int value) VL_MT_SAFE {
|
||||
if (value < 0) value = -value; // Stored as 0..15
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_timeunit = value;
|
||||
}
|
||||
void VerilatedContext::timeprecision(int value) VL_MT_SAFE {
|
||||
if (value < 0) value = -value; // Stored as 0..15
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_timeprecision = value;
|
||||
#ifdef SYSTEMC_VERSION
|
||||
const sc_time sc_res = sc_get_time_resolution();
|
||||
@@ -2387,13 +2390,13 @@ const char* VerilatedContext::timeprecisionString() const VL_MT_SAFE {
|
||||
}
|
||||
|
||||
void VerilatedContext::commandArgs(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex) {
|
||||
const VerilatedLockGuard lock(m_argMutex);
|
||||
const VerilatedLockGuard lock{m_argMutex};
|
||||
m_args.m_argVec.clear(); // Empty first, then add
|
||||
impp()->commandArgsAddGuts(argc, argv);
|
||||
}
|
||||
void VerilatedContext::commandArgsAdd(int argc, const char** argv)
|
||||
VL_MT_SAFE_EXCLUDES(m_argMutex) {
|
||||
const VerilatedLockGuard lock(m_argMutex);
|
||||
const VerilatedLockGuard lock{m_argMutex};
|
||||
impp()->commandArgsAddGuts(argc, argv);
|
||||
}
|
||||
const char* VerilatedContext::commandArgsPlusMatch(const char* prefixp)
|
||||
@@ -2422,20 +2425,20 @@ void VerilatedContext::internalsDump() const VL_MT_SAFE {
|
||||
void VerilatedContextImp::commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(m_argMutex) {
|
||||
if (!m_args.m_argVecLoaded) m_args.m_argVec.clear();
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
m_args.m_argVec.push_back(argv[i]);
|
||||
m_args.m_argVec.emplace_back(argv[i]);
|
||||
commandArgVl(argv[i]);
|
||||
}
|
||||
m_args.m_argVecLoaded = true; // Can't just test later for empty vector, no arguments is ok
|
||||
}
|
||||
void VerilatedContextImp::commandArgDump() const VL_MT_SAFE_EXCLUDES(m_argMutex) {
|
||||
const VerilatedLockGuard lock(m_argMutex);
|
||||
const VerilatedLockGuard lock{m_argMutex};
|
||||
VL_PRINTF_MT(" Argv:");
|
||||
for (const auto& i : m_args.m_argVec) VL_PRINTF_MT(" %s", i.c_str());
|
||||
VL_PRINTF_MT("\n");
|
||||
}
|
||||
std::string VerilatedContextImp::argPlusMatch(const char* prefixp)
|
||||
VL_MT_SAFE_EXCLUDES(m_argMutex) {
|
||||
const VerilatedLockGuard lock(m_argMutex);
|
||||
const VerilatedLockGuard lock{m_argMutex};
|
||||
// Note prefixp does not include the leading "+"
|
||||
const size_t len = std::strlen(prefixp);
|
||||
if (VL_UNLIKELY(!m_args.m_argVecLoaded)) {
|
||||
@@ -2454,7 +2457,7 @@ std::string VerilatedContextImp::argPlusMatch(const char* prefixp)
|
||||
// Return string representing current argv
|
||||
// Only used by VPI so uses static storage, only supports most recent called context
|
||||
std::pair<int, char**> VerilatedContextImp::argc_argv() VL_MT_SAFE_EXCLUDES(m_argMutex) {
|
||||
const VerilatedLockGuard lock(m_argMutex);
|
||||
const VerilatedLockGuard lock{m_argMutex};
|
||||
static bool s_loaded = false;
|
||||
static int s_argc = 0;
|
||||
static char** s_argvp = nullptr;
|
||||
@@ -2495,6 +2498,8 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
|
||||
profThreadsWindow(std::atol(value.c_str()));
|
||||
} else if (commandArgVlValue(arg, "+verilator+prof+threads+file+", value /*ref*/)) {
|
||||
profThreadsFilename(value);
|
||||
} else if (commandArgVlValue(arg, "+verilator+prof+vlt+file+", value /*ref*/)) {
|
||||
profVltFilename(value);
|
||||
} else if (commandArgVlValue(arg, "+verilator+rand+reset+", value /*ref*/)) {
|
||||
randReset(std::atoi(value.c_str()));
|
||||
} else if (commandArgVlValue(arg, "+verilator+seed+", value /*ref*/)) {
|
||||
@@ -2529,7 +2534,7 @@ bool VerilatedContextImp::commandArgVlValue(const std::string& arg, const std::s
|
||||
void VerilatedContext::randSeed(int val) VL_MT_SAFE {
|
||||
// As we have per-thread state, the epoch must be static,
|
||||
// and so the rand seed's mutex must also be static
|
||||
const VerilatedLockGuard lock(VerilatedContextImp::s().s_randMutex);
|
||||
const VerilatedLockGuard lock{VerilatedContextImp::s().s_randMutex};
|
||||
m_s.m_randSeed = val;
|
||||
const vluint64_t newEpoch = VerilatedContextImp::s().s_randSeedEpoch + 1;
|
||||
// Obververs must see new epoch AFTER seed updated
|
||||
@@ -2552,10 +2557,10 @@ vluint64_t VerilatedContextImp::randSeedDefault64() const VL_MT_SAFE {
|
||||
// VerilatedContext:: Methods - scopes
|
||||
|
||||
void VerilatedContext::scopesDump() const VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(m_impdatap->m_nameMutex);
|
||||
const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
|
||||
VL_PRINTF_MT(" scopesDump:\n");
|
||||
for (const auto& i : m_impdatap->m_nameMap) {
|
||||
const VerilatedScope* scopep = i.second;
|
||||
const VerilatedScope* const scopep = i.second;
|
||||
scopep->scopeDump();
|
||||
}
|
||||
VL_PRINTF_MT("\n");
|
||||
@@ -2563,20 +2568,20 @@ void VerilatedContext::scopesDump() const VL_MT_SAFE {
|
||||
|
||||
void VerilatedContextImp::scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE {
|
||||
// Slow ok - called once/scope at construction
|
||||
const VerilatedLockGuard lock(m_impdatap->m_nameMutex);
|
||||
const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
|
||||
const auto it = m_impdatap->m_nameMap.find(scopep->name());
|
||||
if (it == m_impdatap->m_nameMap.end()) m_impdatap->m_nameMap.emplace(scopep->name(), scopep);
|
||||
}
|
||||
void VerilatedContextImp::scopeErase(const VerilatedScope* scopep) VL_MT_SAFE {
|
||||
// Slow ok - called once/scope at destruction
|
||||
const VerilatedLockGuard lock(m_impdatap->m_nameMutex);
|
||||
const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
|
||||
VerilatedImp::userEraseScope(scopep);
|
||||
const auto it = m_impdatap->m_nameMap.find(scopep->name());
|
||||
if (it != m_impdatap->m_nameMap.end()) m_impdatap->m_nameMap.erase(it);
|
||||
}
|
||||
const VerilatedScope* VerilatedContext::scopeFind(const char* namep) const VL_MT_SAFE {
|
||||
// Thread save only assuming this is called only after model construction completed
|
||||
const VerilatedLockGuard lock(m_impdatap->m_nameMutex);
|
||||
const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
|
||||
// If too slow, can assume this is only VL_MT_SAFE_POSINIT
|
||||
const auto& it = m_impdatap->m_nameMap.find(namep);
|
||||
if (VL_UNLIKELY(it == m_impdatap->m_nameMap.end())) return nullptr;
|
||||
@@ -2670,11 +2675,11 @@ static void runCallbacks(const VoidPCbList& cbs) VL_MT_SAFE {
|
||||
}
|
||||
|
||||
void Verilated::addFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(VlCbStatic.s_flushMutex);
|
||||
const VerilatedLockGuard lock{VlCbStatic.s_flushMutex};
|
||||
addCb(cb, datap, VlCbStatic.s_flushCbs);
|
||||
}
|
||||
void Verilated::removeFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(VlCbStatic.s_flushMutex);
|
||||
const VerilatedLockGuard lock{VlCbStatic.s_flushMutex};
|
||||
removeCb(cb, datap, VlCbStatic.s_flushCbs);
|
||||
}
|
||||
void Verilated::runFlushCallbacks() VL_MT_SAFE {
|
||||
@@ -2685,7 +2690,7 @@ void Verilated::runFlushCallbacks() VL_MT_SAFE {
|
||||
static int s_recursing = 0;
|
||||
#endif
|
||||
if (!s_recursing++) {
|
||||
const VerilatedLockGuard lock(VlCbStatic.s_flushMutex);
|
||||
const VerilatedLockGuard lock{VlCbStatic.s_flushMutex};
|
||||
runCallbacks(VlCbStatic.s_flushCbs);
|
||||
}
|
||||
--s_recursing;
|
||||
@@ -2698,11 +2703,11 @@ void Verilated::runFlushCallbacks() VL_MT_SAFE {
|
||||
}
|
||||
|
||||
void Verilated::addExitCb(VoidPCb cb, void* datap) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(VlCbStatic.s_exitMutex);
|
||||
const VerilatedLockGuard lock{VlCbStatic.s_exitMutex};
|
||||
addCb(cb, datap, VlCbStatic.s_exitCbs);
|
||||
}
|
||||
void Verilated::removeExitCb(VoidPCb cb, void* datap) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(VlCbStatic.s_exitMutex);
|
||||
const VerilatedLockGuard lock{VlCbStatic.s_exitMutex};
|
||||
removeCb(cb, datap, VlCbStatic.s_exitCbs);
|
||||
}
|
||||
void Verilated::runExitCallbacks() VL_MT_SAFE {
|
||||
@@ -2712,7 +2717,7 @@ void Verilated::runExitCallbacks() VL_MT_SAFE {
|
||||
static int s_recursing = 0;
|
||||
#endif
|
||||
if (!s_recursing++) {
|
||||
const VerilatedLockGuard lock(VlCbStatic.s_exitMutex);
|
||||
const VerilatedLockGuard lock{VlCbStatic.s_exitMutex};
|
||||
runCallbacks(VlCbStatic.s_exitCbs);
|
||||
}
|
||||
--s_recursing;
|
||||
@@ -2729,7 +2734,7 @@ void Verilated::nullPointerError(const char* filename, int linenum) VL_MT_SAFE {
|
||||
|
||||
void Verilated::overWidthError(const char* signame) VL_MT_SAFE {
|
||||
// Slowpath - Called only when signal sets too high of a bit
|
||||
const std::string msg = (std::string("Testbench C set input '") + signame
|
||||
const std::string msg = (std::string{"Testbench C set input '"} + signame
|
||||
+ "' to value that overflows what the signal's width can fit");
|
||||
VL_FATAL_MT("unknown", 0, "", msg.c_str());
|
||||
VL_UNREACHABLE
|
||||
@@ -2816,7 +2821,7 @@ size_t VerilatedVarProps::totalSize() const {
|
||||
void* VerilatedVarProps::datapAdjustIndex(void* datap, int dim, int indx) const {
|
||||
if (VL_UNLIKELY(dim <= 0 || dim > udims())) return nullptr;
|
||||
if (VL_UNLIKELY(indx < low(dim) || indx > high(dim))) return nullptr;
|
||||
int indxAdj = indx - low(dim);
|
||||
const int indxAdj = indx - low(dim);
|
||||
vluint8_t* bytep = reinterpret_cast<vluint8_t*>(datap);
|
||||
// If on index 1 of a 2 index array, then each index 1 is index2sz*entsz
|
||||
size_t slicesz = entSize();
|
||||
@@ -2846,7 +2851,7 @@ void VerilatedScope::configure(VerilatedSyms* symsp, const char* prefixp, const
|
||||
m_type = type;
|
||||
m_timeunit = timeunit;
|
||||
{
|
||||
char* namep = new char[std::strlen(prefixp) + std::strlen(suffixp) + 2];
|
||||
char* const namep = new char[std::strlen(prefixp) + std::strlen(suffixp) + 2];
|
||||
char* dp = namep;
|
||||
for (const char* sp = prefixp; *sp;) *dp++ = *sp++;
|
||||
if (*prefixp && *suffixp) *dp++ = '.';
|
||||
@@ -2861,7 +2866,7 @@ void VerilatedScope::configure(VerilatedSyms* symsp, const char* prefixp, const
|
||||
void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE {
|
||||
// Slowpath - called once/scope*export at construction
|
||||
// Insert a exported function into scope table
|
||||
int funcnum = VerilatedImp::exportInsert(namep);
|
||||
const int funcnum = VerilatedImp::exportInsert(namep);
|
||||
if (!finalize) {
|
||||
// Need two passes so we know array size to create
|
||||
// Alternative is to dynamically stretch the array, which is more code, and slower.
|
||||
@@ -2886,7 +2891,7 @@ void VerilatedScope::varInsert(int finalize, const char* namep, void* datap, boo
|
||||
// statically construct from that.
|
||||
if (!finalize) return;
|
||||
|
||||
if (!m_varsp) m_varsp = new VerilatedVarNameMap();
|
||||
if (!m_varsp) m_varsp = new VerilatedVarNameMap;
|
||||
VerilatedVar var(namep, datap, vltype, static_cast<VerilatedVarFlags>(vlflags), dims, isParam);
|
||||
|
||||
va_list ap;
|
||||
@@ -2903,9 +2908,9 @@ void VerilatedScope::varInsert(int finalize, const char* namep, void* datap, boo
|
||||
} else {
|
||||
// We could have a linked list of ranges, but really this whole thing needs
|
||||
// to be generalized to support structs and unions, etc.
|
||||
VL_FATAL_MT(
|
||||
__FILE__, __LINE__, "",
|
||||
(std::string("Unsupported multi-dimensional public varInsert: ") + namep).c_str());
|
||||
const std::string msg
|
||||
= std::string{"Unsupported multi-dimensional public varInsert: "} + namep;
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
@@ -2925,7 +2930,7 @@ VerilatedVar* VerilatedScope::varFind(const char* namep) const VL_MT_SAFE_POSTIN
|
||||
void* VerilatedScope::exportFindNullError(int funcnum) VL_MT_SAFE {
|
||||
// Slowpath - Called only when find has failed
|
||||
const std::string msg
|
||||
= (std::string("Testbench C called '") + VerilatedImp::exportName(funcnum)
|
||||
= (std::string{"Testbench C called '"} + VerilatedImp::exportName(funcnum)
|
||||
+ "' but scope wasn't set, perhaps due to dpi import call without "
|
||||
+ "'context', or missing svSetScope. See IEEE 1800-2017 35.5.3.");
|
||||
VL_FATAL_MT("unknown", 0, "", msg.c_str());
|
||||
@@ -2935,7 +2940,7 @@ void* VerilatedScope::exportFindNullError(int funcnum) VL_MT_SAFE {
|
||||
void* VerilatedScope::exportFindError(int funcnum) const {
|
||||
// Slowpath - Called only when find has failed
|
||||
const std::string msg
|
||||
= (std::string("Testbench C called '") + VerilatedImp::exportName(funcnum)
|
||||
= (std::string{"Testbench C called '"} + VerilatedImp::exportName(funcnum)
|
||||
+ "' but this DPI export function exists only in other scopes, not scope '" + name()
|
||||
+ "'");
|
||||
VL_FATAL_MT("unknown", 0, "", msg.c_str());
|
||||
@@ -2950,7 +2955,7 @@ void VerilatedScope::scopeDump() const {
|
||||
VerilatedImp::exportName(i));
|
||||
}
|
||||
}
|
||||
if (VerilatedVarNameMap* varsp = this->varsp()) {
|
||||
if (const VerilatedVarNameMap* const varsp = this->varsp()) {
|
||||
for (const auto& i : *varsp) VL_PRINTF_MT(" VAR %p: %s\n", &(i.second), i.first);
|
||||
}
|
||||
}
|
||||
|
||||
+17
-2139
File diff suppressed because it is too large
Load Diff
@@ -23,8 +23,6 @@ CFG_WITH_LONGTESTS = @CFG_WITH_LONGTESTS@
|
||||
CFG_CXXFLAGS_PROFILE = @CFG_CXXFLAGS_PROFILE@
|
||||
# Select newest language
|
||||
CFG_CXXFLAGS_STD_NEWEST = @CFG_CXXFLAGS_STD_NEWEST@
|
||||
# Select oldest language (for Verilator internal testing only)
|
||||
CFG_CXXFLAGS_STD_OLDEST = @CFG_CXXFLAGS_STD_OLDEST@
|
||||
# Compiler flags to use to turn off unused and generated code warnings, such as -Wno-div-by-zero
|
||||
CFG_CXXFLAGS_NO_UNUSED = @CFG_CXXFLAGS_NO_UNUSED@
|
||||
# Compiler flags that turn on extra warnings
|
||||
|
||||
+39
-35
@@ -132,7 +132,7 @@ private:
|
||||
int valueIndex(const std::string& value) VL_REQUIRES(m_mutex) {
|
||||
const auto iter = m_valueIndexes.find(value);
|
||||
if (iter != m_valueIndexes.end()) return iter->second;
|
||||
m_nextIndex++;
|
||||
++m_nextIndex;
|
||||
assert(m_nextIndex > 0); // Didn't rollover
|
||||
m_valueIndexes.emplace(value, m_nextIndex);
|
||||
m_indexValues.emplace(m_nextIndex, value);
|
||||
@@ -143,8 +143,9 @@ private:
|
||||
std::string rtn;
|
||||
for (const char* pos = text.c_str(); *pos; ++pos) {
|
||||
if (!std::isprint(*pos) || *pos == '%' || *pos == '"') {
|
||||
char hex[10];
|
||||
VL_SNPRINTF(hex, 10, "%%%02X", pos[0]);
|
||||
constexpr size_t LEN_MAX_HEX = 20;
|
||||
char hex[LEN_MAX_HEX];
|
||||
VL_SNPRINTF(hex, LEN_MAX_HEX, "%%%02X", pos[0]);
|
||||
rtn += hex;
|
||||
} else {
|
||||
rtn += *pos;
|
||||
@@ -165,11 +166,11 @@ private:
|
||||
const std::string& value) VL_PURE {
|
||||
std::string name;
|
||||
if (key.length() == 1 && std::isalpha(key[0])) {
|
||||
name += std::string("\001") + key;
|
||||
name += std::string{"\001"} + key;
|
||||
} else {
|
||||
name += std::string("\001") + dequote(key);
|
||||
name += std::string{"\001"} + dequote(key);
|
||||
}
|
||||
name += std::string("\002") + dequote(value);
|
||||
name += std::string{"\002"} + dequote(value);
|
||||
return name;
|
||||
}
|
||||
static std::string combineHier(const std::string& old, const std::string& add) VL_PURE {
|
||||
@@ -180,33 +181,34 @@ private:
|
||||
if (old.empty()) return add;
|
||||
if (add.empty()) return old;
|
||||
|
||||
const char* a = old.c_str();
|
||||
const char* b = add.c_str();
|
||||
const char* const a = old.c_str();
|
||||
const char* const b = add.c_str();
|
||||
|
||||
// Scan forward to first mismatch
|
||||
const char* apre = a;
|
||||
const char* bpre = b;
|
||||
while (*apre == *bpre) {
|
||||
apre++;
|
||||
bpre++;
|
||||
++apre;
|
||||
++bpre;
|
||||
}
|
||||
|
||||
// We used to backup and split on only .'s but it seems better to be verbose
|
||||
// and not assume . is the separator
|
||||
const std::string prefix = std::string(a, apre - a);
|
||||
const size_t prefix_len = apre - a;
|
||||
const std::string prefix = std::string{a, prefix_len};
|
||||
|
||||
// Scan backward to last mismatch
|
||||
const char* apost = a + std::strlen(a) - 1;
|
||||
const char* bpost = b + std::strlen(b) - 1;
|
||||
while (*apost == *bpost && apost > apre && bpost > bpre) {
|
||||
apost--;
|
||||
bpost--;
|
||||
--apost;
|
||||
--bpost;
|
||||
}
|
||||
|
||||
// Forward to . so we have a whole word
|
||||
std::string suffix = *bpost ? std::string(bpost + 1) : "";
|
||||
const std::string suffix = *bpost ? std::string{bpost + 1} : "";
|
||||
|
||||
std::string out = prefix + "*" + suffix;
|
||||
const std::string out = prefix + "*" + suffix;
|
||||
|
||||
// cout << "\nch pre="<<prefix<<" s="<<suffix<<"\nch a="<<old<<"\nch b="<<add
|
||||
// <<"\ncho="<<out<<endl;
|
||||
@@ -217,7 +219,7 @@ private:
|
||||
for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
|
||||
if (itemp->m_keys[i] != VerilatedCovConst::KEY_UNDEF) {
|
||||
// We don't compare keys, only values
|
||||
std::string val = m_indexValues[itemp->m_vals[i]];
|
||||
const std::string val = m_indexValues[itemp->m_vals[i]];
|
||||
if (std::string::npos != val.find(match)) { // Found
|
||||
return true;
|
||||
}
|
||||
@@ -253,17 +255,17 @@ public:
|
||||
// PUBLIC METHODS
|
||||
void forcePerInstance(const bool flag) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_forcePerInstance = flag;
|
||||
}
|
||||
void clear() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
clearGuts();
|
||||
}
|
||||
void clearNonMatch(const char* const matchp) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
if (matchp && matchp[0]) {
|
||||
ItemList newlist;
|
||||
for (const auto& itemp : m_items) {
|
||||
@@ -278,29 +280,29 @@ public:
|
||||
}
|
||||
void zero() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
for (const auto& itemp : m_items) itemp->zero();
|
||||
}
|
||||
|
||||
// We assume there's always call to i/f/p in that order
|
||||
void inserti(VerilatedCovImpItem* itemp) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
assert(!m_insertp);
|
||||
m_insertp = itemp;
|
||||
}
|
||||
void insertf(const char* const filenamep, const int lineno) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_insertFilenamep = filenamep;
|
||||
m_insertLineno = lineno;
|
||||
}
|
||||
void insertp(const char* ckeyps[VerilatedCovConst::MAX_KEYS],
|
||||
const char* valps[VerilatedCovConst::MAX_KEYS]) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
assert(m_insertp);
|
||||
// First two key/vals are filename
|
||||
ckeyps[0] = "filename";
|
||||
valps[0] = m_insertFilenamep;
|
||||
std::string linestr = vlCovCvtToStr(m_insertLineno);
|
||||
const std::string linestr = vlCovCvtToStr(m_insertLineno);
|
||||
ckeyps[1] = "lineno";
|
||||
valps[1] = linestr.c_str();
|
||||
// Default page if not specified
|
||||
@@ -308,7 +310,8 @@ public:
|
||||
while (const char* foundp = std::strchr(fnstartp, '/')) fnstartp = foundp + 1;
|
||||
const char* fnendp = fnstartp;
|
||||
for (; *fnendp && *fnendp != '.'; fnendp++) {}
|
||||
std::string page_default = "sp_user/" + std::string(fnstartp, fnendp - fnstartp);
|
||||
const size_t page_len = fnendp - fnstartp;
|
||||
const std::string page_default = "sp_user/" + std::string{fnstartp, page_len};
|
||||
ckeyps[2] = "page";
|
||||
valps[2] = page_default.c_str();
|
||||
|
||||
@@ -337,9 +340,9 @@ public:
|
||||
// cout<<" "<<__FUNCTION__<<" "<<key<<" = "<<val<<endl;
|
||||
m_insertp->m_keys[addKeynum] = valueIndex(key);
|
||||
m_insertp->m_vals[addKeynum] = valueIndex(val);
|
||||
addKeynum++;
|
||||
++addKeynum;
|
||||
if (VL_UNCOVERABLE(!legalKey(key))) {
|
||||
std::string msg
|
||||
const std::string msg
|
||||
= ("%Error: Coverage keys of one character, or letter+digit are illegal: "
|
||||
+ key); // LCOV_EXCL_LINE
|
||||
VL_FATAL_MT("", 0, "", msg.c_str());
|
||||
@@ -353,15 +356,15 @@ public:
|
||||
|
||||
void write(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
#ifndef VM_COVERAGE
|
||||
VL_FATAL_MT("", 0, "", "%Error: Called VerilatedCov::write when VM_COVERAGE disabled\n");
|
||||
#endif
|
||||
selftest();
|
||||
|
||||
std::ofstream os(filename);
|
||||
std::ofstream os{filename};
|
||||
if (os.fail()) {
|
||||
std::string msg = std::string("%Error: Can't write '") + filename + "'";
|
||||
const std::string msg = std::string{"%Error: Can't write '"} + filename + "'";
|
||||
VL_FATAL_MT("", 0, "", msg.c_str());
|
||||
return;
|
||||
}
|
||||
@@ -377,8 +380,9 @@ public:
|
||||
|
||||
for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
|
||||
if (itemp->m_keys[i] != VerilatedCovConst::KEY_UNDEF) {
|
||||
std::string key = VerilatedCovKey::shortKey(m_indexValues[itemp->m_keys[i]]);
|
||||
std::string val = m_indexValues[itemp->m_vals[i]];
|
||||
const std::string key
|
||||
= VerilatedCovKey::shortKey(m_indexValues[itemp->m_keys[i]]);
|
||||
const std::string val = m_indexValues[itemp->m_vals[i]];
|
||||
if (key == VL_CIK_PER_INSTANCE) {
|
||||
if (val != "0") per_instance = true;
|
||||
}
|
||||
@@ -435,10 +439,10 @@ void VerilatedCovContext::clearNonMatch(const char* matchp) VL_MT_SAFE {
|
||||
void VerilatedCovContext::zero() VL_MT_SAFE { impp()->zero(); }
|
||||
void VerilatedCovContext::write(const char* filenamep) VL_MT_SAFE { impp()->write(filenamep); }
|
||||
void VerilatedCovContext::_inserti(vluint32_t* itemp) VL_MT_SAFE {
|
||||
impp()->inserti(new VerilatedCoverItemSpec<vluint32_t>(itemp));
|
||||
impp()->inserti(new VerilatedCoverItemSpec<vluint32_t>{itemp});
|
||||
}
|
||||
void VerilatedCovContext::_inserti(vluint64_t* itemp) VL_MT_SAFE {
|
||||
impp()->inserti(new VerilatedCoverItemSpec<vluint64_t>(itemp));
|
||||
impp()->inserti(new VerilatedCoverItemSpec<vluint64_t>{itemp});
|
||||
}
|
||||
void VerilatedCovContext::_insertf(const char* filename, int lineno) VL_MT_SAFE {
|
||||
impp()->insertf(filename, lineno);
|
||||
@@ -511,7 +515,7 @@ VerilatedCovContext* VerilatedCov::threadCovp() VL_MT_SAFE {
|
||||
VerilatedCovContext* VerilatedContext::coveragep() VL_MT_SAFE {
|
||||
static VerilatedMutex s_mutex;
|
||||
if (VL_UNLIKELY(!m_coveragep)) {
|
||||
const VerilatedLockGuard lock(s_mutex);
|
||||
const VerilatedLockGuard lock{s_mutex};
|
||||
if (VL_LIKELY(!m_coveragep)) { // Not redundant, prevents race
|
||||
m_coveragep.reset(new VerilatedCovImp);
|
||||
}
|
||||
|
||||
+34
-34
@@ -68,7 +68,7 @@ svLogic svGetBitselLogic(const svLogicVecVal* sp, int bit) {
|
||||
| (((sp[VL_BITWORD_I(bit)].bval >> VL_BITBIT_I(bit)) & 1) << 1));
|
||||
}
|
||||
|
||||
void svPutBitselBit(svBitVecVal* dp, int bit, svBit s) { VL_ASSIGNBIT_WI(32, bit, dp, s); }
|
||||
void svPutBitselBit(svBitVecVal* dp, int bit, svBit s) { VL_ASSIGNBIT_WI(bit, dp, s); }
|
||||
void svPutBitselLogic(svLogicVecVal* dp, int bit, svLogic s) {
|
||||
// Verilator doesn't support X/Z so only aval
|
||||
dp[VL_BITWORD_I(bit)].aval = ((dp[VL_BITWORD_I(bit)].aval & ~(VL_UL(1) << VL_BITBIT_I(bit)))
|
||||
@@ -184,7 +184,7 @@ static inline const VerilatedDpiOpenVar* _vl_openhandle_varp(const svOpenArrayHa
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"%%Error: DPI svOpenArrayHandle function called with nullptr handle");
|
||||
}
|
||||
const VerilatedDpiOpenVar* varp = reinterpret_cast<const VerilatedDpiOpenVar*>(h);
|
||||
const VerilatedDpiOpenVar* const varp = reinterpret_cast<const VerilatedDpiOpenVar*>(h);
|
||||
if (VL_UNLIKELY(!varp->magicOk())) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"%%Error: DPI svOpenArrayHandle function called with non-Verilator handle");
|
||||
@@ -205,13 +205,13 @@ int svDimensions(const svOpenArrayHandle h) { return _vl_openhandle_varp(h)->udi
|
||||
|
||||
// Return pointer to open array data, or nullptr if not in IEEE standard C layout
|
||||
void* svGetArrayPtr(const svOpenArrayHandle h) {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h);
|
||||
if (VL_UNLIKELY(!varp->isDpiStdLayout())) return nullptr;
|
||||
return varp->datap();
|
||||
}
|
||||
// Return size of open array, or 0 if not in IEEE standard C layout
|
||||
int svSizeOfArray(const svOpenArrayHandle h) {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h);
|
||||
if (VL_UNLIKELY(!varp->isDpiStdLayout())) return 0;
|
||||
// Truncate 64 bits to int; DPI is limited to 4GB
|
||||
return static_cast<int>(varp->totalSize());
|
||||
@@ -264,15 +264,15 @@ static void* _vl_svGetArrElemPtr(const svOpenArrayHandle h, int nargs, int indx1
|
||||
int indx3) VL_MT_SAFE {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h);
|
||||
if (VL_UNLIKELY(!varp->isDpiStdLayout())) return nullptr;
|
||||
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
return datap;
|
||||
}
|
||||
|
||||
// Copy to user bit array from simulator open array
|
||||
static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, int nargs,
|
||||
int indx1, int indx2, int indx3) VL_MT_SAFE {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
|
||||
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
|
||||
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
if (VL_UNLIKELY(!datap)) return;
|
||||
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
|
||||
case VLVT_UINT8: d[0] = *(reinterpret_cast<CData*>(datap)); return;
|
||||
@@ -299,8 +299,8 @@ static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s,
|
||||
// Copy to user logic array from simulator open array
|
||||
static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, int nargs,
|
||||
int indx1, int indx2, int indx3) VL_MT_SAFE {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
|
||||
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
|
||||
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
if (VL_UNLIKELY(!datap)) return;
|
||||
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
|
||||
case VLVT_UINT8:
|
||||
@@ -342,8 +342,8 @@ static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandl
|
||||
// Copy to simulator open array from from user bit array
|
||||
static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, int nargs,
|
||||
int indx1, int indx2, int indx3) VL_MT_SAFE {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
|
||||
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
|
||||
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
if (VL_UNLIKELY(!datap)) return;
|
||||
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
|
||||
case VLVT_UINT8: *(reinterpret_cast<CData*>(datap)) = s[0]; return;
|
||||
@@ -364,8 +364,8 @@ static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecV
|
||||
// Copy to simulator open array from from user logic array
|
||||
static void _vl_svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s,
|
||||
int nargs, int indx1, int indx2, int indx3) VL_MT_SAFE {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
|
||||
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
|
||||
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
if (VL_UNLIKELY(!datap)) return;
|
||||
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
|
||||
case VLVT_UINT8: *(reinterpret_cast<CData*>(datap)) = s[0].aval; return;
|
||||
@@ -386,10 +386,10 @@ static void _vl_svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogic
|
||||
|
||||
// Return bit from simulator open array
|
||||
static svBit _vl_svGetBitArrElem(const svOpenArrayHandle s, int nargs, int indx1, int indx2,
|
||||
int indx3, int indx4) VL_MT_SAFE {
|
||||
int indx3, int) VL_MT_SAFE {
|
||||
// One extra index supported, as need bit number
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
|
||||
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
|
||||
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
if (VL_UNLIKELY(!datap)) return 0;
|
||||
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
|
||||
case VLVT_UINT8: return (*(reinterpret_cast<CData*>(datap))) & 1;
|
||||
@@ -401,11 +401,11 @@ static svBit _vl_svGetBitArrElem(const svOpenArrayHandle s, int nargs, int indx1
|
||||
}
|
||||
// Update simulator open array from bit
|
||||
static void _vl_svPutBitArrElem(const svOpenArrayHandle d, svBit value, int nargs, int indx1,
|
||||
int indx2, int indx3, int indx4) VL_MT_SAFE {
|
||||
int indx2, int indx3, int) VL_MT_SAFE {
|
||||
// One extra index supported, as need bit number
|
||||
value &= 1; // Make sure clean
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
|
||||
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
|
||||
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
if (VL_UNLIKELY(!datap)) return;
|
||||
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
|
||||
case VLVT_UINT8: *(reinterpret_cast<CData*>(datap)) = value; return;
|
||||
@@ -420,7 +420,7 @@ static void _vl_svPutBitArrElem(const svOpenArrayHandle d, svBit value, int narg
|
||||
// DPI accessors that simply call above functions
|
||||
|
||||
void* svGetArrElemPtr(const svOpenArrayHandle h, int indx1, ...) {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h);
|
||||
void* datap;
|
||||
va_list ap;
|
||||
va_start(ap, indx1);
|
||||
@@ -428,13 +428,13 @@ void* svGetArrElemPtr(const svOpenArrayHandle h, int indx1, ...) {
|
||||
switch (varp->udims()) {
|
||||
case 1: datap = _vl_svGetArrElemPtr(h, 1, indx1, 0, 0); break;
|
||||
case 2: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
const int indx2 = va_arg(ap, int);
|
||||
datap = _vl_svGetArrElemPtr(h, 2, indx1, indx2, 0);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
int indx3 = va_arg(ap, int);
|
||||
const int indx2 = va_arg(ap, int);
|
||||
const int indx3 = va_arg(ap, int);
|
||||
datap = _vl_svGetArrElemPtr(h, 3, indx1, indx2, indx3);
|
||||
break;
|
||||
}
|
||||
@@ -454,7 +454,7 @@ void* svGetArrElemPtr3(const svOpenArrayHandle h, int indx1, int indx2, int indx
|
||||
}
|
||||
|
||||
void svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, int indx1, ...) {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
|
||||
va_list ap;
|
||||
va_start(ap, indx1);
|
||||
switch (varp->udims()) {
|
||||
@@ -486,7 +486,7 @@ void svPutBitArrElem3VecVal(const svOpenArrayHandle d, const svBitVecVal* s, int
|
||||
_vl_svPutBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
|
||||
}
|
||||
void svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s, int indx1, ...) {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
|
||||
va_list ap;
|
||||
va_start(ap, indx1);
|
||||
switch (varp->udims()) {
|
||||
@@ -522,7 +522,7 @@ void svPutLogicArrElem3VecVal(const svOpenArrayHandle d, const svLogicVecVal* s,
|
||||
// From simulator storage into user space
|
||||
|
||||
void svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1, ...) {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
|
||||
va_list ap;
|
||||
va_start(ap, indx1);
|
||||
switch (varp->udims()) {
|
||||
@@ -553,7 +553,7 @@ void svGetBitArrElem3VecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1
|
||||
_vl_svGetBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
|
||||
}
|
||||
void svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, int indx1, ...) {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
|
||||
va_list ap;
|
||||
va_start(ap, indx1);
|
||||
switch (varp->udims()) {
|
||||
@@ -585,7 +585,7 @@ void svGetLogicArrElem3VecVal(svLogicVecVal* d, const svOpenArrayHandle s, int i
|
||||
}
|
||||
|
||||
svBit svGetBitArrElem(const svOpenArrayHandle s, int indx1, ...) {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
|
||||
svBit out;
|
||||
va_list ap;
|
||||
va_start(ap, indx1);
|
||||
@@ -618,7 +618,7 @@ svBit svGetBitArrElem3(const svOpenArrayHandle s, int indx1, int indx2, int indx
|
||||
}
|
||||
svLogic svGetLogicArrElem(const svOpenArrayHandle s, int indx1, ...) {
|
||||
// Verilator doesn't support X/Z so can just call Bit version
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
|
||||
svBit out;
|
||||
va_list ap;
|
||||
va_start(ap, indx1);
|
||||
@@ -654,7 +654,7 @@ svLogic svGetLogicArrElem3(const svOpenArrayHandle s, int indx1, int indx2, int
|
||||
}
|
||||
|
||||
void svPutBitArrElem(const svOpenArrayHandle d, svBit value, int indx1, ...) {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
|
||||
va_list ap;
|
||||
va_start(ap, indx1);
|
||||
switch (varp->udims()) {
|
||||
@@ -685,7 +685,7 @@ void svPutBitArrElem3(const svOpenArrayHandle d, svBit value, int indx1, int ind
|
||||
}
|
||||
void svPutLogicArrElem(const svOpenArrayHandle d, svLogic value, int indx1, ...) {
|
||||
// Verilator doesn't support X/Z so can just call Bit version
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
|
||||
va_list ap;
|
||||
va_start(ap, indx1);
|
||||
switch (varp->udims()) {
|
||||
@@ -732,15 +732,15 @@ svScope svGetScope() {
|
||||
}
|
||||
|
||||
svScope svSetScope(const svScope scope) {
|
||||
const VerilatedScope* prevScopep = Verilated::dpiScope();
|
||||
const VerilatedScope* vscopep = reinterpret_cast<const VerilatedScope*>(scope);
|
||||
const VerilatedScope* const prevScopep = Verilated::dpiScope();
|
||||
const VerilatedScope* const vscopep = reinterpret_cast<const VerilatedScope*>(scope);
|
||||
Verilated::dpiScope(vscopep);
|
||||
// NOLINTNEXTLINE(google-readability-casting)
|
||||
return (svScope)(prevScopep);
|
||||
}
|
||||
|
||||
const char* svGetNameFromScope(const svScope scope) {
|
||||
const VerilatedScope* vscopep = reinterpret_cast<const VerilatedScope*>(scope);
|
||||
const VerilatedScope* const vscopep = reinterpret_cast<const VerilatedScope*>(scope);
|
||||
return vscopep->name();
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h" // Also presumably included by caller
|
||||
#include "verilated_heavy.h" // Also presumably included by caller
|
||||
#include "verilated_sym_props.h"
|
||||
|
||||
#include "svdpi.h"
|
||||
|
||||
+22
-20
@@ -53,29 +53,31 @@
|
||||
|
||||
//=============================================================================
|
||||
// Check that vltscope_t matches fstScopeType
|
||||
static_assert((int)FST_ST_VCD_MODULE == (int)VLT_TRACE_SCOPE_MODULE,
|
||||
|
||||
static_assert(static_cast<int>(FST_ST_VCD_MODULE) == static_cast<int>(VLT_TRACE_SCOPE_MODULE),
|
||||
"VLT_TRACE_SCOPE_MODULE mismatches");
|
||||
static_assert((int)FST_ST_VCD_TASK == (int)VLT_TRACE_SCOPE_TASK,
|
||||
static_assert(static_cast<int>(FST_ST_VCD_TASK) == static_cast<int>(VLT_TRACE_SCOPE_TASK),
|
||||
"VLT_TRACE_SCOPE_TASK mismatches");
|
||||
static_assert((int)FST_ST_VCD_FUNCTION == (int)VLT_TRACE_SCOPE_FUNCTION,
|
||||
static_assert(static_cast<int>(FST_ST_VCD_FUNCTION) == static_cast<int>(VLT_TRACE_SCOPE_FUNCTION),
|
||||
"VLT_TRACE_SCOPE_FUNCTION mismatches");
|
||||
static_assert((int)FST_ST_VCD_BEGIN == (int)VLT_TRACE_SCOPE_BEGIN,
|
||||
static_assert(static_cast<int>(FST_ST_VCD_BEGIN) == static_cast<int>(VLT_TRACE_SCOPE_BEGIN),
|
||||
"VLT_TRACE_SCOPE_BEGIN mismatches");
|
||||
static_assert((int)FST_ST_VCD_FORK == (int)VLT_TRACE_SCOPE_FORK,
|
||||
static_assert(static_cast<int>(FST_ST_VCD_FORK) == static_cast<int>(VLT_TRACE_SCOPE_FORK),
|
||||
"VLT_TRACE_SCOPE_FORK mismatches");
|
||||
static_assert((int)FST_ST_VCD_GENERATE == (int)VLT_TRACE_SCOPE_GENERATE,
|
||||
static_assert(static_cast<int>(FST_ST_VCD_GENERATE) == static_cast<int>(VLT_TRACE_SCOPE_GENERATE),
|
||||
"VLT_TRACE_SCOPE_GENERATE mismatches");
|
||||
static_assert((int)FST_ST_VCD_STRUCT == (int)VLT_TRACE_SCOPE_STRUCT,
|
||||
static_assert(static_cast<int>(FST_ST_VCD_STRUCT) == static_cast<int>(VLT_TRACE_SCOPE_STRUCT),
|
||||
"VLT_TRACE_SCOPE_STRUCT mismatches");
|
||||
static_assert((int)FST_ST_VCD_UNION == (int)VLT_TRACE_SCOPE_UNION,
|
||||
static_assert(static_cast<int>(FST_ST_VCD_UNION) == static_cast<int>(VLT_TRACE_SCOPE_UNION),
|
||||
"VLT_TRACE_SCOPE_UNION mismatches");
|
||||
static_assert((int)FST_ST_VCD_CLASS == (int)VLT_TRACE_SCOPE_CLASS,
|
||||
static_assert(static_cast<int>(FST_ST_VCD_CLASS) == static_cast<int>(VLT_TRACE_SCOPE_CLASS),
|
||||
"VLT_TRACE_SCOPE_CLASS mismatches");
|
||||
static_assert((int)FST_ST_VCD_INTERFACE == (int)VLT_TRACE_SCOPE_INTERFACE,
|
||||
static_assert(static_cast<int>(FST_ST_VCD_INTERFACE)
|
||||
== static_cast<int>(VLT_TRACE_SCOPE_INTERFACE),
|
||||
"VLT_TRACE_SCOPE_INTERFACE mismatches");
|
||||
static_assert((int)FST_ST_VCD_PACKAGE == (int)VLT_TRACE_SCOPE_PACKAGE,
|
||||
static_assert(static_cast<int>(FST_ST_VCD_PACKAGE) == static_cast<int>(VLT_TRACE_SCOPE_PACKAGE),
|
||||
"VLT_TRACE_SCOPE_PACKAGE mismatches");
|
||||
static_assert((int)FST_ST_VCD_PROGRAM == (int)VLT_TRACE_SCOPE_PROGRAM,
|
||||
static_assert(static_cast<int>(FST_ST_VCD_PROGRAM) == static_cast<int>(VLT_TRACE_SCOPE_PROGRAM),
|
||||
"VLT_TRACE_SCOPE_PROGRAM mismatches");
|
||||
|
||||
//=============================================================================
|
||||
@@ -98,7 +100,7 @@ VerilatedFst::~VerilatedFst() {
|
||||
}
|
||||
|
||||
void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_fst = fstWriterCreate(filename, 1);
|
||||
fstWriterSetPackType(m_fst, FST_WR_PT_LZ4);
|
||||
fstWriterSetTimescaleFromString(m_fst, timeResStr().c_str()); // lintok-begin-on-ref
|
||||
@@ -130,14 +132,14 @@ void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
}
|
||||
|
||||
void VerilatedFst::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
VerilatedTrace<VerilatedFst>::closeBase();
|
||||
fstWriterClose(m_fst);
|
||||
m_fst = nullptr;
|
||||
}
|
||||
|
||||
void VerilatedFst::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
VerilatedTrace<VerilatedFst>::flushBase();
|
||||
fstWriterFlushContext(m_fst);
|
||||
}
|
||||
@@ -161,11 +163,11 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV
|
||||
|
||||
VerilatedTrace<VerilatedFst>::declCode(code, bits, false);
|
||||
|
||||
std::istringstream nameiss(name);
|
||||
std::istringstream nameiss{name};
|
||||
std::istream_iterator<std::string> beg(nameiss);
|
||||
std::istream_iterator<std::string> end;
|
||||
std::list<std::string> tokens(beg, end); // Split name
|
||||
std::string symbol_name(tokens.back());
|
||||
std::string symbol_name{tokens.back()};
|
||||
tokens.pop_back(); // Remove symbol name from hierarchy
|
||||
tokens.insert(tokens.begin(), moduleName()); // Add current module to the hierarchy
|
||||
std::string tmpModName;
|
||||
@@ -191,8 +193,8 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV
|
||||
tmpModName = *new_it;
|
||||
tmpModName.pop_back();
|
||||
// If the scope ends with a non-ascii character, it will be 0x80 + fstScopeType
|
||||
fstWriterSetScope(m_fst, (fstScopeType)(new_it->back() & 0x7f), tmpModName.c_str(),
|
||||
nullptr);
|
||||
fstWriterSetScope(m_fst, static_cast<fstScopeType>(new_it->back() & 0x7f),
|
||||
tmpModName.c_str(), nullptr);
|
||||
} else
|
||||
fstWriterSetScope(m_fst, FST_ST_VCD_SCOPE, new_it->c_str(), nullptr);
|
||||
|
||||
@@ -202,7 +204,7 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV
|
||||
|
||||
std::stringstream name_ss;
|
||||
name_ss << symbol_name;
|
||||
if (array) name_ss << "(" << arraynum << ")";
|
||||
if (array) name_ss << "[" << arraynum << "]";
|
||||
std::string name_str = name_ss.str();
|
||||
|
||||
if (dtypenum > 0) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+7
-956
@@ -12,969 +12,20 @@
|
||||
//*************************************************************************
|
||||
///
|
||||
/// \file
|
||||
/// \brief Verilated string and data-type header
|
||||
/// \brief Verilated old string and data-type header
|
||||
///
|
||||
/// This file is included automatically by Verilator at the top of
|
||||
/// all C++ files it generates. It is used when strings or other
|
||||
/// heavyweight types are required; these contents are not part of
|
||||
/// verilated.h to save compile time when such types aren't used.
|
||||
/// This file is deprecated, and provided for backwards compatibility.
|
||||
/// Include verilated.h instead.
|
||||
///
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_VERILATED_HEAVY_H_
|
||||
#define VERILATOR_VERILATED_HEAVY_H_
|
||||
|
||||
#ifdef VL_NO_LEGACY
|
||||
#error "Include <verilated.h> instead of <verilated_heavy.h>"
|
||||
#endif
|
||||
|
||||
#include "verilated.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
//===================================================================
|
||||
// String formatters (required by below containers)
|
||||
|
||||
extern std::string VL_TO_STRING(CData lhs);
|
||||
extern std::string VL_TO_STRING(SData lhs);
|
||||
extern std::string VL_TO_STRING(IData lhs);
|
||||
extern std::string VL_TO_STRING(QData lhs);
|
||||
inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; }
|
||||
extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
|
||||
|
||||
//===================================================================
|
||||
// Shuffle RNG
|
||||
|
||||
class VlURNG final {
|
||||
public:
|
||||
using result_type = size_t;
|
||||
static constexpr size_t min() { return 0; }
|
||||
static constexpr size_t max() { return 1ULL << 31; }
|
||||
size_t operator()() { return VL_MASK_I(31) & VL_RANDOM_I(32); }
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// Readmem/Writemem operation classes
|
||||
|
||||
class VlReadMem final {
|
||||
bool m_hex; // Hex format
|
||||
int m_bits; // Bit width of values
|
||||
const std::string& m_filename; // Filename
|
||||
QData m_end; // End address (as specified by user)
|
||||
FILE* m_fp; // File handle for filename
|
||||
QData m_addr; // Next address to read
|
||||
int m_linenum; // Line number last read from file
|
||||
public:
|
||||
VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end);
|
||||
~VlReadMem();
|
||||
bool isOpen() const { return m_fp != nullptr; }
|
||||
int linenum() const { return m_linenum; }
|
||||
bool get(QData& addrr, std::string& valuer);
|
||||
void setData(void* valuep, const std::string& rhs);
|
||||
};
|
||||
|
||||
class VlWriteMem final {
|
||||
bool m_hex; // Hex format
|
||||
int m_bits; // Bit width of values
|
||||
FILE* m_fp; // File handle for filename
|
||||
QData m_addr; // Next address to write
|
||||
public:
|
||||
VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end);
|
||||
~VlWriteMem();
|
||||
bool isOpen() const { return m_fp != nullptr; }
|
||||
void print(QData addr, bool addrstamp, const void* valuep);
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
/// Verilog wide packed bit container.
|
||||
/// Similar to std::array<WData, N>, but lighter weight, only methods needed
|
||||
/// by Verilator, to help compile time.
|
||||
///
|
||||
/// A 'struct' as we want this to be an aggregate type that allows
|
||||
/// static aggregate initialization. Consider data members private.
|
||||
///
|
||||
/// For example a Verilog "bit [94:0]" will become a VlWide<3> because 3*32
|
||||
/// bits are needed to hold the 95 bits. The MSB (bit 96) must always be
|
||||
/// zero in memory, but during intermediate operations in the Verilated
|
||||
/// internals is unpredictable.
|
||||
|
||||
template <std::size_t T_Words> struct VlWide final {
|
||||
// MEMBERS
|
||||
// This should be the only data member, otherwise generated static initializers need updating
|
||||
EData m_storage[T_Words]; // Contents of the packed array
|
||||
|
||||
// CONSTRUCTORS
|
||||
// cppcheck-suppress uninitVar
|
||||
VlWide() = default;
|
||||
~VlWide() = default;
|
||||
VlWide(const VlWide&) = default;
|
||||
VlWide(VlWide&&) = default;
|
||||
|
||||
// OPERATOR METHODS
|
||||
VlWide& operator=(const VlWide&) = default;
|
||||
VlWide& operator=(VlWide&&) = default;
|
||||
operator WDataOutP() { return &m_storage[0]; } // This also allows []
|
||||
operator WDataInP() const { return &m_storage[0]; } // This also allows []
|
||||
|
||||
// METHODS
|
||||
const EData& at(size_t index) const { return m_storage[index]; }
|
||||
EData& at(size_t index) { return m_storage[index]; }
|
||||
WData* data() { return &m_storage[0]; }
|
||||
const WData* data() const { return &m_storage[0]; }
|
||||
bool operator<(const VlWide<T_Words>& rhs) const {
|
||||
return VL_LT_W(T_Words, data(), rhs.data());
|
||||
}
|
||||
};
|
||||
|
||||
// Convert a C array to std::array reference by pointer magic, without copy.
|
||||
// Data type (second argument) is so the function template can automatically generate.
|
||||
template <std::size_t T_Words>
|
||||
VlWide<T_Words>& VL_CVT_W_A(const WDataInP inp, const VlWide<T_Words>&) {
|
||||
return *((VlWide<T_Words>*)inp);
|
||||
}
|
||||
|
||||
template <std::size_t T_Words> std::string VL_TO_STRING(const VlWide<T_Words>& obj) {
|
||||
return VL_TO_STRING_W(T_Words, obj.data());
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Verilog queue and dynamic array container
|
||||
// There are no multithreaded locks on this; the base variable must
|
||||
// be protected by other means
|
||||
//
|
||||
// Bound here is the maximum size() allowed, e.g. 1 + SystemVerilog bound
|
||||
// For dynamic arrays it is always zero
|
||||
template <class T_Value, size_t T_MaxSize = 0> class VlQueue final {
|
||||
private:
|
||||
// TYPES
|
||||
using Deque = std::deque<T_Value>;
|
||||
|
||||
public:
|
||||
using const_iterator = typename Deque::const_iterator;
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
Deque m_deque; // State of the assoc array
|
||||
T_Value m_defaultValue; // Default value
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
// m_defaultValue isn't defaulted. Caller's constructor must do it.
|
||||
VlQueue() = default;
|
||||
~VlQueue() = default;
|
||||
VlQueue(const VlQueue&) = default;
|
||||
VlQueue(VlQueue&&) = default;
|
||||
VlQueue& operator=(const VlQueue&) = default;
|
||||
VlQueue& operator=(VlQueue&&) = default;
|
||||
|
||||
// Standard copy constructor works. Verilog: assoca = assocb
|
||||
// Also must allow conversion from a different T_MaxSize queue
|
||||
template <size_t U_MaxSize = 0> VlQueue operator=(const VlQueue<T_Value, U_MaxSize>& rhs) {
|
||||
m_deque = rhs.privateDeque();
|
||||
if (VL_UNLIKELY(T_MaxSize && T_MaxSize < m_deque.size())) m_deque.resize(T_MaxSize - 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
static VlQueue cons(const T_Value& lhs) {
|
||||
VlQueue out;
|
||||
out.push_back(lhs);
|
||||
return out;
|
||||
}
|
||||
static VlQueue cons(const T_Value& lhs, const T_Value& rhs) {
|
||||
VlQueue out;
|
||||
out.push_back(rhs);
|
||||
out.push_back(lhs);
|
||||
return out;
|
||||
}
|
||||
static VlQueue cons(const VlQueue& lhs, const T_Value& rhs) {
|
||||
VlQueue out = lhs;
|
||||
out.push_front(rhs);
|
||||
return out;
|
||||
}
|
||||
static VlQueue cons(const T_Value& lhs, const VlQueue& rhs) {
|
||||
VlQueue out = rhs;
|
||||
out.push_back(lhs);
|
||||
return out;
|
||||
}
|
||||
static VlQueue cons(const VlQueue& lhs, const VlQueue& rhs) {
|
||||
VlQueue out = rhs;
|
||||
for (const auto& i : lhs.m_deque) out.push_back(i);
|
||||
return out;
|
||||
}
|
||||
|
||||
// METHODS
|
||||
T_Value& atDefault() { return m_defaultValue; }
|
||||
const T_Value& atDefault() const { return m_defaultValue; }
|
||||
const Deque& privateDeque() const { return m_deque; }
|
||||
|
||||
// Size. Verilog: function int size(), or int num()
|
||||
int size() const { return m_deque.size(); }
|
||||
// Clear array. Verilog: function void delete([input index])
|
||||
void clear() { m_deque.clear(); }
|
||||
void erase(vlsint32_t index) {
|
||||
if (VL_LIKELY(index >= 0 && index < m_deque.size()))
|
||||
m_deque.erase(m_deque.begin() + index);
|
||||
}
|
||||
|
||||
// Dynamic array new[] becomes a renew()
|
||||
void renew(size_t size) {
|
||||
clear();
|
||||
m_deque.resize(size, atDefault());
|
||||
}
|
||||
// Dynamic array new[]() becomes a renew_copy()
|
||||
void renew_copy(size_t size, const VlQueue<T_Value, T_MaxSize>& rhs) {
|
||||
if (size == 0) {
|
||||
clear();
|
||||
} else {
|
||||
*this = rhs;
|
||||
m_deque.resize(size, atDefault());
|
||||
}
|
||||
}
|
||||
|
||||
// function void q.push_front(value)
|
||||
void push_front(const T_Value& value) {
|
||||
m_deque.push_front(value);
|
||||
if (VL_UNLIKELY(T_MaxSize != 0 && m_deque.size() > T_MaxSize)) m_deque.pop_back();
|
||||
}
|
||||
// function void q.push_back(value)
|
||||
void push_back(const T_Value& value) {
|
||||
if (VL_LIKELY(T_MaxSize == 0 || m_deque.size() < T_MaxSize)) m_deque.push_back(value);
|
||||
}
|
||||
// function value_t q.pop_front();
|
||||
T_Value pop_front() {
|
||||
if (m_deque.empty()) return m_defaultValue;
|
||||
T_Value v = m_deque.front();
|
||||
m_deque.pop_front();
|
||||
return v;
|
||||
}
|
||||
// function value_t q.pop_back();
|
||||
T_Value pop_back() {
|
||||
if (m_deque.empty()) return m_defaultValue;
|
||||
T_Value v = m_deque.back();
|
||||
m_deque.pop_back();
|
||||
return v;
|
||||
}
|
||||
|
||||
// Setting. Verilog: assoc[index] = v
|
||||
// Can't just overload operator[] or provide a "at" reference to set,
|
||||
// because we need to be able to insert only when the value is set
|
||||
T_Value& at(vlsint32_t index) {
|
||||
static T_Value s_throwAway;
|
||||
// Needs to work for dynamic arrays, so does not use T_MaxSize
|
||||
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
|
||||
s_throwAway = atDefault();
|
||||
return s_throwAway;
|
||||
} else {
|
||||
return m_deque[index];
|
||||
}
|
||||
}
|
||||
// Accessing. Verilog: v = assoc[index]
|
||||
const T_Value& at(vlsint32_t index) const {
|
||||
static T_Value s_throwAway;
|
||||
// Needs to work for dynamic arrays, so does not use T_MaxSize
|
||||
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
|
||||
return atDefault();
|
||||
} else {
|
||||
return m_deque[index];
|
||||
}
|
||||
}
|
||||
// function void q.insert(index, value);
|
||||
void insert(vlsint32_t index, const T_Value& value) {
|
||||
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) return;
|
||||
m_deque.insert(m_deque.begin() + index, value);
|
||||
}
|
||||
|
||||
// Return slice q[lsb:msb]
|
||||
VlQueue slice(vlsint32_t lsb, vlsint32_t msb) const {
|
||||
VlQueue out;
|
||||
if (VL_UNLIKELY(lsb < 0)) lsb = 0;
|
||||
if (VL_UNLIKELY(lsb >= m_deque.size())) lsb = m_deque.size() - 1;
|
||||
if (VL_UNLIKELY(msb >= m_deque.size())) msb = m_deque.size() - 1;
|
||||
for (vlsint32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]);
|
||||
return out;
|
||||
}
|
||||
|
||||
// For save/restore
|
||||
const_iterator begin() const { return m_deque.begin(); }
|
||||
const_iterator end() const { return m_deque.end(); }
|
||||
|
||||
// Methods
|
||||
void sort() { std::sort(m_deque.begin(), m_deque.end()); }
|
||||
template <typename Func> void sort(Func with_func) {
|
||||
// with_func returns arbitrary type to use for the sort comparison
|
||||
std::sort(m_deque.begin(), m_deque.end(), [=](const T_Value& a, const T_Value& b) {
|
||||
// index number is meaninless with sort, as it changes
|
||||
return with_func(0, a) < with_func(0, b);
|
||||
});
|
||||
}
|
||||
void rsort() { std::sort(m_deque.rbegin(), m_deque.rend()); }
|
||||
template <typename Func> void rsort(Func with_func) {
|
||||
// with_func returns arbitrary type to use for the sort comparison
|
||||
std::sort(m_deque.rbegin(), m_deque.rend(), [=](const T_Value& a, const T_Value& b) {
|
||||
// index number is meaninless with sort, as it changes
|
||||
return with_func(0, a) < with_func(0, b);
|
||||
});
|
||||
}
|
||||
void reverse() { std::reverse(m_deque.begin(), m_deque.end()); }
|
||||
void shuffle() { std::shuffle(m_deque.begin(), m_deque.end(), VlURNG()); }
|
||||
VlQueue unique() const {
|
||||
VlQueue out;
|
||||
std::unordered_set<T_Value> saw;
|
||||
for (const auto& i : m_deque) {
|
||||
auto it = saw.find(i);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i);
|
||||
out.push_back(i);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
VlQueue<IData> unique_index() const {
|
||||
VlQueue<IData> out;
|
||||
IData index = 0;
|
||||
std::unordered_set<T_Value> saw;
|
||||
for (const auto& i : m_deque) {
|
||||
auto it = saw.find(i);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i);
|
||||
out.push_back(index);
|
||||
}
|
||||
++index;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue find(Func with_func) const {
|
||||
VlQueue out;
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
if (with_func(index, i)) out.push_back(i);
|
||||
++index;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue<IData> find_index(Func with_func) const {
|
||||
VlQueue<IData> out;
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
if (with_func(index, i)) out.push_back(index);
|
||||
++index;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue find_first(Func with_func) const {
|
||||
// Can't use std::find_if as need index number
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
if (with_func(index, i)) return VlQueue::cons(i);
|
||||
++index;
|
||||
}
|
||||
return VlQueue{};
|
||||
}
|
||||
template <typename Func> VlQueue<IData> find_first_index(Func with_func) const {
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
if (with_func(index, i)) return VlQueue<IData>::cons(index);
|
||||
++index;
|
||||
}
|
||||
return VlQueue<IData>{};
|
||||
}
|
||||
template <typename Func> VlQueue find_last(Func with_func) const {
|
||||
IData index = m_deque.size() - 1;
|
||||
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
|
||||
if (with_func(index, *it)) return VlQueue::cons(*it);
|
||||
--index;
|
||||
}
|
||||
return VlQueue{};
|
||||
}
|
||||
template <typename Func> VlQueue<IData> find_last_index(Func with_func) const {
|
||||
IData index = m_deque.size() - 1;
|
||||
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
|
||||
if (with_func(index, *it)) return VlQueue<IData>::cons(index);
|
||||
--index;
|
||||
}
|
||||
return VlQueue<IData>{};
|
||||
}
|
||||
|
||||
// Reduction operators
|
||||
VlQueue min() const {
|
||||
if (m_deque.empty()) return VlQueue();
|
||||
const auto it = std::min_element(m_deque.begin(), m_deque.end());
|
||||
return VlQueue::cons(*it);
|
||||
}
|
||||
VlQueue max() const {
|
||||
if (m_deque.empty()) return VlQueue();
|
||||
const auto it = std::max_element(m_deque.begin(), m_deque.end());
|
||||
return VlQueue::cons(*it);
|
||||
}
|
||||
|
||||
T_Value r_sum() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_deque) out += i;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_sum(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) out += with_func(index++, i);
|
||||
return out;
|
||||
}
|
||||
T_Value r_product() const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
T_Value out{*it};
|
||||
++it;
|
||||
for (; it != m_deque.end(); ++it) out *= *it;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_product(Func with_func) const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
IData index = 0;
|
||||
T_Value out{with_func(index, *it)};
|
||||
++it;
|
||||
++index;
|
||||
for (; it != m_deque.end(); ++it) out *= with_func(index++, *it);
|
||||
return out;
|
||||
}
|
||||
T_Value r_and() const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
T_Value out{*it};
|
||||
++it;
|
||||
for (; it != m_deque.end(); ++it) out &= *it;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_and(Func with_func) const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
IData index = 0;
|
||||
T_Value out{with_func(index, *it)};
|
||||
++it;
|
||||
++index;
|
||||
for (; it != m_deque.end(); ++it) out &= with_func(index, *it);
|
||||
return out;
|
||||
}
|
||||
T_Value r_or() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_deque) out |= i;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_or(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) out |= with_func(index++, i);
|
||||
return out;
|
||||
}
|
||||
T_Value r_xor() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_deque) out ^= i;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_xor(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) out ^= with_func(index++, i);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Dumping. Verilog: str = $sformatf("%p", assoc)
|
||||
std::string to_string() const {
|
||||
if (m_deque.empty()) return "'{}"; // No trailing space
|
||||
std::string out = "'{";
|
||||
std::string comma;
|
||||
for (const auto& i : m_deque) {
|
||||
out += comma + VL_TO_STRING(i);
|
||||
comma = ", ";
|
||||
}
|
||||
return out + "} ";
|
||||
}
|
||||
};
|
||||
|
||||
template <class T_Value> std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
|
||||
return obj.to_string();
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Verilog associative array container
|
||||
// There are no multithreaded locks on this; the base variable must
|
||||
// be protected by other means
|
||||
//
|
||||
template <class T_Key, class T_Value> class VlAssocArray final {
|
||||
private:
|
||||
// TYPES
|
||||
using Map = std::map<T_Key, T_Value>;
|
||||
|
||||
public:
|
||||
using const_iterator = typename Map::const_iterator;
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
Map m_map; // State of the assoc array
|
||||
T_Value m_defaultValue; // Default value
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
// m_defaultValue isn't defaulted. Caller's constructor must do it.
|
||||
VlAssocArray() = default;
|
||||
~VlAssocArray() = default;
|
||||
VlAssocArray(const VlAssocArray&) = default;
|
||||
VlAssocArray(VlAssocArray&&) = default;
|
||||
VlAssocArray& operator=(const VlAssocArray&) = default;
|
||||
VlAssocArray& operator=(VlAssocArray&&) = default;
|
||||
|
||||
// METHODS
|
||||
T_Value& atDefault() { return m_defaultValue; }
|
||||
const T_Value& atDefault() const { return m_defaultValue; }
|
||||
|
||||
// Size of array. Verilog: function int size(), or int num()
|
||||
int size() const { return m_map.size(); }
|
||||
// Clear array. Verilog: function void delete([input index])
|
||||
void clear() { m_map.clear(); }
|
||||
void erase(const T_Key& index) { m_map.erase(index); }
|
||||
// Return 0/1 if element exists. Verilog: function int exists(input index)
|
||||
int exists(const T_Key& index) const { return m_map.find(index) != m_map.end(); }
|
||||
// Return first element. Verilog: function int first(ref index);
|
||||
int first(T_Key& indexr) const {
|
||||
const auto it = m_map.cbegin();
|
||||
if (it == m_map.end()) return 0;
|
||||
indexr = it->first;
|
||||
return 1;
|
||||
}
|
||||
// Return last element. Verilog: function int last(ref index)
|
||||
int last(T_Key& indexr) const {
|
||||
const auto it = m_map.crbegin();
|
||||
if (it == m_map.rend()) return 0;
|
||||
indexr = it->first;
|
||||
return 1;
|
||||
}
|
||||
// Return next element. Verilog: function int next(ref index)
|
||||
int next(T_Key& indexr) const {
|
||||
auto it = m_map.find(indexr);
|
||||
if (VL_UNLIKELY(it == m_map.end())) return 0;
|
||||
++it;
|
||||
if (VL_UNLIKELY(it == m_map.end())) return 0;
|
||||
indexr = it->first;
|
||||
return 1;
|
||||
}
|
||||
// Return prev element. Verilog: function int prev(ref index)
|
||||
int prev(T_Key& indexr) const {
|
||||
auto it = m_map.find(indexr);
|
||||
if (VL_UNLIKELY(it == m_map.end())) return 0;
|
||||
if (VL_UNLIKELY(it == m_map.begin())) return 0;
|
||||
--it;
|
||||
indexr = it->first;
|
||||
return 1;
|
||||
}
|
||||
// Setting. Verilog: assoc[index] = v
|
||||
// Can't just overload operator[] or provide a "at" reference to set,
|
||||
// because we need to be able to insert only when the value is set
|
||||
T_Value& at(const T_Key& index) {
|
||||
const auto it = m_map.find(index);
|
||||
if (it == m_map.end()) {
|
||||
std::pair<typename Map::iterator, bool> pit = m_map.emplace(index, m_defaultValue);
|
||||
return pit.first->second;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
// Accessing. Verilog: v = assoc[index]
|
||||
const T_Value& at(const T_Key& index) const {
|
||||
const auto it = m_map.find(index);
|
||||
if (it == m_map.end()) {
|
||||
return m_defaultValue;
|
||||
} else {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
// Setting as a chained operation
|
||||
VlAssocArray& set(const T_Key& index, const T_Value& value) {
|
||||
at(index) = value;
|
||||
return *this;
|
||||
}
|
||||
VlAssocArray& setDefault(const T_Value& value) {
|
||||
atDefault() = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// For save/restore
|
||||
const_iterator begin() const { return m_map.begin(); }
|
||||
const_iterator end() const { return m_map.end(); }
|
||||
|
||||
// Methods
|
||||
VlQueue<T_Value> unique() const {
|
||||
VlQueue<T_Value> out;
|
||||
std::set<T_Value> saw;
|
||||
for (const auto& i : m_map) {
|
||||
auto it = saw.find(i.second);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i.second);
|
||||
out.push_back(i.second);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
VlQueue<T_Key> unique_index() const {
|
||||
VlQueue<T_Key> out;
|
||||
std::set<T_Key> saw;
|
||||
for (const auto& i : m_map) {
|
||||
auto it = saw.find(i.second);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i.second);
|
||||
out.push_back(i.first);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue<T_Value> find(Func with_func) const {
|
||||
VlQueue<T_Value> out;
|
||||
for (const auto& i : m_map)
|
||||
if (with_func(i.first, i.second)) out.push_back(i.second);
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue<T_Key> find_index(Func with_func) const {
|
||||
VlQueue<T_Key> out;
|
||||
for (const auto& i : m_map)
|
||||
if (with_func(i.first, i.second)) out.push_back(i.first);
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue<T_Value> find_first(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
if (it == m_map.end()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
template <typename Func> VlQueue<T_Key> find_first_index(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
if (it == m_map.end()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Key>::cons(it->first);
|
||||
}
|
||||
template <typename Func> VlQueue<T_Value> find_last(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
if (it == m_map.rend()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
template <typename Func> VlQueue<T_Key> find_last_index(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
if (it == m_map.rend()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Key>::cons(it->first);
|
||||
}
|
||||
|
||||
// Reduction operators
|
||||
VlQueue<T_Value> min() const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
const auto it = std::min_element(
|
||||
m_map.begin(), m_map.end(),
|
||||
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
|
||||
return a.second < b.second;
|
||||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
VlQueue<T_Value> max() const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
const auto it = std::max_element(
|
||||
m_map.begin(), m_map.end(),
|
||||
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
|
||||
return a.second < b.second;
|
||||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
|
||||
T_Value r_sum() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out += i.second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_sum(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out += with_func(i.first, i.second);
|
||||
return out;
|
||||
}
|
||||
T_Value r_product() const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
T_Value out{it->second};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out *= it->second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_product(Func with_func) const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
T_Value out{with_func(it->first, it->second)};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out *= with_func(it->first, it->second);
|
||||
return out;
|
||||
}
|
||||
T_Value r_and() const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
T_Value out{it->second};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out &= it->second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_and(Func with_func) const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
T_Value out{with_func(it->first, it->second)};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out &= with_func(it->first, it->second);
|
||||
return out;
|
||||
}
|
||||
T_Value r_or() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out |= i.second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_or(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out |= with_func(i.first, i.second);
|
||||
return out;
|
||||
}
|
||||
T_Value r_xor() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out ^= i.second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_xor(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out ^= with_func(i.first, i.second);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Dumping. Verilog: str = $sformatf("%p", assoc)
|
||||
std::string to_string() const {
|
||||
if (m_map.empty()) return "'{}"; // No trailing space
|
||||
std::string out = "'{";
|
||||
std::string comma;
|
||||
for (const auto& i : m_map) {
|
||||
out += comma + VL_TO_STRING(i.first) + ":" + VL_TO_STRING(i.second);
|
||||
comma = ", ";
|
||||
}
|
||||
// Default not printed - maybe random init data
|
||||
return out + "} ";
|
||||
}
|
||||
};
|
||||
|
||||
template <class T_Key, class T_Value>
|
||||
std::string VL_TO_STRING(const VlAssocArray<T_Key, T_Value>& obj) {
|
||||
return obj.to_string();
|
||||
}
|
||||
|
||||
template <class T_Key, class T_Value>
|
||||
void VL_READMEM_N(bool hex, int bits, const std::string& filename,
|
||||
VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
|
||||
VlReadMem rmem(hex, bits, filename, start, end);
|
||||
if (VL_UNLIKELY(!rmem.isOpen())) return;
|
||||
while (true) {
|
||||
QData addr;
|
||||
std::string data;
|
||||
if (rmem.get(addr /*ref*/, data /*ref*/)) {
|
||||
rmem.setData(&(obj.at(addr)), data);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class T_Key, class T_Value>
|
||||
void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
|
||||
const VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
|
||||
VlWriteMem wmem(hex, bits, filename, start, end);
|
||||
if (VL_UNLIKELY(!wmem.isOpen())) return;
|
||||
for (const auto& i : obj) {
|
||||
const QData addr = i.first;
|
||||
if (addr >= start && addr <= end) wmem.print(addr, true, &(i.second));
|
||||
}
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
/// Verilog unpacked array container
|
||||
/// For when a standard C++[] array is not sufficient, e.g. an
|
||||
/// array under a queue, or methods operating on the array.
|
||||
///
|
||||
/// A 'struct' as we want this to be an aggregate type that allows
|
||||
/// static aggregate initialization. Consider data members private.
|
||||
///
|
||||
/// This class may get exposed to a Verilated Model's top I/O, if the top
|
||||
/// IO has an unpacked array.
|
||||
|
||||
template <class T_Value, std::size_t T_Depth> struct VlUnpacked final {
|
||||
// MEMBERS
|
||||
// This should be the only data member, otherwise generated static initializers need updating
|
||||
T_Value m_storage[T_Depth]; // Contents of the unpacked array
|
||||
|
||||
// CONSTRUCTORS
|
||||
VlUnpacked() = default;
|
||||
~VlUnpacked() = default;
|
||||
VlUnpacked(const VlUnpacked&) = default;
|
||||
VlUnpacked(VlUnpacked&&) = default;
|
||||
VlUnpacked& operator=(const VlUnpacked&) = default;
|
||||
VlUnpacked& operator=(VlUnpacked&&) = default;
|
||||
|
||||
// METHODS
|
||||
// Raw access
|
||||
WData* data() { return &m_storage[0]; }
|
||||
const WData* data() const { return &m_storage[0]; }
|
||||
|
||||
T_Value& operator[](size_t index) { return m_storage[index]; };
|
||||
const T_Value& operator[](size_t index) const { return m_storage[index]; };
|
||||
|
||||
// Dumping. Verilog: str = $sformatf("%p", assoc)
|
||||
std::string to_string() const {
|
||||
std::string out = "'{";
|
||||
std::string comma;
|
||||
for (int i = 0; i < T_Depth; ++i) {
|
||||
out += comma + VL_TO_STRING(m_storage[i]);
|
||||
comma = ", ";
|
||||
}
|
||||
return out + "} ";
|
||||
}
|
||||
};
|
||||
|
||||
template <class T_Value, std::size_t T_Depth>
|
||||
std::string VL_TO_STRING(const VlUnpacked<T_Value, T_Depth>& obj) {
|
||||
return obj.to_string();
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Verilog class reference container
|
||||
// There are no multithreaded locks on this; the base variable must
|
||||
// be protected by other means
|
||||
|
||||
#define VlClassRef std::shared_ptr
|
||||
|
||||
template <class T> // T typically of type VlClassRef<x>
|
||||
inline T VL_NULL_CHECK(T t, const char* filename, int linenum) {
|
||||
if (VL_UNLIKELY(!t)) Verilated::nullPointerError(filename, linenum);
|
||||
return t;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
static inline bool VL_CAST_DYNAMIC(VlClassRef<T> in, VlClassRef<U>& outr) {
|
||||
VlClassRef<U> casted = std::dynamic_pointer_cast<U>(in);
|
||||
if (VL_LIKELY(casted)) {
|
||||
outr = casted;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Conversion functions
|
||||
|
||||
extern std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_MT_SAFE;
|
||||
inline std::string VL_CVT_PACK_STR_NQ(QData lhs) VL_PURE {
|
||||
VlWide<VL_WQ_WORDS_E> lw;
|
||||
VL_SET_WQ(lw, lhs);
|
||||
return VL_CVT_PACK_STR_NW(VL_WQ_WORDS_E, lw);
|
||||
}
|
||||
inline std::string VL_CVT_PACK_STR_NN(const std::string& lhs) VL_PURE { return lhs; }
|
||||
inline std::string& VL_CVT_PACK_STR_NN(std::string& lhs) VL_PURE { return lhs; }
|
||||
inline std::string VL_CVT_PACK_STR_NI(IData lhs) VL_PURE {
|
||||
VlWide<VL_WQ_WORDS_E> lw;
|
||||
VL_SET_WI(lw, lhs);
|
||||
return VL_CVT_PACK_STR_NW(1, lw);
|
||||
}
|
||||
inline std::string VL_CONCATN_NNN(const std::string& lhs, const std::string& rhs) VL_PURE {
|
||||
return lhs + rhs;
|
||||
}
|
||||
inline std::string VL_REPLICATEN_NNQ(int, int, int, const std::string& lhs, IData rep) VL_PURE {
|
||||
std::string out;
|
||||
out.reserve(lhs.length() * rep);
|
||||
for (unsigned times = 0; times < rep; ++times) out += lhs;
|
||||
return out;
|
||||
}
|
||||
inline std::string VL_REPLICATEN_NNI(int obits, int lbits, int rbits, const std::string& lhs,
|
||||
IData rep) VL_PURE {
|
||||
return VL_REPLICATEN_NNQ(obits, lbits, rbits, lhs, rep);
|
||||
}
|
||||
|
||||
inline IData VL_LEN_IN(const std::string& ld) { return ld.length(); }
|
||||
extern std::string VL_TOLOWER_NN(const std::string& ld);
|
||||
extern std::string VL_TOUPPER_NN(const std::string& ld);
|
||||
|
||||
extern IData VL_FERROR_IN(IData fpi, std::string& outputr) VL_MT_SAFE;
|
||||
extern IData VL_FOPEN_NN(const std::string& filename, const std::string& mode) VL_MT_SAFE;
|
||||
extern IData VL_FOPEN_MCD_N(const std::string& filename) VL_MT_SAFE;
|
||||
extern void VL_READMEM_N(bool hex, int bits, QData depth, int array_lsb,
|
||||
const std::string& filename, void* memp, QData start,
|
||||
QData end) VL_MT_SAFE;
|
||||
extern void VL_WRITEMEM_N(bool hex, int bits, QData depth, int array_lsb,
|
||||
const std::string& filename, const void* memp, QData start,
|
||||
QData 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 void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suffix, int width,
|
||||
VerilatedContext* contextp) 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, CData& rdr) VL_MT_SAFE {
|
||||
VlWide<2> rwp; // WData must always be at least 2
|
||||
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
|
||||
if (got) rdr = rwp[0];
|
||||
return got;
|
||||
}
|
||||
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, SData& rdr) VL_MT_SAFE {
|
||||
VlWide<2> rwp; // WData must always be at least 2
|
||||
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
|
||||
if (got) rdr = rwp[0];
|
||||
return got;
|
||||
}
|
||||
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, IData& rdr) VL_MT_SAFE {
|
||||
VlWide<2> rwp;
|
||||
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
|
||||
if (got) rdr = rwp[0];
|
||||
return got;
|
||||
}
|
||||
inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, QData& rdr) VL_MT_SAFE {
|
||||
VlWide<2> rwp;
|
||||
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
|
||||
if (got) rdr = VL_SET_QW(rwp);
|
||||
return got;
|
||||
}
|
||||
inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, double& rdr) VL_MT_SAFE {
|
||||
VlWide<2> rwp;
|
||||
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
|
||||
if (got) rdr = VL_CVT_D_Q(VL_SET_QW(rwp));
|
||||
return got;
|
||||
}
|
||||
extern IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_SAFE;
|
||||
|
||||
//======================================================================
|
||||
// Strings
|
||||
|
||||
extern std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE;
|
||||
extern CData VL_GETC_N(const std::string& lhs, IData rhs) VL_PURE;
|
||||
extern std::string VL_SUBSTR_N(const std::string& lhs, IData rhs, IData ths) VL_PURE;
|
||||
|
||||
inline IData VL_CMP_NN(const std::string& lhs, const std::string& rhs, bool ignoreCase) VL_PURE {
|
||||
// SystemVerilog does not allow a string variable to contain '\0'.
|
||||
// So C functions such as strcmp() can correctly compare strings.
|
||||
if (ignoreCase) {
|
||||
return VL_STRCASECMP(lhs.c_str(), rhs.c_str());
|
||||
} else {
|
||||
return std::strcmp(lhs.c_str(), rhs.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
extern IData VL_ATOI_N(const std::string& str, int base) VL_PURE;
|
||||
|
||||
extern IData VL_FGETS_NI(std::string& dest, IData fpi);
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+29
-30
@@ -30,7 +30,6 @@
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h"
|
||||
#include "verilated_heavy.h"
|
||||
#include "verilated_syms.h"
|
||||
|
||||
#include <deque>
|
||||
@@ -104,7 +103,7 @@ public:
|
||||
// METHODS
|
||||
// Add message to queue (called by producer)
|
||||
void post(const VerilatedMsg& msg) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_queue.insert(msg); // Pass by value to copy the message into queue
|
||||
++m_depth;
|
||||
}
|
||||
@@ -124,7 +123,7 @@ public:
|
||||
const VerilatedMsg msg = *(it);
|
||||
m_queue.erase(it);
|
||||
m_mutex.unlock();
|
||||
m_depth--; // Ok if outside critical section as only this code checks the value
|
||||
--m_depth; // Ok if outside critical section as only this code checks the value
|
||||
{
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("Executing callback from mtaskId=%d\n", msg.mtaskId()););
|
||||
msg.run();
|
||||
@@ -179,7 +178,7 @@ public:
|
||||
|
||||
// FILE* list constructed from a file-descriptor
|
||||
class VerilatedFpList final {
|
||||
FILE* m_fp[31];
|
||||
FILE* m_fp[31] = {};
|
||||
std::size_t m_sz = 0;
|
||||
|
||||
public:
|
||||
@@ -256,11 +255,11 @@ public: // But only for verilated*.cpp
|
||||
int timeFormatWidth() const VL_MT_SAFE { return m_s.m_timeFormatWidth; }
|
||||
void timeFormatWidth(int value) VL_MT_SAFE { m_s.m_timeFormatWidth = value; }
|
||||
std::string timeFormatSuffix() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
|
||||
const VerilatedLockGuard lock(m_timeDumpMutex);
|
||||
const VerilatedLockGuard lock{m_timeDumpMutex};
|
||||
return m_timeFormatSuffix;
|
||||
}
|
||||
void timeFormatSuffix(const std::string& value) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
|
||||
const VerilatedLockGuard lock(m_timeDumpMutex);
|
||||
const VerilatedLockGuard lock{m_timeDumpMutex};
|
||||
m_timeFormatSuffix = value;
|
||||
}
|
||||
|
||||
@@ -276,19 +275,19 @@ public: // But only for verilated*.cpp
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - file IO
|
||||
IData fdNewMcd(const char* filenamep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock(m_fdMutex);
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
if (m_fdFreeMct.empty()) return 0;
|
||||
IData idx = m_fdFreeMct.back();
|
||||
const IData idx = m_fdFreeMct.back();
|
||||
m_fdFreeMct.pop_back();
|
||||
m_fdps[idx] = std::fopen(filenamep, "w");
|
||||
if (VL_UNLIKELY(!m_fdps[idx])) return 0;
|
||||
return (1 << idx);
|
||||
}
|
||||
IData fdNew(const char* filenamep, const char* modep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
FILE* fp = std::fopen(filenamep, modep);
|
||||
FILE* const fp = std::fopen(filenamep, modep);
|
||||
if (VL_UNLIKELY(!fp)) return 0;
|
||||
// Bit 31 indicates it's a descriptor not a MCD
|
||||
const VerilatedLockGuard lock(m_fdMutex);
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
if (m_fdFree.empty()) {
|
||||
// Need to create more space in m_fdps and m_fdFree
|
||||
const std::size_t start = std::max<std::size_t>(31UL + 1UL + 3UL, m_fdps.size());
|
||||
@@ -300,31 +299,31 @@ public: // But only for verilated*.cpp
|
||||
m_fdFree[i] = id;
|
||||
}
|
||||
}
|
||||
IData idx = m_fdFree.back();
|
||||
const IData idx = m_fdFree.back();
|
||||
m_fdFree.pop_back();
|
||||
m_fdps[idx] = fp;
|
||||
return (idx | (1UL << 31)); // bit 31 indicates not MCD
|
||||
}
|
||||
void fdFlush(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock(m_fdMutex);
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
const VerilatedFpList fdlist = fdToFpList(fdi);
|
||||
for (const auto& i : fdlist) std::fflush(i);
|
||||
}
|
||||
IData fdSeek(IData fdi, IData offset, IData origin) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock(m_fdMutex);
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
const VerilatedFpList fdlist = fdToFpList(fdi);
|
||||
if (VL_UNLIKELY(fdlist.size() != 1)) return 0;
|
||||
return static_cast<IData>(
|
||||
std::fseek(*fdlist.begin(), static_cast<long>(offset), static_cast<int>(origin)));
|
||||
}
|
||||
IData fdTell(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock(m_fdMutex);
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
const VerilatedFpList fdlist = fdToFpList(fdi);
|
||||
if (VL_UNLIKELY(fdlist.size() != 1)) return 0;
|
||||
return static_cast<IData>(std::ftell(*fdlist.begin()));
|
||||
}
|
||||
void fdWrite(IData fdi, const std::string& output) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock(m_fdMutex);
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
const VerilatedFpList fdlist = fdToFpList(fdi);
|
||||
for (const auto& i : fdlist) {
|
||||
if (VL_UNLIKELY(!i)) continue;
|
||||
@@ -332,10 +331,10 @@ public: // But only for verilated*.cpp
|
||||
}
|
||||
}
|
||||
void fdClose(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock(m_fdMutex);
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
if (VL_BITISSET_I(fdi, 31)) {
|
||||
// Non-MCD case
|
||||
IData idx = VL_MASK_I(31) & fdi;
|
||||
const IData idx = VL_MASK_I(31) & fdi;
|
||||
if (VL_UNLIKELY(idx >= m_fdps.size())) return;
|
||||
if (VL_UNLIKELY(idx <= 2)) return; // stdout/stdin/stderr
|
||||
if (VL_UNLIKELY(!m_fdps[idx])) return; // Already free
|
||||
@@ -356,7 +355,7 @@ public: // But only for verilated*.cpp
|
||||
}
|
||||
}
|
||||
inline FILE* fdToFp(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock(m_fdMutex);
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
const VerilatedFpList fdlist = fdToFpList(fdi);
|
||||
if (VL_UNLIKELY(fdlist.size() != 1)) return nullptr;
|
||||
return *fdlist.begin();
|
||||
@@ -463,7 +462,7 @@ public:
|
||||
// per map overhead * N scopes would take much more space and cache thrashing.
|
||||
// As scopep's are pointers, this implicitly handles multiple Context's
|
||||
static inline void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(s().m_userMapMutex);
|
||||
const VerilatedLockGuard lock{s().m_userMapMutex};
|
||||
const auto it = s().m_userMap.find(std::make_pair(scopep, userKey));
|
||||
if (it != s().m_userMap.end()) {
|
||||
it->second = userData;
|
||||
@@ -472,7 +471,7 @@ public:
|
||||
}
|
||||
}
|
||||
static inline void* userFind(const void* scopep, void* userKey) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(s().m_userMapMutex);
|
||||
const VerilatedLockGuard lock{s().m_userMapMutex};
|
||||
const auto& it = vlstd::as_const(s().m_userMap).find(std::make_pair(scopep, userKey));
|
||||
if (VL_UNLIKELY(it == s().m_userMap.end())) return nullptr;
|
||||
return it->second;
|
||||
@@ -482,7 +481,7 @@ public: // But only for verilated.cpp
|
||||
// Symbol table destruction cleans up the entries for each scope.
|
||||
static void userEraseScope(const VerilatedScope* scopep) VL_MT_SAFE {
|
||||
// Slow ok - called once/scope on destruction, so we simply iterate.
|
||||
const VerilatedLockGuard lock(s().m_userMapMutex);
|
||||
const VerilatedLockGuard lock{s().m_userMapMutex};
|
||||
for (auto it = s().m_userMap.begin(); it != s().m_userMap.end();) {
|
||||
if (it->first.first == scopep) {
|
||||
s().m_userMap.erase(it++);
|
||||
@@ -492,7 +491,7 @@ public: // But only for verilated.cpp
|
||||
}
|
||||
}
|
||||
static void userDump() VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(s().m_userMapMutex); // Avoid it changing in middle of dump
|
||||
const VerilatedLockGuard lock{s().m_userMapMutex}; // Avoid it changing in middle of dump
|
||||
bool first = true;
|
||||
for (const auto& i : s().m_userMap) {
|
||||
if (first) {
|
||||
@@ -508,13 +507,13 @@ public: // But only for verilated*.cpp
|
||||
// METHODS - hierarchy
|
||||
static void hierarchyAdd(const VerilatedScope* fromp, const VerilatedScope* top) VL_MT_SAFE {
|
||||
// Slow ok - called at construction for VPI accessible elements
|
||||
const VerilatedLockGuard lock(s().m_hierMapMutex);
|
||||
const VerilatedLockGuard lock{s().m_hierMapMutex};
|
||||
s().m_hierMap[fromp].push_back(top);
|
||||
}
|
||||
static void hierarchyRemove(const VerilatedScope* fromp,
|
||||
const VerilatedScope* top) VL_MT_SAFE {
|
||||
// Slow ok - called at destruction for VPI accessible elements
|
||||
const VerilatedLockGuard lock(s().m_hierMapMutex);
|
||||
const VerilatedLockGuard lock{s().m_hierMapMutex};
|
||||
VerilatedHierarchyMap& map = s().m_hierMap;
|
||||
if (map.find(fromp) == map.end()) return;
|
||||
auto& scopes = map[fromp];
|
||||
@@ -537,7 +536,7 @@ public: // But only for verilated*.cpp
|
||||
// miss at the cost of a multiply, and all lookups move to slowpath.
|
||||
static int exportInsert(const char* namep) VL_MT_SAFE {
|
||||
// Slow ok - called once/function at creation
|
||||
const VerilatedLockGuard lock(s().m_exportMutex);
|
||||
const VerilatedLockGuard lock{s().m_exportMutex};
|
||||
const auto it = s().m_exportMap.find(namep);
|
||||
if (it == s().m_exportMap.end()) {
|
||||
s().m_exportMap.emplace(namep, s().m_exportNext++);
|
||||
@@ -547,24 +546,24 @@ public: // But only for verilated*.cpp
|
||||
}
|
||||
}
|
||||
static int exportFind(const char* namep) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(s().m_exportMutex);
|
||||
const VerilatedLockGuard lock{s().m_exportMutex};
|
||||
const auto& it = s().m_exportMap.find(namep);
|
||||
if (VL_LIKELY(it != s().m_exportMap.end())) return it->second;
|
||||
std::string msg = (std::string("%Error: Testbench C called ") + namep
|
||||
+ " but no such DPI export function name exists in ANY model");
|
||||
const std::string msg = (std::string{"%Error: Testbench C called "} + namep
|
||||
+ " but no such DPI export function name exists in ANY model");
|
||||
VL_FATAL_MT("unknown", 0, "", msg.c_str());
|
||||
return -1;
|
||||
}
|
||||
static const char* exportName(int funcnum) VL_MT_SAFE {
|
||||
// Slowpath; find name for given export; errors only so no map to reverse-map it
|
||||
const VerilatedLockGuard lock(s().m_exportMutex);
|
||||
const VerilatedLockGuard lock{s().m_exportMutex};
|
||||
for (const auto& i : s().m_exportMap) {
|
||||
if (i.second == funcnum) return i.first;
|
||||
}
|
||||
return "*UNKNOWN*";
|
||||
}
|
||||
static void exportsDump() VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(s().m_exportMutex);
|
||||
const VerilatedLockGuard lock{s().m_exportMutex};
|
||||
bool first = true;
|
||||
for (const auto& i : s().m_exportMap) {
|
||||
if (first) {
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//=============================================================================
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
// Copyright 2012-2021 by Wilson Snyder. This program is free software; you
|
||||
// can redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//=============================================================================
|
||||
///
|
||||
/// \file
|
||||
/// \brief Verilated general profiling header
|
||||
///
|
||||
/// This file is not part of the Verilated public-facing API.
|
||||
/// It is only for internal use by Verilated library routines.
|
||||
///
|
||||
//=============================================================================
|
||||
|
||||
#ifndef VERILATOR_VERILATED_PROFILER_H_
|
||||
#define VERILATOR_VERILATED_PROFILER_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h" // for VerilatedMutex and clang annotations
|
||||
|
||||
// Profile record, private class used only by this header
|
||||
class VerilatedProfilerRec final {
|
||||
const std::string m_name; // Hashed name of mtask/etc
|
||||
const size_t m_counterNumber = 0; // Which counter has data
|
||||
public:
|
||||
// METHODS
|
||||
VerilatedProfilerRec(size_t counterNumber, const std::string& name)
|
||||
: m_name{name}
|
||||
, m_counterNumber{counterNumber} {}
|
||||
VerilatedProfilerRec() = default;
|
||||
size_t counterNumber() const { return m_counterNumber; }
|
||||
std::string name() const { return m_name; }
|
||||
};
|
||||
|
||||
// Create some number of bucketed profilers
|
||||
template <std::size_t T_Entries> class VerilatedProfiler final {
|
||||
// Counters are stored packed, all together, versus in VerilatedProfilerRec to
|
||||
// reduce cache effects
|
||||
std::array<vluint64_t, T_Entries> m_counters{}; // Time spent on this record
|
||||
std::deque<VerilatedProfilerRec> m_records; // Record information
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
VerilatedProfiler() = default;
|
||||
~VerilatedProfiler() = default;
|
||||
void write(const char* modelp, const std::string& filename) VL_MT_SAFE;
|
||||
void addCounter(size_t counter, const std::string& name) {
|
||||
VL_DEBUG_IF(assert(counter < T_Entries););
|
||||
m_records.emplace_back(VerilatedProfilerRec{counter, name});
|
||||
}
|
||||
void startCounter(size_t counter) {
|
||||
vluint64_t val;
|
||||
VL_RDTSC(val);
|
||||
// -= so when we add end time in stopCounter, we already subtracted
|
||||
// out, without needing to hold another temporary
|
||||
m_counters[counter] -= val;
|
||||
}
|
||||
void stopCounter(size_t counter) {
|
||||
vluint64_t val;
|
||||
VL_RDTSC(val);
|
||||
m_counters[counter] += val;
|
||||
}
|
||||
};
|
||||
|
||||
template <std::size_t T_Entries>
|
||||
void VerilatedProfiler<T_Entries>::write(const char* modelp,
|
||||
const std::string& filename) VL_MT_SAFE {
|
||||
static VerilatedMutex s_mutex;
|
||||
const VerilatedLockGuard lock{s_mutex};
|
||||
|
||||
// On the first call we create the file. On later calls we append.
|
||||
// So when we have multiple models in an executable, possibly even
|
||||
// running on different threads, each will have a different symtab so
|
||||
// each will collect is own data correctly. However when each is
|
||||
// destroid we need to get all the data, not keep overwriting and only
|
||||
// get the last model's data.
|
||||
static bool s_firstCall = true;
|
||||
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("+prof+vlt+file writing to '%s'\n", filename.c_str()););
|
||||
|
||||
FILE* fp = nullptr;
|
||||
if (!s_firstCall) fp = std::fopen(filename.c_str(), "a");
|
||||
if (VL_UNLIKELY(!fp))
|
||||
fp = std::fopen(filename.c_str(), "w"); // firstCall, or doesn't exist yet
|
||||
if (VL_UNLIKELY(!fp)) {
|
||||
VL_FATAL_MT(filename.c_str(), 0, "", "+prof+vlt+file file not writable");
|
||||
// cppcheck-suppress resourceLeak // bug, doesn't realize fp is nullptr
|
||||
return; // LCOV_EXCL_LINE
|
||||
}
|
||||
s_firstCall = false;
|
||||
|
||||
// TODO Perhaps merge with verilated_coverage output format, so can
|
||||
// have a common merging and reporting tool, etc.
|
||||
fprintf(fp, "// Verilated model profile-guided optimization data dump file\n");
|
||||
fprintf(fp, "`verilator_config\n");
|
||||
|
||||
for (const auto& it : m_records) {
|
||||
const std::string& name = it.name();
|
||||
fprintf(fp, "profile_data -model \"%s\" -mtask \"%s\" -cost 64'd%" VL_PRI64 "u\n", modelp,
|
||||
name.c_str(), m_counters[it.counterNumber()]);
|
||||
}
|
||||
|
||||
std::fclose(fp);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -93,8 +93,8 @@ void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE {
|
||||
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_HEADER_STR, std::strlen(VLTSAVE_HEADER_STR)))) {
|
||||
const std::string fn = filename();
|
||||
const std::string msg
|
||||
= std::string(
|
||||
"Can't deserialize; file has wrong header signature, or file not found: ")
|
||||
= std::
|
||||
string{"Can't deserialize; file has wrong header signature, or file not found: "}
|
||||
+ filename();
|
||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||
// Die before we close() as close would infinite loop
|
||||
@@ -112,7 +112,7 @@ void VerilatedDeserialize::trailer() VL_MT_UNSAFE_ONE {
|
||||
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_TRAILER_STR, std::strlen(VLTSAVE_TRAILER_STR)))) {
|
||||
const std::string fn = filename();
|
||||
const std::string msg
|
||||
= std::string("Can't deserialize; file has wrong end-of-file signature: ")
|
||||
= std::string{"Can't deserialize; file has wrong end-of-file signature: "}
|
||||
+ filename();
|
||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||
// Die before we close() as close would infinite loop
|
||||
@@ -192,7 +192,7 @@ void VerilatedRestore::close() VL_MT_UNSAFE_ONE {
|
||||
void VerilatedSave::flush() VL_MT_UNSAFE_ONE {
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(!isOpen())) return;
|
||||
vluint8_t* wp = m_bufp;
|
||||
const vluint8_t* wp = m_bufp;
|
||||
while (true) {
|
||||
const ssize_t remaining = (m_cp - wp);
|
||||
if (remaining == 0) break;
|
||||
@@ -204,7 +204,7 @@ void VerilatedSave::flush() VL_MT_UNSAFE_ONE {
|
||||
if (VL_UNCOVERABLE(errno != EAGAIN && errno != EINTR)) {
|
||||
// LCOV_EXCL_START
|
||||
// write failed, presume error (perhaps out of disk space)
|
||||
std::string msg = std::string(__FUNCTION__) + ": " + std::strerror(errno);
|
||||
const std::string msg = std::string{__FUNCTION__} + ": " + std::strerror(errno);
|
||||
VL_FATAL_MT("", 0, "", msg.c_str());
|
||||
close();
|
||||
break;
|
||||
@@ -235,7 +235,7 @@ void VerilatedRestore::fill() VL_MT_UNSAFE_ONE {
|
||||
if (VL_UNCOVERABLE(errno != EAGAIN && errno != EINTR)) {
|
||||
// LCOV_EXCL_START
|
||||
// write failed, presume error (perhaps out of disk space)
|
||||
const std::string msg = std::string(__FUNCTION__) + ": " + std::strerror(errno);
|
||||
const std::string msg = std::string{__FUNCTION__} + ": " + std::strerror(errno);
|
||||
VL_FATAL_MT("", 0, "", msg.c_str());
|
||||
close();
|
||||
break;
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#define VERILATOR_VERILATED_SAVE_C_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated_heavy.h"
|
||||
#include "verilated.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -279,7 +279,7 @@ inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, float& rhs) {
|
||||
return os.read(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const std::string& rhs) {
|
||||
vluint32_t len = rhs.length();
|
||||
const vluint32_t len = rhs.length();
|
||||
os << len;
|
||||
return os.write(rhs.data(), len);
|
||||
}
|
||||
|
||||
@@ -192,8 +192,8 @@ public:
|
||||
|
||||
class VerilatedDpiOpenVar final {
|
||||
// MEMBERS
|
||||
const VerilatedVarProps* m_propsp; // Variable properties
|
||||
void* m_datap; // Location of data (local to thread always, so safe)
|
||||
const VerilatedVarProps* const m_propsp; // Variable properties
|
||||
void* const m_datap; // Location of data (local to thread always, so safe)
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VerilatedDpiOpenVar(const VerilatedVarProps* propsp, void* datap)
|
||||
@@ -230,10 +230,10 @@ public:
|
||||
|
||||
class VerilatedVar final : public VerilatedVarProps {
|
||||
// MEMBERS
|
||||
void* m_datap; // Location of data
|
||||
const char* m_namep; // Name - slowpath
|
||||
void* const m_datap; // Location of data
|
||||
const char* const m_namep; // Name - slowpath
|
||||
protected:
|
||||
bool m_isParam;
|
||||
const bool m_isParam;
|
||||
friend class VerilatedScope;
|
||||
// CONSTRUCTORS
|
||||
VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#define VERILATOR_VERILATED_SYMS_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated_heavy.h"
|
||||
#include "verilated.h"
|
||||
#include "verilated_sym_props.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "verilated_threads.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
|
||||
//=============================================================================
|
||||
// Globals
|
||||
@@ -48,7 +49,8 @@ VlMTaskVertex::VlMTaskVertex(vluint32_t upstreamDepCount)
|
||||
// VlWorkerThread
|
||||
|
||||
VlWorkerThread::VlWorkerThread(VlThreadPool* poolp, VerilatedContext* contextp, bool profiling)
|
||||
: m_poolp{poolp}
|
||||
: m_ready_size{0}
|
||||
, m_poolp{poolp}
|
||||
, m_profiling{profiling} // Must init this last -- after setting up fields that it might read:
|
||||
, m_exiting{false}
|
||||
, m_cthread{startWorker, this}
|
||||
@@ -104,7 +106,7 @@ VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads, bool profil
|
||||
}
|
||||
// Create'em
|
||||
for (int i = 0; i < nThreads; ++i) {
|
||||
m_workers.push_back(new VlWorkerThread(this, contextp, profiling));
|
||||
m_workers.push_back(new VlWorkerThread{this, contextp, 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
|
||||
@@ -131,22 +133,22 @@ void VlThreadPool::setupProfilingClientThread() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
// try not to malloc while collecting profiling.
|
||||
t_profilep->reserve(4096);
|
||||
{
|
||||
const VerilatedLockGuard lk(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_allProfiles.insert(t_profilep);
|
||||
}
|
||||
}
|
||||
|
||||
void VlThreadPool::profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lk(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
for (const auto& profilep : m_allProfiles) {
|
||||
// Every thread's profile trace gets a copy of rec.
|
||||
profilep->emplace_back(rec);
|
||||
}
|
||||
}
|
||||
|
||||
void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed)
|
||||
void VlThreadPool::profileDump(const char* filenamep, vluint64_t tickStart, vluint64_t tickEnd)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lk(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("+prof+threads writing to '%s'\n", filenamep););
|
||||
|
||||
FILE* const fp = std::fopen(filenamep, "w");
|
||||
@@ -158,7 +160,7 @@ void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed)
|
||||
|
||||
// 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, "VLPROFTHREAD 1.1 # Verilator thread profile dump version 1.1\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::threadContextp()->profThreadsStart());
|
||||
@@ -166,6 +168,16 @@ void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed)
|
||||
Verilated::threadContextp()->profThreadsWindow());
|
||||
fprintf(fp, "VLPROF stat yields %" VL_PRI64 "u\n", VlMTaskVertex::yields());
|
||||
|
||||
// Copy /proc/cpuinfo into this output so verilator_gantt can be run on
|
||||
// a different machine
|
||||
{
|
||||
const std::unique_ptr<std::ifstream> ifp{new std::ifstream("/proc/cpuinfo")};
|
||||
if (!ifp->fail()) {
|
||||
std::string line;
|
||||
while (std::getline(*ifp, line)) { fprintf(fp, "VLPROFPROC %s\n", line.c_str()); }
|
||||
}
|
||||
}
|
||||
|
||||
vluint32_t thread_id = 0;
|
||||
for (const auto& pi : m_allProfiles) {
|
||||
++thread_id;
|
||||
@@ -176,20 +188,36 @@ void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed)
|
||||
case VlProfileRec::TYPE_BARRIER: //
|
||||
printing = true;
|
||||
break;
|
||||
case VlProfileRec::TYPE_EVAL:
|
||||
if (!printing) break;
|
||||
fprintf(fp,
|
||||
"VLPROF eval start %" VL_PRI64 "u elapsed %" VL_PRI64 "u"
|
||||
" cpu %u on thread %u\n",
|
||||
ei.m_startTime - tickStart, (ei.m_endTime - ei.m_startTime), ei.m_cpu,
|
||||
thread_id);
|
||||
break;
|
||||
case VlProfileRec::TYPE_EVAL_LOOP:
|
||||
if (!printing) break;
|
||||
fprintf(fp,
|
||||
"VLPROF eval_loop start %" VL_PRI64 "u elapsed %" VL_PRI64 "u"
|
||||
" cpu %u on thread %u\n",
|
||||
ei.m_startTime - tickStart, (ei.m_endTime - ei.m_startTime), ei.m_cpu,
|
||||
thread_id);
|
||||
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",
|
||||
ei.m_mtaskId, ei.m_startTime, ei.m_endTime,
|
||||
(ei.m_endTime - ei.m_startTime), ei.m_predictTime, ei.m_cpu, thread_id);
|
||||
" start %" VL_PRI64 "u elapsed %" VL_PRI64 "u"
|
||||
" predict_start %u predict_cost %u cpu %u on thread %u\n",
|
||||
ei.m_mtaskId, ei.m_startTime - tickStart, (ei.m_endTime - ei.m_startTime),
|
||||
ei.m_predictStart, ei.m_predictCost, ei.m_cpu, thread_id);
|
||||
break;
|
||||
default: assert(false); break; // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(fp, "VLPROF stat ticks %" VL_PRI64 "u\n", ticksElapsed);
|
||||
fprintf(fp, "VLPROF stat ticks %" VL_PRI64 "u\n", tickEnd - tickStart);
|
||||
|
||||
std::fclose(fp);
|
||||
}
|
||||
|
||||
+31
-15
@@ -131,21 +131,36 @@ public:
|
||||
class VlProfileRec final {
|
||||
protected:
|
||||
friend class VlThreadPool;
|
||||
enum VlProfileE { TYPE_MTASK_RUN, TYPE_BARRIER };
|
||||
VlProfileE m_type = TYPE_BARRIER; // Record type
|
||||
vluint32_t m_mtaskId = 0; // Mtask we're logging
|
||||
vluint32_t m_predictTime = 0; // How long scheduler predicted would take
|
||||
vluint64_t m_startTime = 0; // Tick at start of execution
|
||||
enum VlProfileE { TYPE_MTASK_RUN, TYPE_EVAL, TYPE_EVAL_LOOP, TYPE_BARRIER };
|
||||
// Layout below allows efficient packing.
|
||||
// Leave endTime first, so no math needed to calculate address in endRecord
|
||||
vluint64_t m_endTime = 0; // Tick at end of execution
|
||||
vluint64_t m_startTime = 0; // Tick at start of execution
|
||||
vluint32_t m_mtaskId = 0; // Mtask we're logging
|
||||
vluint32_t m_predictStart = 0; // Time scheduler predicted would start
|
||||
vluint32_t m_predictCost = 0; // How long scheduler predicted would take
|
||||
VlProfileE m_type = TYPE_BARRIER; // Record type
|
||||
unsigned m_cpu; // Execution CPU number (at start anyways)
|
||||
public:
|
||||
class Barrier {};
|
||||
VlProfileRec() = default;
|
||||
explicit VlProfileRec(Barrier) { m_cpu = getcpu(); }
|
||||
void startRecord(vluint64_t time, uint32_t mtask, uint32_t predict) {
|
||||
void startEval(vluint64_t time) {
|
||||
m_type = VlProfileRec::TYPE_EVAL;
|
||||
m_startTime = time;
|
||||
m_cpu = getcpu();
|
||||
}
|
||||
void startEvalLoop(vluint64_t time) {
|
||||
m_type = VlProfileRec::TYPE_EVAL_LOOP;
|
||||
m_startTime = time;
|
||||
m_cpu = getcpu();
|
||||
}
|
||||
void startRecord(vluint64_t time, vluint32_t mtask, vluint32_t predictStart,
|
||||
vluint32_t predictCost) {
|
||||
m_type = VlProfileRec::TYPE_MTASK_RUN;
|
||||
m_mtaskId = mtask;
|
||||
m_predictTime = predict;
|
||||
m_predictStart = predictStart;
|
||||
m_predictCost = predictCost;
|
||||
m_startTime = time;
|
||||
m_cpu = getcpu();
|
||||
}
|
||||
@@ -202,12 +217,12 @@ private:
|
||||
// Store the size atomically, so we can spin wait
|
||||
std::atomic<size_t> m_ready_size;
|
||||
|
||||
VlThreadPool* m_poolp; // Our associated thread pool
|
||||
VlThreadPool* const m_poolp; // Our associated thread pool
|
||||
|
||||
bool m_profiling; // Is profiling enabled?
|
||||
const bool m_profiling; // Is profiling enabled?
|
||||
std::atomic<bool> m_exiting; // Worker thread should exit
|
||||
std::thread m_cthread; // Underlying C++ thread record
|
||||
VerilatedContext* m_contextp; // Context for spawned thread
|
||||
VerilatedContext* const m_contextp; // Context for spawned thread
|
||||
|
||||
VL_UNCOPYABLE(VlWorkerThread);
|
||||
|
||||
@@ -225,10 +240,10 @@ public:
|
||||
}
|
||||
VL_CPU_RELAX();
|
||||
}
|
||||
VerilatedLockGuard lk(m_mutex);
|
||||
VerilatedLockGuard lock{m_mutex};
|
||||
while (m_ready.empty()) {
|
||||
m_waiting = true;
|
||||
m_cv.wait(lk);
|
||||
m_cv.wait(lock);
|
||||
}
|
||||
m_waiting = false;
|
||||
// As noted above this is inefficient if our ready list is ever
|
||||
@@ -242,7 +257,7 @@ public:
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
bool notify;
|
||||
{
|
||||
const VerilatedLockGuard lk(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_ready.emplace_back(fnp, selfp, evenCycle);
|
||||
m_ready_size.fetch_add(1, std::memory_order_relaxed);
|
||||
notify = m_waiting;
|
||||
@@ -259,7 +274,7 @@ class VlThreadPool final {
|
||||
|
||||
// MEMBERS
|
||||
std::vector<VlWorkerThread*> m_workers; // our workers
|
||||
bool m_profiling; // is profiling enabled?
|
||||
const 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
|
||||
@@ -292,7 +307,8 @@ public:
|
||||
return &(t_profilep->back());
|
||||
}
|
||||
void profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
void profileDump(const char* filenamep, vluint64_t ticksElapsed) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
void profileDump(const char* filenamep, vluint64_t tickStart, vluint64_t tickEnd)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
// In profiling mode, each executing thread must call
|
||||
// this once to setup profiling state:
|
||||
void setupProfilingClientThread() VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
|
||||
@@ -52,21 +52,21 @@ private:
|
||||
public:
|
||||
// Put an element at the back of the queue
|
||||
void put(T value) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_queue.push_back(value);
|
||||
m_cv.notify_one();
|
||||
}
|
||||
|
||||
// Put an element at the front of the queue
|
||||
void put_front(T value) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_queue.push_front(value);
|
||||
m_cv.notify_one();
|
||||
}
|
||||
|
||||
// Get an element from the front of the queue. Blocks if none available
|
||||
T get() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
VerilatedLockGuard lock{m_mutex};
|
||||
m_cv.wait(lock, [this]() VL_REQUIRES(m_mutex) { return !m_queue.empty(); });
|
||||
assert(!m_queue.empty());
|
||||
T value = m_queue.front();
|
||||
@@ -76,7 +76,7 @@ public:
|
||||
|
||||
// Non blocking get
|
||||
bool tryGet(T& result) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lockGuard(m_mutex);
|
||||
const VerilatedLockGuard lockGuard{m_mutex};
|
||||
if (m_queue.empty()) return false;
|
||||
result = m_queue.front();
|
||||
m_queue.pop_front();
|
||||
|
||||
@@ -246,7 +246,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::closeBase() {
|
||||
shutdownWorker();
|
||||
while (m_numTraceBuffers) {
|
||||
delete[] m_buffersFromWorker.get();
|
||||
m_numTraceBuffers--;
|
||||
--m_numTraceBuffers;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -267,7 +267,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::flushBase() {
|
||||
// Callbacks to run on global events
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::onFlush(void* selfp) {
|
||||
// This calls 'flush' on the derived classo (which must then get any mutex)
|
||||
// This calls 'flush' on the derived class (which must then get any mutex)
|
||||
reinterpret_cast<VL_DERIVED_T*>(selfp)->flush();
|
||||
}
|
||||
|
||||
@@ -351,7 +351,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
|
||||
m_traceBufferSize = nextCode() + numSignals() * 2 + 4;
|
||||
|
||||
// Start the worker thread
|
||||
m_workerThread.reset(new std::thread(&VerilatedTrace<VL_DERIVED_T>::workerThreadMain, this));
|
||||
m_workerThread.reset(new std::thread{&VerilatedTrace<VL_DERIVED_T>::workerThreadMain, this});
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -398,7 +398,7 @@ void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m
|
||||
// Not really VL_MT_SAFE but more VL_MT_UNSAFE_ONE.
|
||||
// This does get the mutex, but if multiple threads are trying to dump
|
||||
// chances are the data being dumped will have other problems
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
if (VL_UNCOVERABLE(m_timeLastDump && timeui <= m_timeLastDump)) { // LCOV_EXCL_START
|
||||
VL_PRINTF_MT("%%Warning: previous dump at t=%" VL_PRI64 "u, requesting t=%" VL_PRI64
|
||||
"u, dump call ignored\n",
|
||||
@@ -479,29 +479,29 @@ template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::addCallbackRecord(std::vector<CallbackRecord>& cbVec,
|
||||
CallbackRecord& cbRec)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
if (VL_UNCOVERABLE(timeLastDump() != 0)) { // LCOV_EXCL_START
|
||||
std::string msg = (std::string("Internal: ") + __FILE__ + "::" + __FUNCTION__
|
||||
+ " called with already open file");
|
||||
const std::string msg = (std::string{"Internal: "} + __FILE__ + "::" + __FUNCTION__
|
||||
+ " called with already open file");
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
|
||||
} // LCOV_EXCL_STOP
|
||||
cbVec.push_back(cbRec);
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::addInitCb(initCb_t cb, void* userp) VL_MT_SAFE {
|
||||
CallbackRecord cbr(cb, userp);
|
||||
CallbackRecord cbr{cb, userp};
|
||||
addCallbackRecord(m_initCbs, cbr);
|
||||
}
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
|
||||
CallbackRecord cbr(cb, userp);
|
||||
CallbackRecord cbr{cb, userp};
|
||||
addCallbackRecord(m_fullCbs, cbr);
|
||||
}
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
|
||||
CallbackRecord cbr(cb, userp);
|
||||
CallbackRecord cbr{cb, userp};
|
||||
addCallbackRecord(m_chgCbs, cbr);
|
||||
}
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::addCleanupCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
|
||||
CallbackRecord cbr(cb, userp);
|
||||
CallbackRecord cbr{cb, userp};
|
||||
addCallbackRecord(m_cleanupCbs, cbr);
|
||||
}
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::module(const std::string& name) VL_MT_UNSAFE {
|
||||
|
||||
@@ -0,0 +1,897 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you can
|
||||
// redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
///
|
||||
/// \file
|
||||
/// \brief Verilated common data type containers
|
||||
///
|
||||
/// verilated.h should be included instead of this file.
|
||||
///
|
||||
/// Those macro/function/variable starting or ending in _ are internal,
|
||||
/// however many of the other function/macros here are also internal.
|
||||
///
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_VERILATED_TYPES_H_
|
||||
#define VERILATOR_VERILATED_TYPES_H_
|
||||
|
||||
#ifndef VERILATOR_VERILATED_H_INTERNAL_
|
||||
#error "verilated_types.h should only be included by verilated.h"
|
||||
#endif
|
||||
|
||||
//===================================================================
|
||||
// String formatters (required by below containers)
|
||||
|
||||
extern std::string VL_TO_STRING(CData lhs);
|
||||
extern std::string VL_TO_STRING(SData lhs);
|
||||
extern std::string VL_TO_STRING(IData lhs);
|
||||
extern std::string VL_TO_STRING(QData lhs);
|
||||
inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; }
|
||||
extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
|
||||
|
||||
//=========================================================================
|
||||
// Declare net data types
|
||||
|
||||
#define VL_SIG8(name, msb, lsb) CData name ///< Declare signal, 1-8 bits
|
||||
#define VL_SIG16(name, msb, lsb) SData name ///< Declare signal, 9-16 bits
|
||||
#define VL_SIG64(name, msb, lsb) QData name ///< Declare signal, 33-64 bits
|
||||
#define VL_SIG(name, msb, lsb) IData name ///< Declare signal, 17-32 bits
|
||||
#define VL_SIGW(name, msb, lsb, words) WData name[words] ///< Declare signal, 65+ bits
|
||||
#define VL_IN8(name, msb, lsb) CData name ///< Declare input signal, 1-8 bits
|
||||
#define VL_IN16(name, msb, lsb) SData name ///< Declare input signal, 9-16 bits
|
||||
#define VL_IN64(name, msb, lsb) QData name ///< Declare input signal, 33-64 bits
|
||||
#define VL_IN(name, msb, lsb) IData name ///< Declare input signal, 17-32 bits
|
||||
#define VL_INW(name, msb, lsb, words) WData name[words] ///< Declare input signal, 65+ bits
|
||||
#define VL_INOUT8(name, msb, lsb) CData name ///< Declare bidir signal, 1-8 bits
|
||||
#define VL_INOUT16(name, msb, lsb) SData name ///< Declare bidir signal, 9-16 bits
|
||||
#define VL_INOUT64(name, msb, lsb) QData name ///< Declare bidir signal, 33-64 bits
|
||||
#define VL_INOUT(name, msb, lsb) IData name ///< Declare bidir signal, 17-32 bits
|
||||
#define VL_INOUTW(name, msb, lsb, words) WData name[words] ///< Declare bidir signal, 65+ bits
|
||||
#define VL_OUT8(name, msb, lsb) CData name ///< Declare output signal, 1-8 bits
|
||||
#define VL_OUT16(name, msb, lsb) SData name ///< Declare output signal, 9-16 bits
|
||||
#define VL_OUT64(name, msb, lsb) QData name ///< Declare output signal, 33-64bits
|
||||
#define VL_OUT(name, msb, lsb) IData name ///< Declare output signal, 17-32 bits
|
||||
#define VL_OUTW(name, msb, lsb, words) WData name[words] ///< Declare output signal, 65+ bits
|
||||
|
||||
//===================================================================
|
||||
// Shuffle RNG
|
||||
|
||||
extern vluint64_t vl_rand64() VL_MT_SAFE;
|
||||
|
||||
class VlURNG final {
|
||||
public:
|
||||
using result_type = size_t;
|
||||
static constexpr size_t min() { return 0; }
|
||||
static constexpr size_t max() { return 1ULL << 31; }
|
||||
size_t operator()() { return VL_MASK_I(31) & vl_rand64(); }
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// Readmem/Writemem operation classes
|
||||
|
||||
class VlReadMem final {
|
||||
const bool m_hex; // Hex format
|
||||
const int m_bits; // Bit width of values
|
||||
const std::string& m_filename; // Filename
|
||||
const QData m_end; // End address (as specified by user)
|
||||
FILE* m_fp; // File handle for filename
|
||||
QData m_addr; // Next address to read
|
||||
int m_linenum; // Line number last read from file
|
||||
public:
|
||||
VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end);
|
||||
~VlReadMem();
|
||||
bool isOpen() const { return m_fp != nullptr; }
|
||||
int linenum() const { return m_linenum; }
|
||||
bool get(QData& addrr, std::string& valuer);
|
||||
void setData(void* valuep, const std::string& rhs);
|
||||
};
|
||||
|
||||
class VlWriteMem final {
|
||||
const bool m_hex; // Hex format
|
||||
const int m_bits; // Bit width of values
|
||||
FILE* m_fp; // File handle for filename
|
||||
QData m_addr; // Next address to write
|
||||
public:
|
||||
VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end);
|
||||
~VlWriteMem();
|
||||
bool isOpen() const { return m_fp != nullptr; }
|
||||
void print(QData addr, bool addrstamp, const void* valuep);
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
/// Verilog wide packed bit container.
|
||||
/// Similar to std::array<WData, N>, but lighter weight, only methods needed
|
||||
/// by Verilator, to help compile time.
|
||||
///
|
||||
/// A 'struct' as we want this to be an aggregate type that allows
|
||||
/// static aggregate initialization. Consider data members private.
|
||||
///
|
||||
/// For example a Verilog "bit [94:0]" will become a VlWide<3> because 3*32
|
||||
/// bits are needed to hold the 95 bits. The MSB (bit 96) must always be
|
||||
/// zero in memory, but during intermediate operations in the Verilated
|
||||
/// internals is unpredictable.
|
||||
|
||||
static int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE;
|
||||
|
||||
template <std::size_t T_Words> struct VlWide final {
|
||||
// MEMBERS
|
||||
// This should be the only data member, otherwise generated static initializers need updating
|
||||
EData m_storage[T_Words]; // Contents of the packed array
|
||||
|
||||
// CONSTRUCTORS
|
||||
// Default constructors and destructor are used. Note however that C++20 requires that
|
||||
// aggregate types do not have a user declared constructor, not even an explicitly defaulted
|
||||
// one.
|
||||
|
||||
// OPERATOR METHODS
|
||||
// Default copy assignment operators are used.
|
||||
operator WDataOutP() { return &m_storage[0]; } // This also allows []
|
||||
operator WDataInP() const { return &m_storage[0]; } // This also allows []
|
||||
|
||||
// METHODS
|
||||
const EData& at(size_t index) const { return m_storage[index]; }
|
||||
EData& at(size_t index) { return m_storage[index]; }
|
||||
WData* data() { return &m_storage[0]; }
|
||||
const WData* data() const { return &m_storage[0]; }
|
||||
bool operator<(const VlWide<T_Words>& rhs) const {
|
||||
return _vl_cmp_w(T_Words, data(), rhs.data()) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Convert a C array to std::array reference by pointer magic, without copy.
|
||||
// Data type (second argument) is so the function template can automatically generate.
|
||||
template <std::size_t T_Words>
|
||||
VlWide<T_Words>& VL_CVT_W_A(const WDataInP inp, const VlWide<T_Words>&) {
|
||||
return *((VlWide<T_Words>*)inp);
|
||||
}
|
||||
|
||||
template <std::size_t T_Words> std::string VL_TO_STRING(const VlWide<T_Words>& obj) {
|
||||
return VL_TO_STRING_W(T_Words, obj.data());
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Verilog queue and dynamic array container
|
||||
// There are no multithreaded locks on this; the base variable must
|
||||
// be protected by other means
|
||||
//
|
||||
// Bound here is the maximum size() allowed, e.g. 1 + SystemVerilog bound
|
||||
// For dynamic arrays it is always zero
|
||||
template <class T_Value, size_t T_MaxSize = 0> class VlQueue final {
|
||||
private:
|
||||
// TYPES
|
||||
using Deque = std::deque<T_Value>;
|
||||
|
||||
public:
|
||||
using const_iterator = typename Deque::const_iterator;
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
Deque m_deque; // State of the assoc array
|
||||
T_Value m_defaultValue; // Default value
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
// m_defaultValue isn't defaulted. Caller's constructor must do it.
|
||||
VlQueue() = default;
|
||||
~VlQueue() = default;
|
||||
VlQueue(const VlQueue&) = default;
|
||||
VlQueue(VlQueue&&) = default;
|
||||
VlQueue& operator=(const VlQueue&) = default;
|
||||
VlQueue& operator=(VlQueue&&) = default;
|
||||
|
||||
// Standard copy constructor works. Verilog: assoca = assocb
|
||||
// Also must allow conversion from a different T_MaxSize queue
|
||||
template <size_t U_MaxSize = 0> VlQueue operator=(const VlQueue<T_Value, U_MaxSize>& rhs) {
|
||||
m_deque = rhs.privateDeque();
|
||||
if (VL_UNLIKELY(T_MaxSize && T_MaxSize < m_deque.size())) m_deque.resize(T_MaxSize - 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
static VlQueue cons(const T_Value& lhs) {
|
||||
VlQueue out;
|
||||
out.push_back(lhs);
|
||||
return out;
|
||||
}
|
||||
static VlQueue cons(const T_Value& lhs, const T_Value& rhs) {
|
||||
VlQueue out;
|
||||
out.push_back(rhs);
|
||||
out.push_back(lhs);
|
||||
return out;
|
||||
}
|
||||
static VlQueue cons(const VlQueue& lhs, const T_Value& rhs) {
|
||||
VlQueue out = lhs;
|
||||
out.push_front(rhs);
|
||||
return out;
|
||||
}
|
||||
static VlQueue cons(const T_Value& lhs, const VlQueue& rhs) {
|
||||
VlQueue out = rhs;
|
||||
out.push_back(lhs);
|
||||
return out;
|
||||
}
|
||||
static VlQueue cons(const VlQueue& lhs, const VlQueue& rhs) {
|
||||
VlQueue out = rhs;
|
||||
for (const auto& i : lhs.m_deque) out.push_back(i);
|
||||
return out;
|
||||
}
|
||||
|
||||
// METHODS
|
||||
T_Value& atDefault() { return m_defaultValue; }
|
||||
const T_Value& atDefault() const { return m_defaultValue; }
|
||||
const Deque& privateDeque() const { return m_deque; }
|
||||
|
||||
// Size. Verilog: function int size(), or int num()
|
||||
int size() const { return m_deque.size(); }
|
||||
// Clear array. Verilog: function void delete([input index])
|
||||
void clear() { m_deque.clear(); }
|
||||
void erase(vlsint32_t index) {
|
||||
if (VL_LIKELY(index >= 0 && index < m_deque.size()))
|
||||
m_deque.erase(m_deque.begin() + index);
|
||||
}
|
||||
|
||||
// Dynamic array new[] becomes a renew()
|
||||
void renew(size_t size) {
|
||||
clear();
|
||||
m_deque.resize(size, atDefault());
|
||||
}
|
||||
// Dynamic array new[]() becomes a renew_copy()
|
||||
void renew_copy(size_t size, const VlQueue<T_Value, T_MaxSize>& rhs) {
|
||||
if (size == 0) {
|
||||
clear();
|
||||
} else {
|
||||
*this = rhs;
|
||||
m_deque.resize(size, atDefault());
|
||||
}
|
||||
}
|
||||
|
||||
// function void q.push_front(value)
|
||||
void push_front(const T_Value& value) {
|
||||
m_deque.push_front(value);
|
||||
if (VL_UNLIKELY(T_MaxSize != 0 && m_deque.size() > T_MaxSize)) m_deque.pop_back();
|
||||
}
|
||||
// function void q.push_back(value)
|
||||
void push_back(const T_Value& value) {
|
||||
if (VL_LIKELY(T_MaxSize == 0 || m_deque.size() < T_MaxSize)) m_deque.push_back(value);
|
||||
}
|
||||
// function value_t q.pop_front();
|
||||
T_Value pop_front() {
|
||||
if (m_deque.empty()) return m_defaultValue;
|
||||
T_Value v = m_deque.front();
|
||||
m_deque.pop_front();
|
||||
return v;
|
||||
}
|
||||
// function value_t q.pop_back();
|
||||
T_Value pop_back() {
|
||||
if (m_deque.empty()) return m_defaultValue;
|
||||
T_Value v = m_deque.back();
|
||||
m_deque.pop_back();
|
||||
return v;
|
||||
}
|
||||
|
||||
// Setting. Verilog: assoc[index] = v
|
||||
// Can't just overload operator[] or provide a "at" reference to set,
|
||||
// because we need to be able to insert only when the value is set
|
||||
T_Value& at(vlsint32_t index) {
|
||||
static T_Value s_throwAway;
|
||||
// Needs to work for dynamic arrays, so does not use T_MaxSize
|
||||
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
|
||||
s_throwAway = atDefault();
|
||||
return s_throwAway;
|
||||
} else {
|
||||
return m_deque[index];
|
||||
}
|
||||
}
|
||||
// Accessing. Verilog: v = assoc[index]
|
||||
const T_Value& at(vlsint32_t index) const {
|
||||
static T_Value s_throwAway;
|
||||
// Needs to work for dynamic arrays, so does not use T_MaxSize
|
||||
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
|
||||
return atDefault();
|
||||
} else {
|
||||
return m_deque[index];
|
||||
}
|
||||
}
|
||||
// function void q.insert(index, value);
|
||||
void insert(vlsint32_t index, const T_Value& value) {
|
||||
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) return;
|
||||
m_deque.insert(m_deque.begin() + index, value);
|
||||
}
|
||||
|
||||
// Return slice q[lsb:msb]
|
||||
VlQueue slice(vlsint32_t lsb, vlsint32_t msb) const {
|
||||
VlQueue out;
|
||||
if (VL_UNLIKELY(lsb < 0)) lsb = 0;
|
||||
if (VL_UNLIKELY(lsb >= m_deque.size())) lsb = m_deque.size() - 1;
|
||||
if (VL_UNLIKELY(msb >= m_deque.size())) msb = m_deque.size() - 1;
|
||||
for (vlsint32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]);
|
||||
return out;
|
||||
}
|
||||
|
||||
// For save/restore
|
||||
const_iterator begin() const { return m_deque.begin(); }
|
||||
const_iterator end() const { return m_deque.end(); }
|
||||
|
||||
// Methods
|
||||
void sort() { std::sort(m_deque.begin(), m_deque.end()); }
|
||||
template <typename Func> void sort(Func with_func) {
|
||||
// with_func returns arbitrary type to use for the sort comparison
|
||||
std::sort(m_deque.begin(), m_deque.end(), [=](const T_Value& a, const T_Value& b) {
|
||||
// index number is meaninless with sort, as it changes
|
||||
return with_func(0, a) < with_func(0, b);
|
||||
});
|
||||
}
|
||||
void rsort() { std::sort(m_deque.rbegin(), m_deque.rend()); }
|
||||
template <typename Func> void rsort(Func with_func) {
|
||||
// with_func returns arbitrary type to use for the sort comparison
|
||||
std::sort(m_deque.rbegin(), m_deque.rend(), [=](const T_Value& a, const T_Value& b) {
|
||||
// index number is meaninless with sort, as it changes
|
||||
return with_func(0, a) < with_func(0, b);
|
||||
});
|
||||
}
|
||||
void reverse() { std::reverse(m_deque.begin(), m_deque.end()); }
|
||||
void shuffle() { std::shuffle(m_deque.begin(), m_deque.end(), VlURNG{}); }
|
||||
VlQueue unique() const {
|
||||
VlQueue out;
|
||||
std::unordered_set<T_Value> saw;
|
||||
for (const auto& i : m_deque) {
|
||||
auto it = saw.find(i);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i);
|
||||
out.push_back(i);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
VlQueue<IData> unique_index() const {
|
||||
VlQueue<IData> out;
|
||||
IData index = 0;
|
||||
std::unordered_set<T_Value> saw;
|
||||
for (const auto& i : m_deque) {
|
||||
auto it = saw.find(i);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i);
|
||||
out.push_back(index);
|
||||
}
|
||||
++index;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue find(Func with_func) const {
|
||||
VlQueue out;
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
if (with_func(index, i)) out.push_back(i);
|
||||
++index;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue<IData> find_index(Func with_func) const {
|
||||
VlQueue<IData> out;
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
if (with_func(index, i)) out.push_back(index);
|
||||
++index;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue find_first(Func with_func) const {
|
||||
// Can't use std::find_if as need index number
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
if (with_func(index, i)) return VlQueue::cons(i);
|
||||
++index;
|
||||
}
|
||||
return VlQueue{};
|
||||
}
|
||||
template <typename Func> VlQueue<IData> find_first_index(Func with_func) const {
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
if (with_func(index, i)) return VlQueue<IData>::cons(index);
|
||||
++index;
|
||||
}
|
||||
return VlQueue<IData>{};
|
||||
}
|
||||
template <typename Func> VlQueue find_last(Func with_func) const {
|
||||
IData index = m_deque.size() - 1;
|
||||
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
|
||||
if (with_func(index, *it)) return VlQueue::cons(*it);
|
||||
--index;
|
||||
}
|
||||
return VlQueue{};
|
||||
}
|
||||
template <typename Func> VlQueue<IData> find_last_index(Func with_func) const {
|
||||
IData index = m_deque.size() - 1;
|
||||
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
|
||||
if (with_func(index, *it)) return VlQueue<IData>::cons(index);
|
||||
--index;
|
||||
}
|
||||
return VlQueue<IData>{};
|
||||
}
|
||||
|
||||
// Reduction operators
|
||||
VlQueue min() const {
|
||||
if (m_deque.empty()) return VlQueue{};
|
||||
const auto it = std::min_element(m_deque.begin(), m_deque.end());
|
||||
return VlQueue::cons(*it);
|
||||
}
|
||||
VlQueue max() const {
|
||||
if (m_deque.empty()) return VlQueue{};
|
||||
const auto it = std::max_element(m_deque.begin(), m_deque.end());
|
||||
return VlQueue::cons(*it);
|
||||
}
|
||||
|
||||
T_Value r_sum() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_deque) out += i;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_sum(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) out += with_func(index++, i);
|
||||
return out;
|
||||
}
|
||||
T_Value r_product() const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
T_Value out{*it};
|
||||
++it;
|
||||
for (; it != m_deque.end(); ++it) out *= *it;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_product(Func with_func) const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
IData index = 0;
|
||||
T_Value out{with_func(index, *it)};
|
||||
++it;
|
||||
++index;
|
||||
for (; it != m_deque.end(); ++it) out *= with_func(index++, *it);
|
||||
return out;
|
||||
}
|
||||
T_Value r_and() const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
T_Value out{*it};
|
||||
++it;
|
||||
for (; it != m_deque.end(); ++it) out &= *it;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_and(Func with_func) const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
IData index = 0;
|
||||
T_Value out{with_func(index, *it)};
|
||||
++it;
|
||||
++index;
|
||||
for (; it != m_deque.end(); ++it) out &= with_func(index, *it);
|
||||
return out;
|
||||
}
|
||||
T_Value r_or() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_deque) out |= i;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_or(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) out |= with_func(index++, i);
|
||||
return out;
|
||||
}
|
||||
T_Value r_xor() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_deque) out ^= i;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_xor(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) out ^= with_func(index++, i);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Dumping. Verilog: str = $sformatf("%p", assoc)
|
||||
std::string to_string() const {
|
||||
if (m_deque.empty()) return "'{}"; // No trailing space
|
||||
std::string out = "'{";
|
||||
std::string comma;
|
||||
for (const auto& i : m_deque) {
|
||||
out += comma + VL_TO_STRING(i);
|
||||
comma = ", ";
|
||||
}
|
||||
return out + "} ";
|
||||
}
|
||||
};
|
||||
|
||||
template <class T_Value> std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
|
||||
return obj.to_string();
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Verilog associative array container
|
||||
// There are no multithreaded locks on this; the base variable must
|
||||
// be protected by other means
|
||||
//
|
||||
template <class T_Key, class T_Value> class VlAssocArray final {
|
||||
private:
|
||||
// TYPES
|
||||
using Map = std::map<T_Key, T_Value>;
|
||||
|
||||
public:
|
||||
using const_iterator = typename Map::const_iterator;
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
Map m_map; // State of the assoc array
|
||||
T_Value m_defaultValue; // Default value
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
// m_defaultValue isn't defaulted. Caller's constructor must do it.
|
||||
VlAssocArray() = default;
|
||||
~VlAssocArray() = default;
|
||||
VlAssocArray(const VlAssocArray&) = default;
|
||||
VlAssocArray(VlAssocArray&&) = default;
|
||||
VlAssocArray& operator=(const VlAssocArray&) = default;
|
||||
VlAssocArray& operator=(VlAssocArray&&) = default;
|
||||
|
||||
// METHODS
|
||||
T_Value& atDefault() { return m_defaultValue; }
|
||||
const T_Value& atDefault() const { return m_defaultValue; }
|
||||
|
||||
// Size of array. Verilog: function int size(), or int num()
|
||||
int size() const { return m_map.size(); }
|
||||
// Clear array. Verilog: function void delete([input index])
|
||||
void clear() { m_map.clear(); }
|
||||
void erase(const T_Key& index) { m_map.erase(index); }
|
||||
// Return 0/1 if element exists. Verilog: function int exists(input index)
|
||||
int exists(const T_Key& index) const { return m_map.find(index) != m_map.end(); }
|
||||
// Return first element. Verilog: function int first(ref index);
|
||||
int first(T_Key& indexr) const {
|
||||
const auto it = m_map.cbegin();
|
||||
if (it == m_map.end()) return 0;
|
||||
indexr = it->first;
|
||||
return 1;
|
||||
}
|
||||
// Return last element. Verilog: function int last(ref index)
|
||||
int last(T_Key& indexr) const {
|
||||
const auto it = m_map.crbegin();
|
||||
if (it == m_map.rend()) return 0;
|
||||
indexr = it->first;
|
||||
return 1;
|
||||
}
|
||||
// Return next element. Verilog: function int next(ref index)
|
||||
int next(T_Key& indexr) const {
|
||||
auto it = m_map.find(indexr);
|
||||
if (VL_UNLIKELY(it == m_map.end())) return 0;
|
||||
++it;
|
||||
if (VL_UNLIKELY(it == m_map.end())) return 0;
|
||||
indexr = it->first;
|
||||
return 1;
|
||||
}
|
||||
// Return prev element. Verilog: function int prev(ref index)
|
||||
int prev(T_Key& indexr) const {
|
||||
auto it = m_map.find(indexr);
|
||||
if (VL_UNLIKELY(it == m_map.end())) return 0;
|
||||
if (VL_UNLIKELY(it == m_map.begin())) return 0;
|
||||
--it;
|
||||
indexr = it->first;
|
||||
return 1;
|
||||
}
|
||||
// Setting. Verilog: assoc[index] = v
|
||||
// Can't just overload operator[] or provide a "at" reference to set,
|
||||
// because we need to be able to insert only when the value is set
|
||||
T_Value& at(const T_Key& index) {
|
||||
const auto it = m_map.find(index);
|
||||
if (it == m_map.end()) {
|
||||
std::pair<typename Map::iterator, bool> pit = m_map.emplace(index, m_defaultValue);
|
||||
return pit.first->second;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
// Accessing. Verilog: v = assoc[index]
|
||||
const T_Value& at(const T_Key& index) const {
|
||||
const auto it = m_map.find(index);
|
||||
if (it == m_map.end()) {
|
||||
return m_defaultValue;
|
||||
} else {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
// Setting as a chained operation
|
||||
VlAssocArray& set(const T_Key& index, const T_Value& value) {
|
||||
at(index) = value;
|
||||
return *this;
|
||||
}
|
||||
VlAssocArray& setDefault(const T_Value& value) {
|
||||
atDefault() = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// For save/restore
|
||||
const_iterator begin() const { return m_map.begin(); }
|
||||
const_iterator end() const { return m_map.end(); }
|
||||
|
||||
// Methods
|
||||
VlQueue<T_Value> unique() const {
|
||||
VlQueue<T_Value> out;
|
||||
std::set<T_Value> saw;
|
||||
for (const auto& i : m_map) {
|
||||
auto it = saw.find(i.second);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i.second);
|
||||
out.push_back(i.second);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
VlQueue<T_Key> unique_index() const {
|
||||
VlQueue<T_Key> out;
|
||||
std::set<T_Key> saw;
|
||||
for (const auto& i : m_map) {
|
||||
auto it = saw.find(i.second);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i.second);
|
||||
out.push_back(i.first);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue<T_Value> find(Func with_func) const {
|
||||
VlQueue<T_Value> out;
|
||||
for (const auto& i : m_map)
|
||||
if (with_func(i.first, i.second)) out.push_back(i.second);
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue<T_Key> find_index(Func with_func) const {
|
||||
VlQueue<T_Key> out;
|
||||
for (const auto& i : m_map)
|
||||
if (with_func(i.first, i.second)) out.push_back(i.first);
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue<T_Value> find_first(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
if (it == m_map.end()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
template <typename Func> VlQueue<T_Key> find_first_index(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
if (it == m_map.end()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Key>::cons(it->first);
|
||||
}
|
||||
template <typename Func> VlQueue<T_Value> find_last(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
if (it == m_map.rend()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
template <typename Func> VlQueue<T_Key> find_last_index(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
if (it == m_map.rend()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Key>::cons(it->first);
|
||||
}
|
||||
|
||||
// Reduction operators
|
||||
VlQueue<T_Value> min() const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
const auto it = std::min_element(
|
||||
m_map.begin(), m_map.end(),
|
||||
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
|
||||
return a.second < b.second;
|
||||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
VlQueue<T_Value> max() const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
const auto it = std::max_element(
|
||||
m_map.begin(), m_map.end(),
|
||||
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
|
||||
return a.second < b.second;
|
||||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
|
||||
T_Value r_sum() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out += i.second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_sum(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out += with_func(i.first, i.second);
|
||||
return out;
|
||||
}
|
||||
T_Value r_product() const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
T_Value out{it->second};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out *= it->second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_product(Func with_func) const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
T_Value out{with_func(it->first, it->second)};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out *= with_func(it->first, it->second);
|
||||
return out;
|
||||
}
|
||||
T_Value r_and() const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
T_Value out{it->second};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out &= it->second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_and(Func with_func) const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
T_Value out{with_func(it->first, it->second)};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out &= with_func(it->first, it->second);
|
||||
return out;
|
||||
}
|
||||
T_Value r_or() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out |= i.second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_or(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out |= with_func(i.first, i.second);
|
||||
return out;
|
||||
}
|
||||
T_Value r_xor() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out ^= i.second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_xor(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out ^= with_func(i.first, i.second);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Dumping. Verilog: str = $sformatf("%p", assoc)
|
||||
std::string to_string() const {
|
||||
if (m_map.empty()) return "'{}"; // No trailing space
|
||||
std::string out = "'{";
|
||||
std::string comma;
|
||||
for (const auto& i : m_map) {
|
||||
out += comma + VL_TO_STRING(i.first) + ":" + VL_TO_STRING(i.second);
|
||||
comma = ", ";
|
||||
}
|
||||
// Default not printed - maybe random init data
|
||||
return out + "} ";
|
||||
}
|
||||
};
|
||||
|
||||
template <class T_Key, class T_Value>
|
||||
std::string VL_TO_STRING(const VlAssocArray<T_Key, T_Value>& obj) {
|
||||
return obj.to_string();
|
||||
}
|
||||
|
||||
template <class T_Key, class T_Value>
|
||||
void VL_READMEM_N(bool hex, int bits, const std::string& filename,
|
||||
VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
|
||||
VlReadMem rmem{hex, bits, filename, start, end};
|
||||
if (VL_UNLIKELY(!rmem.isOpen())) return;
|
||||
while (true) {
|
||||
QData addr;
|
||||
std::string data;
|
||||
if (rmem.get(addr /*ref*/, data /*ref*/)) {
|
||||
rmem.setData(&(obj.at(addr)), data);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class T_Key, class T_Value>
|
||||
void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
|
||||
const VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
|
||||
VlWriteMem wmem{hex, bits, filename, start, end};
|
||||
if (VL_UNLIKELY(!wmem.isOpen())) return;
|
||||
for (const auto& i : obj) {
|
||||
const QData addr = i.first;
|
||||
if (addr >= start && addr <= end) wmem.print(addr, true, &(i.second));
|
||||
}
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
/// Verilog unpacked array container
|
||||
/// For when a standard C++[] array is not sufficient, e.g. an
|
||||
/// array under a queue, or methods operating on the array.
|
||||
///
|
||||
/// A 'struct' as we want this to be an aggregate type that allows
|
||||
/// static aggregate initialization. Consider data members private.
|
||||
///
|
||||
/// This class may get exposed to a Verilated Model's top I/O, if the top
|
||||
/// IO has an unpacked array.
|
||||
|
||||
template <class T_Value, std::size_t T_Depth> struct VlUnpacked final {
|
||||
// MEMBERS
|
||||
// This should be the only data member, otherwise generated static initializers need updating
|
||||
T_Value m_storage[T_Depth]; // Contents of the unpacked array
|
||||
|
||||
// CONSTRUCTORS
|
||||
// Default constructors and destructor are used. Note however that C++20 requires that
|
||||
// aggregate types do not have a user declared constructor, not even an explicitly defaulted
|
||||
// one.
|
||||
|
||||
// OPERATOR METHODS
|
||||
// Default copy assignment operators are used.
|
||||
|
||||
// METHODS
|
||||
// Raw access
|
||||
WData* data() { return &m_storage[0]; }
|
||||
const WData* data() const { return &m_storage[0]; }
|
||||
|
||||
T_Value& operator[](size_t index) { return m_storage[index]; };
|
||||
const T_Value& operator[](size_t index) const { return m_storage[index]; };
|
||||
|
||||
// Dumping. Verilog: str = $sformatf("%p", assoc)
|
||||
std::string to_string() const {
|
||||
std::string out = "'{";
|
||||
std::string comma;
|
||||
for (int i = 0; i < T_Depth; ++i) {
|
||||
out += comma + VL_TO_STRING(m_storage[i]);
|
||||
comma = ", ";
|
||||
}
|
||||
return out + "} ";
|
||||
}
|
||||
};
|
||||
|
||||
template <class T_Value, std::size_t T_Depth>
|
||||
std::string VL_TO_STRING(const VlUnpacked<T_Value, T_Depth>& obj) {
|
||||
return obj.to_string();
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Verilog class reference container
|
||||
// There are no multithreaded locks on this; the base variable must
|
||||
// be protected by other means
|
||||
|
||||
#define VlClassRef std::shared_ptr
|
||||
|
||||
template <class T> // T typically of type VlClassRef<x>
|
||||
inline T VL_NULL_CHECK(T t, const char* filename, int linenum) {
|
||||
if (VL_UNLIKELY(!t)) Verilated::nullPointerError(filename, linenum);
|
||||
return t;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
static inline bool VL_CAST_DYNAMIC(VlClassRef<T> in, VlClassRef<U>& outr) {
|
||||
VlClassRef<U> casted = std::dynamic_pointer_cast<U>(in);
|
||||
if (VL_LIKELY(casted)) {
|
||||
outr = casted;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
#endif // Guard
|
||||
+26
-25
@@ -102,7 +102,7 @@ VerilatedVcd::VerilatedVcd(VerilatedVcdFile* filep) {
|
||||
}
|
||||
|
||||
void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
if (isOpen()) return;
|
||||
|
||||
// Set member variables
|
||||
@@ -120,7 +120,7 @@ void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
void VerilatedVcd::openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
// Open next filename in concat sequence, mangle filename if
|
||||
// incFilename is true.
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
openNextImp(incFilename);
|
||||
}
|
||||
|
||||
@@ -195,11 +195,11 @@ void VerilatedVcd::makeNameMap() {
|
||||
if (!hiername.empty() && hiername[0] == '\t') nullScope = true;
|
||||
}
|
||||
if (nullScope) {
|
||||
NameMap* newmapp = new NameMap;
|
||||
NameMap* const newmapp = new NameMap;
|
||||
for (const auto& i : *m_namemapp) {
|
||||
const std::string& hiername = i.first;
|
||||
const std::string& decl = i.second;
|
||||
std::string newname = std::string("top");
|
||||
std::string newname{"top"};
|
||||
if (hiername[0] != '\t') newname += ' ';
|
||||
newname += hiername;
|
||||
newmapp->emplace(newname, decl);
|
||||
@@ -243,7 +243,7 @@ void VerilatedVcd::closeErr() {
|
||||
|
||||
void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
// This function is on the flush() call path
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
if (!isOpen()) return;
|
||||
if (m_evcd) {
|
||||
printStr("$vcdclose ");
|
||||
@@ -257,7 +257,7 @@ void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
}
|
||||
|
||||
void VerilatedVcd::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
VerilatedTrace<VerilatedVcd>::flushBase();
|
||||
bufferFlush();
|
||||
}
|
||||
@@ -271,8 +271,9 @@ void VerilatedVcd::printStr(const char* str) {
|
||||
}
|
||||
|
||||
void VerilatedVcd::printQuad(vluint64_t n) {
|
||||
char buf[100];
|
||||
VL_SNPRINTF(buf, 100, "%" VL_PRI64 "u", n);
|
||||
constexpr size_t LEN_STR_QUAD = 40;
|
||||
char buf[LEN_STR_QUAD];
|
||||
VL_SNPRINTF(buf, LEN_STR_QUAD, "%" VL_PRI64 "u", n);
|
||||
printStr(buf);
|
||||
}
|
||||
|
||||
@@ -280,7 +281,7 @@ 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;
|
||||
const char* oldbufp = m_wrBufp;
|
||||
m_wrChunkSize = minsize * 2;
|
||||
m_wrBufp = new char[m_wrChunkSize * 8];
|
||||
std::memcpy(m_wrBufp, oldbufp, m_writep - oldbufp);
|
||||
@@ -297,7 +298,7 @@ void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
|
||||
// When it gets nearly full we dump it using this routine which calls write()
|
||||
// This is much faster than using buffered I/O
|
||||
if (VL_UNLIKELY(!isOpen())) return;
|
||||
char* wp = m_wrBufp;
|
||||
const char* wp = m_wrBufp;
|
||||
while (true) {
|
||||
const ssize_t remaining = (m_writep - wp);
|
||||
if (remaining == 0) break;
|
||||
@@ -310,8 +311,8 @@ void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
|
||||
if (VL_UNCOVERABLE(errno != EAGAIN && errno != EINTR)) {
|
||||
// LCOV_EXCL_START
|
||||
// write failed, presume error (perhaps out of disk space)
|
||||
std::string msg
|
||||
= std::string("VerilatedVcd::bufferFlush: ") + std::strerror(errno);
|
||||
const std::string msg
|
||||
= std::string{"VerilatedVcd::bufferFlush: "} + std::strerror(errno);
|
||||
VL_FATAL_MT("", 0, "", msg.c_str());
|
||||
closeErr();
|
||||
break;
|
||||
@@ -331,7 +332,7 @@ char* VerilatedVcd::writeCode(char* writep, vluint32_t code) {
|
||||
*writep++ = static_cast<char>('!' + code % 94);
|
||||
code /= 94;
|
||||
while (code) {
|
||||
code--;
|
||||
--code;
|
||||
*writep++ = static_cast<char>('!' + code % 94);
|
||||
code /= 94;
|
||||
}
|
||||
@@ -355,9 +356,9 @@ void VerilatedVcd::dumpHeader() {
|
||||
const time_t tick = time(nullptr);
|
||||
tm ticktm;
|
||||
VL_LOCALTIME_R(&tick, &ticktm);
|
||||
constexpr int bufsize = 50;
|
||||
char buf[bufsize];
|
||||
std::strftime(buf, bufsize, "%c", &ticktm);
|
||||
constexpr size_t LEN_BUF = 50;
|
||||
char buf[LEN_BUF];
|
||||
std::strftime(buf, LEN_BUF, "%c", &ticktm);
|
||||
printStr(buf);
|
||||
}
|
||||
printStr(" $end\n");
|
||||
@@ -385,7 +386,7 @@ void VerilatedVcd::dumpHeader() {
|
||||
const std::string& decl = i.second;
|
||||
|
||||
// Determine difference between the old and new names
|
||||
const char* hiername = hiernamestr.c_str();
|
||||
const char* const hiername = hiernamestr.c_str();
|
||||
const char* lp = lastName;
|
||||
const char* np = hiername;
|
||||
lastName = hiername;
|
||||
@@ -393,8 +394,8 @@ void VerilatedVcd::dumpHeader() {
|
||||
// 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--;
|
||||
--np;
|
||||
--lp;
|
||||
}
|
||||
// printf("hier %s\n lp=%s\n np=%s\n",hiername,lp,np);
|
||||
|
||||
@@ -430,9 +431,9 @@ void VerilatedVcd::dumpHeader() {
|
||||
|
||||
for (; *np && *np != ' ' && *np != '\t'; np++) {
|
||||
if (*np == '[') {
|
||||
printStr("(");
|
||||
printStr("[");
|
||||
} else if (*np == ']') {
|
||||
printStr(")");
|
||||
printStr("]");
|
||||
} else if (!(*np & '\x80')) {
|
||||
*m_writep++ = *np;
|
||||
}
|
||||
@@ -531,7 +532,7 @@ void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep,
|
||||
decl += " ";
|
||||
decl += basename;
|
||||
if (array) {
|
||||
VL_SNPRINTF(buf, bufsize, "(%d)", arraynum);
|
||||
VL_SNPRINTF(buf, bufsize, "[%d]", arraynum);
|
||||
decl += buf;
|
||||
hiername += buf;
|
||||
}
|
||||
@@ -896,9 +897,9 @@ void vcdTestMain(const char* filenamep) {
|
||||
VerilatedVcdC* vcdp = new VerilatedVcdC;
|
||||
vcdp->evcd(true);
|
||||
vcdp->set_time_unit("1ms");
|
||||
vcdp->set_time_unit(std::string("1ms"));
|
||||
vcdp->set_time_unit(std::string{"1ms"});
|
||||
vcdp->set_time_resolution("1ns");
|
||||
vcdp->set_time_resolution(std::string("1ns"));
|
||||
vcdp->set_time_resolution(std::string{"1ns"});
|
||||
vcdp->spTrace()->addInitCb(&vcdInit, 0);
|
||||
vcdp->spTrace()->addFullCb(&vcdFull, 0);
|
||||
vcdp->spTrace()->addChgCb(&vcdChange, 0);
|
||||
@@ -927,7 +928,7 @@ void vcdTestMain(const char* filenamep) {
|
||||
vcdp->dump(++timestamp);
|
||||
vcdp->dump(++timestamp);
|
||||
# ifdef VERILATED_VCD_TEST_64BIT
|
||||
vluint64_t bytesPerDump = 15ULL;
|
||||
const vluint64_t bytesPerDump = 15ULL;
|
||||
for (vluint64_t i = 0; i < ((1ULL << 32) / bytesPerDump); i++) {
|
||||
v1 = i;
|
||||
vcdp->dump(++timestamp);
|
||||
|
||||
@@ -74,7 +74,7 @@ private:
|
||||
int m_modDepth = 0; // Depth of module hierarchy
|
||||
|
||||
char* m_wrBufp; // Output buffer
|
||||
char* m_wrFlushp; // Output buffer flush trigger location
|
||||
const 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 = 0; // Number of bytes written to this file
|
||||
|
||||
@@ -44,9 +44,9 @@ public:
|
||||
// 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.
|
||||
const sc_time t1sec(1, SC_SEC);
|
||||
const sc_time t1sec{1, SC_SEC};
|
||||
if (t1sec.to_default_time_units() != 0) {
|
||||
const sc_time tunits(1.0 / t1sec.to_default_time_units(), SC_SEC);
|
||||
const 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());
|
||||
|
||||
+122
-116
@@ -80,7 +80,7 @@ public:
|
||||
static const size_t chunk = 96;
|
||||
if (VL_UNCOVERABLE(size > chunk)) VL_FATAL_MT(__FILE__, __LINE__, "", "increase chunk");
|
||||
if (VL_LIKELY(t_freeHead)) {
|
||||
vluint8_t* newp = t_freeHead;
|
||||
vluint8_t* const newp = t_freeHead;
|
||||
t_freeHead = *(reinterpret_cast<vluint8_t**>(newp));
|
||||
*(reinterpret_cast<vluint32_t*>(newp)) = activeMagic();
|
||||
return newp + 8;
|
||||
@@ -91,7 +91,7 @@ public:
|
||||
return newp + 8;
|
||||
}
|
||||
static void operator delete(void* obj, size_t /*size*/)VL_MT_SAFE {
|
||||
vluint8_t* oldp = (static_cast<vluint8_t*>(obj)) - 8;
|
||||
vluint8_t* const oldp = (static_cast<vluint8_t*>(obj)) - 8;
|
||||
if (VL_UNLIKELY(*(reinterpret_cast<vluint32_t*>(oldp)) != activeMagic())) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"vpi_release_handle() called on same object twice, or on non-Verilator "
|
||||
@@ -123,12 +123,12 @@ public:
|
||||
class VerilatedVpioTimedCb final : public VerilatedVpio {
|
||||
// A handle to a timed callback created with vpi_register_cb
|
||||
// User can call vpi_remove_cb or vpi_release_handle on it
|
||||
vluint64_t m_id; // Unique id/sequence number to find schedule's event
|
||||
QData m_time;
|
||||
const vluint64_t m_id; // Unique id/sequence number to find schedule's event
|
||||
const QData m_time;
|
||||
|
||||
public:
|
||||
VerilatedVpioTimedCb(vluint64_t id, QData time)
|
||||
: m_id(id)
|
||||
: m_id{id}
|
||||
, m_time{time} {}
|
||||
virtual ~VerilatedVpioTimedCb() override = default;
|
||||
static VerilatedVpioTimedCb* castp(vpiHandle h) {
|
||||
@@ -141,13 +141,13 @@ public:
|
||||
class VerilatedVpioReasonCb final : public VerilatedVpio {
|
||||
// A handle to a non-timed callback created with vpi_register_cb
|
||||
// User can call vpi_remove_cb or vpi_release_handle on it
|
||||
vluint64_t m_id; // Unique id/sequence number to find schedule's event
|
||||
PLI_INT32 m_reason; // VPI callback reason code
|
||||
const vluint64_t m_id; // Unique id/sequence number to find schedule's event
|
||||
const PLI_INT32 m_reason; // VPI callback reason code
|
||||
|
||||
public:
|
||||
// cppcheck-suppress uninitVar // m_value
|
||||
VerilatedVpioReasonCb(vluint64_t id, PLI_INT32 reason)
|
||||
: m_id(id)
|
||||
: m_id{id}
|
||||
, m_reason{reason} {}
|
||||
virtual ~VerilatedVpioReasonCb() override = default;
|
||||
static VerilatedVpioReasonCb* castp(vpiHandle h) {
|
||||
@@ -158,7 +158,7 @@ public:
|
||||
};
|
||||
|
||||
class VerilatedVpioConst final : public VerilatedVpio {
|
||||
vlsint32_t m_num;
|
||||
const vlsint32_t m_num;
|
||||
|
||||
public:
|
||||
explicit VerilatedVpioConst(vlsint32_t num)
|
||||
@@ -200,7 +200,7 @@ public:
|
||||
virtual const char* name() const override { return m_varp->name(); }
|
||||
virtual const char* fullname() const override {
|
||||
static VL_THREAD_LOCAL std::string t_out;
|
||||
t_out = std::string(m_scopep->name()) + "." + name();
|
||||
t_out = std::string{m_scopep->name()} + "." + name();
|
||||
return t_out.c_str();
|
||||
}
|
||||
};
|
||||
@@ -208,7 +208,7 @@ public:
|
||||
class VerilatedVpioParam final : public VerilatedVpioVarBase {
|
||||
public:
|
||||
VerilatedVpioParam(const VerilatedVar* varp, const VerilatedScope* scopep)
|
||||
: VerilatedVpioVarBase(varp, scopep) {}
|
||||
: VerilatedVpioVarBase{varp, scopep} {}
|
||||
virtual ~VerilatedVpioParam() override = default;
|
||||
|
||||
static VerilatedVpioParam* castp(vpiHandle h) {
|
||||
@@ -219,7 +219,7 @@ public:
|
||||
};
|
||||
|
||||
class VerilatedVpioRange final : public VerilatedVpio {
|
||||
const VerilatedRange* m_range;
|
||||
const VerilatedRange* const m_range;
|
||||
|
||||
public:
|
||||
explicit VerilatedVpioRange(const VerilatedRange* range)
|
||||
@@ -235,7 +235,7 @@ public:
|
||||
|
||||
class VerilatedVpioRangeIter final : public VerilatedVpio {
|
||||
// Only supports 1 dimension
|
||||
const VerilatedRange* m_range;
|
||||
const VerilatedRange* const m_range;
|
||||
bool m_done = false;
|
||||
|
||||
public:
|
||||
@@ -252,13 +252,13 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
m_done = true;
|
||||
return ((new VerilatedVpioRange(m_range))->castVpiHandle());
|
||||
return ((new VerilatedVpioRange{m_range})->castVpiHandle());
|
||||
}
|
||||
};
|
||||
|
||||
class VerilatedVpioScope VL_NOT_FINAL : public VerilatedVpio {
|
||||
protected:
|
||||
const VerilatedScope* m_scopep;
|
||||
const VerilatedScope* const m_scopep;
|
||||
|
||||
public:
|
||||
explicit VerilatedVpioScope(const VerilatedScope* scopep)
|
||||
@@ -286,13 +286,13 @@ protected:
|
||||
|
||||
public:
|
||||
VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep)
|
||||
: VerilatedVpioVarBase(varp, scopep) {
|
||||
: VerilatedVpioVarBase{varp, scopep} {
|
||||
m_mask.u32 = VL_MASK_I(varp->packed().elements());
|
||||
m_entSize = varp->entSize();
|
||||
m_varDatap = varp->datap();
|
||||
}
|
||||
explicit VerilatedVpioVar(const VerilatedVpioVar* varp)
|
||||
: VerilatedVpioVarBase(varp) {
|
||||
: VerilatedVpioVarBase{varp} {
|
||||
if (varp) {
|
||||
m_mask.u32 = varp->m_mask.u32;
|
||||
m_entSize = varp->m_entSize;
|
||||
@@ -343,15 +343,16 @@ public:
|
||||
virtual const VerilatedRange* rangep() const override { return &(varp()->packed()); }
|
||||
virtual const char* fullname() const override {
|
||||
static VL_THREAD_LOCAL std::string t_out;
|
||||
char num[25];
|
||||
VL_SNPRINTF(num, 25, "%d", m_index);
|
||||
t_out = std::string(scopep()->name()) + "." + name() + "[" + num + "]";
|
||||
constexpr size_t LEN_MAX_INDEX = 25;
|
||||
char num[LEN_MAX_INDEX];
|
||||
VL_SNPRINTF(num, LEN_MAX_INDEX, "%d", m_index);
|
||||
t_out = std::string{scopep()->name()} + "." + name() + "[" + num + "]";
|
||||
return t_out.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
class VerilatedVpioVarIter final : public VerilatedVpio {
|
||||
const VerilatedScope* m_scopep;
|
||||
const VerilatedScope* const m_scopep;
|
||||
VerilatedVarNameMap::const_iterator m_it;
|
||||
bool m_started = false;
|
||||
|
||||
@@ -379,7 +380,7 @@ public:
|
||||
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
||||
return nullptr;
|
||||
}
|
||||
return ((new VerilatedVpioVar(&(m_it->second), m_scopep))->castVpiHandle());
|
||||
return ((new VerilatedVpioVar{&(m_it->second), m_scopep})->castVpiHandle());
|
||||
}
|
||||
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
||||
return nullptr; // End of list - only one deep
|
||||
@@ -388,9 +389,9 @@ public:
|
||||
|
||||
class VerilatedVpioMemoryWordIter final : public VerilatedVpio {
|
||||
const vpiHandle m_handle;
|
||||
const VerilatedVar* m_varp;
|
||||
const VerilatedVar* const m_varp;
|
||||
vlsint32_t m_iteration;
|
||||
vlsint32_t m_direction;
|
||||
const vlsint32_t m_direction;
|
||||
bool m_done = false;
|
||||
|
||||
public:
|
||||
@@ -412,7 +413,7 @@ public:
|
||||
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
||||
return nullptr;
|
||||
}
|
||||
vpiHandle result = vpi_handle_by_index(m_handle, m_iteration);
|
||||
const vpiHandle result = vpi_handle_by_index(m_handle, m_iteration);
|
||||
iterationInc();
|
||||
return result;
|
||||
}
|
||||
@@ -456,8 +457,8 @@ public:
|
||||
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
||||
return nullptr;
|
||||
}
|
||||
const VerilatedScope* modp = *m_it++;
|
||||
return (new VerilatedVpioModule(modp))->castVpiHandle();
|
||||
const VerilatedScope* const modp = *m_it++;
|
||||
return (new VerilatedVpioModule{modp})->castVpiHandle();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -475,16 +476,16 @@ class VerilatedVpiCbHolder final {
|
||||
public:
|
||||
// cppcheck-suppress uninitVar // m_value
|
||||
VerilatedVpiCbHolder(vluint64_t id, const s_cb_data* cbDatap, const VerilatedVpioVar* varop)
|
||||
: m_id(id)
|
||||
, m_cbData(*cbDatap)
|
||||
, m_varo(varop) {
|
||||
: m_id{id}
|
||||
, m_cbData{*cbDatap}
|
||||
, m_varo{varop} {
|
||||
m_value.format = cbDatap->value ? cbDatap->value->format : vpiSuppressVal;
|
||||
m_cbData.value = &m_value;
|
||||
if (varop) {
|
||||
m_cbData.obj = m_varo.castVpiHandle();
|
||||
m_varo.createPrevDatap();
|
||||
} else {
|
||||
m_cbData.obj = NULL;
|
||||
m_cbData.obj = nullptr;
|
||||
}
|
||||
}
|
||||
~VerilatedVpiCbHolder() = default;
|
||||
@@ -594,7 +595,7 @@ public:
|
||||
const auto last = std::prev(cbObjList.end()); // prevent looping over newly added elements
|
||||
for (auto it = cbObjList.begin(); true;) {
|
||||
// cbReasonRemove sets to nullptr, so we know on removal the old end() will still exist
|
||||
bool was_last = it == last;
|
||||
const bool was_last = it == last;
|
||||
if (VL_UNLIKELY(it->invalid())) { // Deleted earlier, cleanup
|
||||
it = cbObjList.erase(it);
|
||||
if (was_last) break;
|
||||
@@ -626,16 +627,18 @@ public:
|
||||
continue;
|
||||
}
|
||||
VerilatedVpiCbHolder& ho = *it++;
|
||||
if (VerilatedVpioVar* varop = VerilatedVpioVar::castp(ho.cb_datap()->obj)) {
|
||||
void* newDatap = varop->varDatap();
|
||||
void* prevDatap = varop->prevDatap(); // Was malloced when we added the callback
|
||||
if (VerilatedVpioVar* const varop = VerilatedVpioVar::castp(ho.cb_datap()->obj)) {
|
||||
void* const newDatap = varop->varDatap();
|
||||
void* const prevDatap
|
||||
= varop->prevDatap(); // Was malloced when we added the callback
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n",
|
||||
varop->fullname(), *((CData*)newDatap),
|
||||
*((CData*)prevDatap), newDatap, prevDatap););
|
||||
varop->fullname(), *(static_cast<CData*>(newDatap)),
|
||||
*(static_cast<CData*>(prevDatap)), newDatap,
|
||||
prevDatap););
|
||||
if (std::memcmp(prevDatap, newDatap, varop->entSize()) != 0) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" VL_PRI64
|
||||
"d %s v[0]=%d\n",
|
||||
ho.id(), varop->fullname(), *((CData*)newDatap)););
|
||||
VL_DEBUG_IF_PLI(
|
||||
VL_DBG_MSGF("- vpi: value_callback %" VL_PRI64 "d %s v[0]=%d\n", ho.id(),
|
||||
varop->fullname(), *(static_cast<CData*>(newDatap))););
|
||||
update.insert(varop);
|
||||
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
|
||||
(ho.cb_rtnp())(ho.cb_datap());
|
||||
@@ -706,7 +709,8 @@ public:
|
||||
va_end(args);
|
||||
m_errorInfo.state = vpiPLI;
|
||||
t_filehold = file;
|
||||
setError((PLI_BYTE8*)m_buff, nullptr, const_cast<PLI_BYTE8*>(t_filehold.c_str()), line);
|
||||
setError(static_cast<PLI_BYTE8*>(m_buff), nullptr,
|
||||
const_cast<PLI_BYTE8*>(t_filehold.c_str()), line);
|
||||
}
|
||||
p_vpi_error_info getError() {
|
||||
if (m_flag) return &m_errorInfo;
|
||||
@@ -758,7 +762,7 @@ PLI_INT32 VerilatedVpioReasonCb::dovpi_remove_cb() {
|
||||
|
||||
VerilatedVpiError* VerilatedVpiImp::error_info() VL_MT_UNSAFE_ONE {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
if (VL_UNLIKELY(!s().m_errorInfop)) s().m_errorInfop = new VerilatedVpiError();
|
||||
if (VL_UNLIKELY(!s().m_errorInfop)) s().m_errorInfop = new VerilatedVpiError;
|
||||
return s().m_errorInfop;
|
||||
}
|
||||
|
||||
@@ -1107,14 +1111,14 @@ const char* VerilatedVpiError::strFromVpiProp(PLI_INT32 vpiVal) VL_MT_SAFE {
|
||||
|
||||
#define SELF_CHECK_RESULT_CSTR(got, exp) \
|
||||
if (0 != std::strcmp((got), (exp))) { \
|
||||
std::string msg \
|
||||
= std::string("%Error: ") + "GOT = '" + got + "'" + " EXP = '" + exp + "'"; \
|
||||
const std::string msg \
|
||||
= std::string{"%Error: "} + "GOT = '" + (got) + "'" + " EXP = '" + (exp) + "'"; \
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); \
|
||||
}
|
||||
|
||||
#define SELF_CHECK_ENUM_STR(fn, enumn) \
|
||||
do { \
|
||||
const char* strVal = VerilatedVpiError::fn(enumn); \
|
||||
const char* const strVal = VerilatedVpiError::fn(enumn); \
|
||||
SELF_CHECK_RESULT_CSTR(strVal, #enumn); \
|
||||
} while (0)
|
||||
|
||||
@@ -1301,7 +1305,7 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
|
||||
QData time = 0;
|
||||
if (cb_data_p->time) time = VL_SET_QII(cb_data_p->time->high, cb_data_p->time->low);
|
||||
const QData abstime = VL_TIME_Q() + time;
|
||||
vluint64_t id = VerilatedVpiImp::nextCallbackId();
|
||||
const vluint64_t id = VerilatedVpiImp::nextCallbackId();
|
||||
VerilatedVpioTimedCb* const vop = new VerilatedVpioTimedCb{id, abstime};
|
||||
VerilatedVpiImp::cbTimedAdd(id, cb_data_p, abstime);
|
||||
return vop->castVpiHandle();
|
||||
@@ -1358,7 +1362,7 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||
const VerilatedVpioScope* const voScopep = VerilatedVpioScope::castp(scope);
|
||||
std::string scopeAndName = namep;
|
||||
if (voScopep) {
|
||||
scopeAndName = std::string(voScopep->fullname()) + "." + namep;
|
||||
scopeAndName = std::string{voScopep->fullname()} + "." + namep;
|
||||
namep = const_cast<PLI_BYTE8*>(scopeAndName.c_str());
|
||||
}
|
||||
{
|
||||
@@ -1366,9 +1370,9 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||
scopep = Verilated::threadContextp()->scopeFind(namep);
|
||||
if (scopep) { // Whole thing found as a scope
|
||||
if (scopep->type() == VerilatedScope::SCOPE_MODULE) {
|
||||
return (new VerilatedVpioModule(scopep))->castVpiHandle();
|
||||
return (new VerilatedVpioModule{scopep})->castVpiHandle();
|
||||
} else {
|
||||
return (new VerilatedVpioScope(scopep))->castVpiHandle();
|
||||
return (new VerilatedVpioScope{scopep})->castVpiHandle();
|
||||
}
|
||||
}
|
||||
const char* baseNamep = scopeAndName.c_str();
|
||||
@@ -1376,7 +1380,8 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||
const char* const dotp = std::strrchr(namep, '.');
|
||||
if (VL_LIKELY(dotp)) {
|
||||
baseNamep = dotp + 1;
|
||||
scopename = std::string(namep, dotp - namep);
|
||||
const size_t len = dotp - namep;
|
||||
scopename = std::string{namep, len};
|
||||
}
|
||||
|
||||
if (scopename.find('.') == std::string::npos) {
|
||||
@@ -1393,9 +1398,9 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||
if (!varp) return nullptr;
|
||||
|
||||
if (varp->isParam()) {
|
||||
return (new VerilatedVpioParam(varp, scopep))->castVpiHandle();
|
||||
return (new VerilatedVpioParam{varp, scopep})->castVpiHandle();
|
||||
} else {
|
||||
return (new VerilatedVpioVar(varp, scopep))->castVpiHandle();
|
||||
return (new VerilatedVpioVar{varp, scopep})->castVpiHandle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1405,7 +1410,7 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
VL_VPI_ERROR_RESET_();
|
||||
// Memory words are not indexable
|
||||
VerilatedVpioMemoryWord* vop = VerilatedVpioMemoryWord::castp(object);
|
||||
const VerilatedVpioMemoryWord* const vop = VerilatedVpioMemoryWord::castp(object);
|
||||
if (VL_UNLIKELY(vop)) return nullptr;
|
||||
const VerilatedVpioVar* const varop = VerilatedVpioVar::castp(object);
|
||||
if (VL_LIKELY(varop)) {
|
||||
@@ -1414,15 +1419,15 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) {
|
||||
if (VL_UNLIKELY(indx > varop->varp()->unpacked().left()
|
||||
|| indx < varop->varp()->unpacked().right()))
|
||||
return nullptr;
|
||||
return (new VerilatedVpioMemoryWord(varop->varp(), varop->scopep(), indx,
|
||||
indx - varop->varp()->unpacked().right()))
|
||||
return (new VerilatedVpioMemoryWord{varop->varp(), varop->scopep(), indx,
|
||||
indx - varop->varp()->unpacked().right()})
|
||||
->castVpiHandle();
|
||||
}
|
||||
if (VL_UNLIKELY(indx < varop->varp()->unpacked().left()
|
||||
|| indx > varop->varp()->unpacked().right()))
|
||||
return nullptr;
|
||||
return (new VerilatedVpioMemoryWord(varop->varp(), varop->scopep(), indx,
|
||||
indx - varop->varp()->unpacked().left()))
|
||||
return (new VerilatedVpioMemoryWord{varop->varp(), varop->scopep(), indx,
|
||||
indx - varop->varp()->unpacked().left()})
|
||||
->castVpiHandle();
|
||||
}
|
||||
VL_VPI_INTERNAL_(__FILE__, __LINE__, "%s : can't resolve handle", __func__);
|
||||
@@ -1437,12 +1442,12 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
|
||||
VL_VPI_ERROR_RESET_();
|
||||
switch (type) {
|
||||
case vpiLeftRange: {
|
||||
if (VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object)) {
|
||||
if (const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object)) {
|
||||
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
|
||||
return (new VerilatedVpioConst(vop->rangep()->left()))->castVpiHandle();
|
||||
} else if (VerilatedVpioRange* vop = VerilatedVpioRange::castp(object)) {
|
||||
return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle();
|
||||
} else if (const VerilatedVpioRange* const vop = VerilatedVpioRange::castp(object)) {
|
||||
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
|
||||
return (new VerilatedVpioConst(vop->rangep()->left()))->castVpiHandle();
|
||||
return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle();
|
||||
}
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__,
|
||||
"%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned",
|
||||
@@ -1450,12 +1455,12 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
|
||||
return nullptr;
|
||||
}
|
||||
case vpiRightRange: {
|
||||
if (VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object)) {
|
||||
if (const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object)) {
|
||||
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
|
||||
return (new VerilatedVpioConst(vop->rangep()->right()))->castVpiHandle();
|
||||
} else if (VerilatedVpioRange* vop = VerilatedVpioRange::castp(object)) {
|
||||
return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle();
|
||||
} else if (const VerilatedVpioRange* const vop = VerilatedVpioRange::castp(object)) {
|
||||
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
|
||||
return (new VerilatedVpioConst(vop->rangep()->right()))->castVpiHandle();
|
||||
return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle();
|
||||
}
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__,
|
||||
"%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned",
|
||||
@@ -1463,19 +1468,20 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
|
||||
return nullptr;
|
||||
}
|
||||
case vpiIndex: {
|
||||
VerilatedVpioVar* vop = VerilatedVpioVar::castp(object);
|
||||
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return nullptr;
|
||||
return (new VerilatedVpioConst(vop->index()))->castVpiHandle();
|
||||
const vlsint32_t val = vop->index();
|
||||
return (new VerilatedVpioConst{val})->castVpiHandle();
|
||||
}
|
||||
case vpiScope: {
|
||||
VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object);
|
||||
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return nullptr;
|
||||
return (new VerilatedVpioScope(vop->scopep()))->castVpiHandle();
|
||||
return (new VerilatedVpioScope{vop->scopep()})->castVpiHandle();
|
||||
}
|
||||
case vpiParent: {
|
||||
VerilatedVpioMemoryWord* vop = VerilatedVpioMemoryWord::castp(object);
|
||||
const VerilatedVpioMemoryWord* const vop = VerilatedVpioMemoryWord::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return nullptr;
|
||||
return (new VerilatedVpioVar(vop->varp(), vop->scopep()))->castVpiHandle();
|
||||
return (new VerilatedVpioVar{vop->varp(), vop->scopep()})->castVpiHandle();
|
||||
}
|
||||
default:
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
|
||||
@@ -1505,7 +1511,7 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
|
||||
VerilatedVpiError::strFromVpiMethod(type), vop->fullname(),
|
||||
vop->varp()->dims());
|
||||
}
|
||||
return (new VerilatedVpioMemoryWordIter(object, vop->varp()))->castVpiHandle();
|
||||
return (new VerilatedVpioMemoryWordIter{object, vop->varp()})->castVpiHandle();
|
||||
}
|
||||
case vpiRange: {
|
||||
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
||||
@@ -1518,12 +1524,12 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
|
||||
VerilatedVpiError::strFromVpiMethod(type), vop->fullname(),
|
||||
vop->varp()->dims());
|
||||
}
|
||||
return ((new VerilatedVpioRangeIter(vop->rangep()))->castVpiHandle());
|
||||
return ((new VerilatedVpioRangeIter{vop->rangep()})->castVpiHandle());
|
||||
}
|
||||
case vpiReg: {
|
||||
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return nullptr;
|
||||
return ((new VerilatedVpioVarIter(vop->scopep()))->castVpiHandle());
|
||||
return ((new VerilatedVpioVarIter{vop->scopep()})->castVpiHandle());
|
||||
}
|
||||
case vpiModule: {
|
||||
const VerilatedVpioModule* const vop = VerilatedVpioModule::castp(object);
|
||||
@@ -1531,7 +1537,7 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
|
||||
const VerilatedScope* const modp = vop ? vop->scopep() : nullptr;
|
||||
const auto it = vlstd::as_const(map)->find(const_cast<VerilatedScope*>(modp));
|
||||
if (it == map->end()) return nullptr;
|
||||
return ((new VerilatedVpioModuleIter(it->second))->castVpiHandle());
|
||||
return ((new VerilatedVpioModuleIter{it->second})->castVpiHandle());
|
||||
}
|
||||
default:
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
|
||||
@@ -1543,7 +1549,7 @@ vpiHandle vpi_scan(vpiHandle object) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_scan %p\n", object););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
VL_VPI_ERROR_RESET_();
|
||||
VerilatedVpio* vop = VerilatedVpio::castp(object);
|
||||
VerilatedVpio* const vop = VerilatedVpio::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return nullptr;
|
||||
return vop->dovpi_scan();
|
||||
}
|
||||
@@ -1689,15 +1695,15 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
const char* fullname) {
|
||||
if (!vl_check_format(varp, valuep, fullname, true)) return;
|
||||
// Maximum required size is for binary string, one byte per bit plus null termination
|
||||
static VL_THREAD_LOCAL char t_outStr[1 + VL_MULS_MAX_WORDS * 32];
|
||||
static VL_THREAD_LOCAL char t_outStr[VL_VALUE_STRING_MAX_WORDS * VL_EDATASIZE + 1];
|
||||
// cppcheck-suppress variableScope
|
||||
static VL_THREAD_LOCAL int t_outStrSz = sizeof(t_outStr) - 1;
|
||||
const static VL_THREAD_LOCAL int t_outStrSz = sizeof(t_outStr) - 1;
|
||||
// We used to presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal
|
||||
// This may cause backward compatibility issues with older code.
|
||||
if (valuep->format == vpiVectorVal) {
|
||||
// Vector pointer must come from our memory pool
|
||||
// It only needs to persist until the next vpi_get_value
|
||||
static VL_THREAD_LOCAL t_vpi_vecval t_out[VL_MULS_MAX_WORDS * 2];
|
||||
static VL_THREAD_LOCAL t_vpi_vecval t_out[VL_VALUE_STRING_MAX_WORDS * 2];
|
||||
valuep->value.vector = t_out;
|
||||
if (varp->vltype() == VLVT_UINT8) {
|
||||
t_out[0].aval = *(reinterpret_cast<CData*>(varDatap));
|
||||
@@ -1712,18 +1718,18 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
t_out[0].bval = 0;
|
||||
return;
|
||||
} else if (varp->vltype() == VLVT_UINT64) {
|
||||
QData data = *(reinterpret_cast<QData*>(varDatap));
|
||||
const QData data = *(reinterpret_cast<QData*>(varDatap));
|
||||
t_out[1].aval = static_cast<IData>(data >> 32ULL);
|
||||
t_out[1].bval = 0;
|
||||
t_out[0].aval = static_cast<IData>(data);
|
||||
t_out[0].bval = 0;
|
||||
return;
|
||||
} else if (varp->vltype() == VLVT_WDATA) {
|
||||
int words = VL_WORDS_I(varp->packed().elements());
|
||||
if (VL_UNCOVERABLE(words >= VL_MULS_MAX_WORDS)) {
|
||||
VL_FATAL_MT(
|
||||
__FILE__, __LINE__, "",
|
||||
"vpi_get_value with more than VL_MULS_MAX_WORDS; increase and recompile");
|
||||
const int words = VL_WORDS_I(varp->packed().elements());
|
||||
if (VL_UNCOVERABLE(words >= VL_VALUE_STRING_MAX_WORDS)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"vpi_get_value with more than VL_VALUE_STRING_MAX_WORDS; increase and "
|
||||
"recompile");
|
||||
}
|
||||
const WDataInP datap = (reinterpret_cast<EData*>(varDatap));
|
||||
for (int i = 0; i < words; ++i) {
|
||||
@@ -1735,7 +1741,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
} else if (valuep->format == vpiBinStrVal) {
|
||||
valuep->value.str = t_outStr;
|
||||
int bits = varp->packed().elements();
|
||||
CData* datap = (reinterpret_cast<CData*>(varDatap));
|
||||
const CData* datap = (reinterpret_cast<CData*>(varDatap));
|
||||
int i;
|
||||
if (bits > t_outStrSz) {
|
||||
// limit maximum size of output to size of buffer to prevent overrun.
|
||||
@@ -1743,12 +1749,12 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
VL_VPI_WARNING_(
|
||||
__FILE__, __LINE__,
|
||||
"%s: Truncating string value of %s for %s"
|
||||
" as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)",
|
||||
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
|
||||
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
|
||||
VL_MULS_MAX_WORDS, bits);
|
||||
VL_VALUE_STRING_MAX_WORDS, bits);
|
||||
}
|
||||
for (i = 0; i < bits; ++i) {
|
||||
char val = (datap[i >> 3] >> (i & 7)) & 1;
|
||||
const char val = (datap[i >> 3] >> (i & 7)) & 1;
|
||||
t_outStr[bits - i - 1] = val ? '1' : '0';
|
||||
}
|
||||
t_outStr[i] = '\0';
|
||||
@@ -1757,16 +1763,16 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
valuep->value.str = t_outStr;
|
||||
int chars = (varp->packed().elements() + 2) / 3;
|
||||
const int bytes = VL_BYTES_I(varp->packed().elements());
|
||||
CData* datap = (reinterpret_cast<CData*>(varDatap));
|
||||
const CData* datap = (reinterpret_cast<CData*>(varDatap));
|
||||
int i;
|
||||
if (chars > t_outStrSz) {
|
||||
// limit maximum size of output to size of buffer to prevent overrun.
|
||||
VL_VPI_WARNING_(
|
||||
__FILE__, __LINE__,
|
||||
"%s: Truncating string value of %s for %s"
|
||||
" as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)",
|
||||
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
|
||||
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
|
||||
VL_MULS_MAX_WORDS, chars);
|
||||
VL_VALUE_STRING_MAX_WORDS, chars);
|
||||
chars = t_outStrSz;
|
||||
}
|
||||
for (i = 0; i < chars; ++i) {
|
||||
@@ -1782,7 +1788,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
if (i == (chars - 1)) {
|
||||
// most signifcant char, mask off non existant bits when vector
|
||||
// size is not a multiple of 3
|
||||
unsigned int rem = varp->packed().elements() % 3;
|
||||
const unsigned int rem = varp->packed().elements() % 3;
|
||||
if (rem) {
|
||||
// generate bit mask & zero non existant bits
|
||||
val &= (1 << rem) - 1;
|
||||
@@ -1815,16 +1821,16 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
} else if (valuep->format == vpiHexStrVal) {
|
||||
valuep->value.str = t_outStr;
|
||||
int chars = (varp->packed().elements() + 3) >> 2;
|
||||
CData* datap = (reinterpret_cast<CData*>(varDatap));
|
||||
const CData* datap = (reinterpret_cast<CData*>(varDatap));
|
||||
int i;
|
||||
if (chars > t_outStrSz) {
|
||||
// limit maximum size of output to size of buffer to prevent overrun.
|
||||
VL_VPI_WARNING_(
|
||||
__FILE__, __LINE__,
|
||||
"%s: Truncating string value of %s for %s"
|
||||
" as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)",
|
||||
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
|
||||
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
|
||||
VL_MULS_MAX_WORDS, chars);
|
||||
VL_VALUE_STRING_MAX_WORDS, chars);
|
||||
chars = t_outStrSz;
|
||||
}
|
||||
for (i = 0; i < chars; ++i) {
|
||||
@@ -1849,16 +1855,16 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
} else {
|
||||
valuep->value.str = t_outStr;
|
||||
int bytes = VL_BYTES_I(varp->packed().elements());
|
||||
CData* datap = (reinterpret_cast<CData*>(varDatap));
|
||||
const CData* datap = (reinterpret_cast<CData*>(varDatap));
|
||||
int i;
|
||||
if (bytes > t_outStrSz) {
|
||||
// limit maximum size of output to size of buffer to prevent overrun.
|
||||
VL_VPI_WARNING_(
|
||||
__FILE__, __LINE__,
|
||||
"%s: Truncating string value of %s for %s"
|
||||
" as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)",
|
||||
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname,
|
||||
t_outStrSz, VL_MULS_MAX_WORDS, bytes);
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__,
|
||||
"%s: Truncating string value of %s for %s"
|
||||
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than "
|
||||
"required (%d)",
|
||||
__func__, VerilatedVpiError::strFromVpiVal(valuep->format),
|
||||
fullname, t_outStrSz, VL_VALUE_STRING_MAX_WORDS, bytes);
|
||||
bytes = t_outStrSz;
|
||||
}
|
||||
for (i = 0; i < bytes; ++i) {
|
||||
@@ -1893,13 +1899,13 @@ void vpi_get_value(vpiHandle object, p_vpi_value valuep) {
|
||||
VL_VPI_ERROR_RESET_();
|
||||
if (VL_UNLIKELY(!valuep)) return;
|
||||
|
||||
if (VerilatedVpioVar* vop = VerilatedVpioVar::castp(object)) {
|
||||
if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) {
|
||||
vl_get_value(vop->varp(), vop->varDatap(), valuep, vop->fullname());
|
||||
return;
|
||||
} else if (const VerilatedVpioParam* const vop = VerilatedVpioParam::castp(object)) {
|
||||
vl_get_value(vop->varp(), vop->varDatap(), valuep, vop->fullname());
|
||||
return;
|
||||
} else if (VerilatedVpioConst* vop = VerilatedVpioConst::castp(object)) {
|
||||
} else if (const VerilatedVpioConst* const vop = VerilatedVpioConst::castp(object)) {
|
||||
if (valuep->format == vpiIntVal) {
|
||||
valuep->value.integer = vop->num();
|
||||
return;
|
||||
@@ -1964,9 +1970,9 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
} else if (valuep->format == vpiBinStrVal) {
|
||||
const int bits = vop->varp()->packed().elements();
|
||||
const int len = std::strlen(valuep->value.str);
|
||||
CData* datap = (reinterpret_cast<CData*>(vop->varDatap()));
|
||||
CData* const datap = (reinterpret_cast<CData*>(vop->varDatap()));
|
||||
for (int i = 0; i < bits; ++i) {
|
||||
char set = (i < len) ? (valuep->value.str[len - i - 1] == '1') : 0;
|
||||
const char set = (i < len) ? (valuep->value.str[len - i - 1] == '1') : 0;
|
||||
// zero bits 7:1 of byte when assigning to bit 0, else
|
||||
// or in 1 if bit set
|
||||
if (i & 7) {
|
||||
@@ -1980,7 +1986,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
const int chars = (vop->varp()->packed().elements() + 2) / 3;
|
||||
const int bytes = VL_BYTES_I(vop->varp()->packed().elements());
|
||||
const int len = std::strlen(valuep->value.str);
|
||||
CData* datap = (reinterpret_cast<CData*>(vop->varDatap()));
|
||||
CData* const datap = (reinterpret_cast<CData*>(vop->varDatap()));
|
||||
div_t idx;
|
||||
datap[0] = 0; // reset zero'th byte
|
||||
for (int i = 0; i < chars; ++i) {
|
||||
@@ -1991,7 +1997,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
idx = div(i * 3, 8);
|
||||
if (i < len) {
|
||||
// ignore illegal chars
|
||||
char digit = valuep->value.str[len - i - 1];
|
||||
const char digit = valuep->value.str[len - i - 1];
|
||||
if (digit >= '0' && digit <= '7') {
|
||||
val.half = digit - '0';
|
||||
} else {
|
||||
@@ -2058,8 +2064,8 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
}
|
||||
} else if (valuep->format == vpiHexStrVal) {
|
||||
const int chars = (vop->varp()->packed().elements() + 3) >> 2;
|
||||
CData* datap = (reinterpret_cast<CData*>(vop->varDatap()));
|
||||
char* val = valuep->value.str;
|
||||
CData* const datap = (reinterpret_cast<CData*>(vop->varDatap()));
|
||||
const char* val = valuep->value.str;
|
||||
// skip hex ident if one is detected at the start of the string
|
||||
if (val[0] == '0' && (val[1] == 'x' || val[1] == 'X')) val += 2;
|
||||
const int len = std::strlen(val);
|
||||
@@ -2067,7 +2073,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
char hex;
|
||||
// compute hex digit value
|
||||
if (i < len) {
|
||||
char digit = val[len - i - 1];
|
||||
const char digit = val[len - i - 1];
|
||||
if (digit >= '0' && digit <= '9') {
|
||||
hex = digit - '0';
|
||||
} else if (digit >= 'a' && digit <= 'f') {
|
||||
@@ -2099,7 +2105,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
} else if (valuep->format == vpiStringVal) {
|
||||
const int bytes = VL_BYTES_I(vop->varp()->packed().elements());
|
||||
const int len = std::strlen(valuep->value.str);
|
||||
CData* datap = (reinterpret_cast<CData*>(vop->varDatap()));
|
||||
CData* const datap = (reinterpret_cast<CData*>(vop->varDatap()));
|
||||
for (int i = 0; i < bytes; ++i) {
|
||||
// prepend with 0 values before placing string the least significant bytes
|
||||
datap[i] = (i < len) ? valuep->value.str[len - i - 1] : 0;
|
||||
@@ -2161,7 +2167,7 @@ void vpi_get_time(vpiHandle object, p_vpi_time time_p) {
|
||||
return;
|
||||
} else if (time_p->type == vpiScaledRealTime) {
|
||||
double dtime = VL_TIME_D();
|
||||
if (VerilatedVpioScope* vop = VerilatedVpioScope::castp(object)) {
|
||||
if (const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object)) {
|
||||
const int scalePow10
|
||||
= Verilated::threadContextp()->timeprecision() - vop->scopep()->timeunit();
|
||||
const double scale = vl_time_multiplier(scalePow10); // e.g. 0.0001
|
||||
@@ -2271,7 +2277,7 @@ PLI_INT32 vpi_free_object(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);
|
||||
VerilatedVpio* const vop = VerilatedVpio::castp(object);
|
||||
VL_VPI_ERROR_RESET_();
|
||||
if (VL_UNLIKELY(!vop)) return 0;
|
||||
VL_DO_DANGLING(delete vop, vop);
|
||||
@@ -2281,7 +2287,7 @@ PLI_INT32 vpi_release_handle(vpiHandle object) {
|
||||
PLI_INT32 vpi_get_vlog_info(p_vpi_vlog_info vlog_info_p) VL_MT_SAFE {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
VL_VPI_ERROR_RESET_();
|
||||
auto argc_argv = Verilated::threadContextp()->impp()->argc_argv();
|
||||
const auto argc_argv = Verilated::threadContextp()->impp()->argc_argv();
|
||||
vlog_info_p->argc = argc_argv.first;
|
||||
vlog_info_p->argv = argc_argv.second;
|
||||
vlog_info_p->product = const_cast<PLI_BYTE8*>(Verilated::productName());
|
||||
|
||||
+10
-3
@@ -333,7 +333,8 @@ typedef signed __int32 ssize_t; ///< signed size_t; returned from read()
|
||||
# include <stdint.h> // Linux and most flavors
|
||||
# include <sys/types.h> // __WORDSIZE
|
||||
# include <unistd.h> // ssize_t
|
||||
typedef char vlsint8_t; ///< 8-bit signed type
|
||||
// Arm64 gcc 9.3.0 defaults to unsigned char, not signed char
|
||||
typedef signed char vlsint8_t; ///< 8-bit signed type
|
||||
typedef uint8_t vluint8_t; ///< 8-bit unsigned type
|
||||
typedef short vlsint16_t; ///< 16-bit signed type
|
||||
typedef uint16_t vluint16_t; ///< 16-bit unsigned type
|
||||
@@ -421,7 +422,8 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
||||
// Verilated function size macros
|
||||
|
||||
#define VL_MULS_MAX_WORDS 16 ///< Max size in words of MULS operation
|
||||
#define VL_TO_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation
|
||||
#define VL_VALUE_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation
|
||||
#define VL_VALUE_STRING_MAX_CHARS (VL_VALUE_STRING_MAX_WORDS * VL_EDATASIZE / VL_BYTESIZE)
|
||||
|
||||
//=========================================================================
|
||||
// Base macros
|
||||
@@ -471,7 +473,12 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
||||
(val) = ((vluint64_t)lo) | (((vluint64_t)hi) << 32); \
|
||||
}
|
||||
#elif defined(__aarch64__)
|
||||
# define VL_RDTSC(val) asm volatile("mrs %[rt],PMCCNTR_EL0" : [rt] "=r"(val));
|
||||
// 1 GHz virtual system timer on SBSA level 5 compliant systems, else often 100 MHz
|
||||
# define VL_RDTSC(val) \
|
||||
{ \
|
||||
asm volatile("isb" : : : "memory"); \
|
||||
asm volatile("mrs %[rt],CNTVCT_EL0" : [rt] "=r"(val)); \
|
||||
}
|
||||
#else
|
||||
// We just silently ignore unknown OSes, as only leads to missing statistics
|
||||
# define VL_RDTSC(val) (val) = 0;
|
||||
|
||||
@@ -23,10 +23,6 @@ source_globs("include/*/*.c")
|
||||
# Note *'s are removed when using fastcov
|
||||
remove_source("/usr/*")
|
||||
remove_source("*/include/sysc/*")
|
||||
remove_source("*/V3ClkGater.cpp")
|
||||
remove_source("*/V3ClkGater.h")
|
||||
remove_source("*/V3GraphDfa.cpp")
|
||||
remove_source("*/V3GraphDfa.h")
|
||||
remove_source("*/V3Lexer_pregen.yy.cpp")
|
||||
remove_source("*/V3PreLex_pregen.yy.cpp")
|
||||
remove_source("*/verilog.c")
|
||||
|
||||
+2
-2
@@ -66,11 +66,11 @@ def cwrite(filename):
|
||||
fh.write(" auto* gp = &m_graph;\n")
|
||||
for ver in sorted(Vertexes, key=lambda ver: ver['num']):
|
||||
fh.write(
|
||||
" auto* %s = new V3GraphTestVertex(gp, \"%s\"); if (%s) {}\n"
|
||||
" auto* %s = new V3GraphTestVertex{gp, \"%s\"}; if (%s) {}\n"
|
||||
% (ver['name'], ver['name'], ver['name']))
|
||||
fh.write("\n")
|
||||
for edge in Edges:
|
||||
fh.write(" new V3GraphEdge(gp, %s, %s, %s, %s);\n" %
|
||||
fh.write(" new V3GraphEdge{gp, %s, %s, %s, %s};\n" %
|
||||
(edge['from'], edge['to'], edge['weight'],
|
||||
"true" if edge['cutable'] else "false"))
|
||||
fh.write("}\n")
|
||||
|
||||
+4
-2
@@ -169,6 +169,7 @@ RAW_OBJS = \
|
||||
V3Clean.o \
|
||||
V3Clock.o \
|
||||
V3Combine.o \
|
||||
V3Common.o \
|
||||
V3Config.o \
|
||||
V3Const__gen.o \
|
||||
V3Coverage.o \
|
||||
@@ -179,10 +180,11 @@ RAW_OBJS = \
|
||||
V3DepthBlock.o \
|
||||
V3Descope.o \
|
||||
V3DupFinder.o \
|
||||
V3EmitC.o \
|
||||
V3EmitCBase.o \
|
||||
V3EmitCConstPool.o \
|
||||
V3EmitCFunc.o \
|
||||
V3EmitCHeaders.o \
|
||||
V3EmitCImp.o \
|
||||
V3EmitCInlines.o \
|
||||
V3EmitCMain.o \
|
||||
V3EmitCMake.o \
|
||||
@@ -201,7 +203,6 @@ RAW_OBJS = \
|
||||
V3Graph.o \
|
||||
V3GraphAlg.o \
|
||||
V3GraphAcyc.o \
|
||||
V3GraphDfa.o \
|
||||
V3GraphPathChecker.o \
|
||||
V3GraphTest.o \
|
||||
V3Hash.o \
|
||||
@@ -254,6 +255,7 @@ RAW_OBJS = \
|
||||
V3Undriven.o \
|
||||
V3Unknown.o \
|
||||
V3Unroll.o \
|
||||
V3VariableOrder.o \
|
||||
V3Waiver.o \
|
||||
V3Width.o \
|
||||
V3WidthSel.o \
|
||||
|
||||
+44
-44
@@ -50,8 +50,8 @@ public:
|
||||
enum VertexType : uint8_t { VT_BLOCK, VT_BRANCH, VT_OUTPUT };
|
||||
|
||||
private:
|
||||
string m_name; // Only used for .dot file generation
|
||||
VertexType m_type; // Vertex type (BLOCK/BRANCH/OUTPUT)
|
||||
const string m_name; // Only used for .dot file generation
|
||||
const VertexType m_type; // Vertex type (BLOCK/BRANCH/OUTPUT)
|
||||
|
||||
string typestr() const { // "
|
||||
switch (m_type) {
|
||||
@@ -64,11 +64,11 @@ private:
|
||||
|
||||
public:
|
||||
LatchDetectGraphVertex(V3Graph* graphp, const string& name, VertexType type = VT_BLOCK)
|
||||
: V3GraphVertex(graphp)
|
||||
, m_name(name)
|
||||
, m_type(type) {}
|
||||
virtual string name() const { return m_name + " " + typestr(); }
|
||||
virtual string dotColor() const { return user() ? "green" : "black"; }
|
||||
: V3GraphVertex{graphp}
|
||||
, m_name{name}
|
||||
, m_type{type} {}
|
||||
virtual string name() const override { return m_name + " " + typestr(); }
|
||||
virtual string dotColor() const override { return user() ? "green" : "black"; }
|
||||
virtual int type() const { return m_type; }
|
||||
};
|
||||
|
||||
@@ -108,9 +108,10 @@ protected:
|
||||
}
|
||||
break;
|
||||
case LatchDetectGraphVertex::VT_BRANCH: // (AND of both sibling)
|
||||
// A BRANCH vertex always has exactly 2 siblings
|
||||
LatchDetectGraphVertex* ifp = castVertexp(vertexp->outBeginp()->top());
|
||||
LatchDetectGraphVertex* elsp = castVertexp(vertexp->outBeginp()->outNextp()->top());
|
||||
// A BRANCH vertex always has exactly 2 siblings
|
||||
LatchDetectGraphVertex* const ifp = castVertexp(vertexp->outBeginp()->top());
|
||||
LatchDetectGraphVertex* const elsp
|
||||
= castVertexp(vertexp->outBeginp()->outNextp()->top());
|
||||
result = latchCheckInternal(ifp) && latchCheckInternal(elsp);
|
||||
break;
|
||||
}
|
||||
@@ -120,7 +121,7 @@ protected:
|
||||
|
||||
public:
|
||||
LatchDetectGraph() { clear(); }
|
||||
~LatchDetectGraph() { clear(); }
|
||||
virtual ~LatchDetectGraph() override { clear(); }
|
||||
// ACCESSORS
|
||||
LatchDetectGraphVertex* currentp() { return m_curVertexp; }
|
||||
void currentp(LatchDetectGraphVertex* vertex) { m_curVertexp = vertex; }
|
||||
@@ -142,17 +143,17 @@ public:
|
||||
// Add a new control path and connect it to its parent
|
||||
LatchDetectGraphVertex* addPathVertex(LatchDetectGraphVertex* parent, const string& name,
|
||||
bool branch = false) {
|
||||
m_curVertexp = new LatchDetectGraphVertex(this, name,
|
||||
m_curVertexp = new LatchDetectGraphVertex{this, name,
|
||||
branch ? LatchDetectGraphVertex::VT_BRANCH
|
||||
: LatchDetectGraphVertex::VT_BLOCK);
|
||||
new V3GraphEdge(this, parent, m_curVertexp, 1);
|
||||
: LatchDetectGraphVertex::VT_BLOCK};
|
||||
new V3GraphEdge{this, parent, m_curVertexp, 1};
|
||||
return m_curVertexp;
|
||||
}
|
||||
// Add a new output variable vertex and store a pointer to it in the user1 field of the
|
||||
// variables AstNode
|
||||
LatchDetectGraphVertex* addOutputVertex(AstVarRef* nodep) {
|
||||
LatchDetectGraphVertex* outVertexp
|
||||
= new LatchDetectGraphVertex(this, nodep->name(), LatchDetectGraphVertex::VT_OUTPUT);
|
||||
LatchDetectGraphVertex* const outVertexp
|
||||
= new LatchDetectGraphVertex{this, nodep->name(), LatchDetectGraphVertex::VT_OUTPUT};
|
||||
nodep->varp()->user1p(outVertexp);
|
||||
m_outputs.push_back(nodep);
|
||||
return outVertexp;
|
||||
@@ -172,7 +173,7 @@ public:
|
||||
void latchCheck(AstNode* nodep, bool latch_expected) {
|
||||
bool latch_detected = false;
|
||||
for (const auto& vrp : m_outputs) {
|
||||
LatchDetectGraphVertex* vertp = castVertexp(vrp->varp()->user1p());
|
||||
LatchDetectGraphVertex* const vertp = castVertexp(vrp->varp()->user1p());
|
||||
vertp->user(true); // Identify the output vertex we are checking paths _to_
|
||||
if (!latchCheckInternal(castVertexp(verticesBeginp()))) latch_detected = true;
|
||||
if (latch_detected && !latch_expected) {
|
||||
@@ -262,7 +263,7 @@ public:
|
||||
// Return a sentree in this scope that matches given sense list.
|
||||
|
||||
AstActive* activep = nullptr;
|
||||
AstSenTree* activeSenp = m_activeSens.find(sensesp);
|
||||
AstSenTree* const activeSenp = m_activeSens.find(sensesp);
|
||||
if (activeSenp) {
|
||||
const auto it = m_activeMap.find(activeSenp);
|
||||
UASSERT(it != m_activeMap.end(), "Corrupt active map");
|
||||
@@ -271,7 +272,7 @@ public:
|
||||
|
||||
// Not found, form a new one
|
||||
if (!activep) {
|
||||
AstSenTree* newsenp = sensesp->cloneTree(false);
|
||||
AstSenTree* const newsenp = sensesp->cloneTree(false);
|
||||
activep = new AstActive(fl, "sequent", newsenp);
|
||||
activep->sensesStorep(activep->sensesp());
|
||||
UINFO(8, " New ACTIVE " << activep << endl);
|
||||
@@ -298,20 +299,20 @@ private:
|
||||
// NODE STATE
|
||||
// Input:
|
||||
// AstVar::user1p // V2LatchGraphVertex* The vertex handling this node
|
||||
AstUser1InUse m_inuser1;
|
||||
const AstUser1InUse m_inuser1;
|
||||
// STATE
|
||||
LatchDetectGraph m_graph; // Graph used to detect latches in combo always
|
||||
// VISITORS
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
AstVar* varp = nodep->varp();
|
||||
const AstVar* const varp = nodep->varp();
|
||||
if (nodep->access().isWriteOrRW() && varp->isSignal() && !varp->isUsedLoopIdx()) {
|
||||
m_graph.addAssignment(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeIf* nodep) {
|
||||
if (!nodep->isBoundsCheck()) {
|
||||
LatchDetectGraphVertex* parentp = m_graph.currentp();
|
||||
LatchDetectGraphVertex* branchp = m_graph.addPathVertex(parentp, "BRANCH", true);
|
||||
LatchDetectGraphVertex* const parentp = m_graph.currentp();
|
||||
LatchDetectGraphVertex* const branchp = m_graph.addPathVertex(parentp, "BRANCH", true);
|
||||
m_graph.addPathVertex(branchp, "IF");
|
||||
iterateAndNextNull(nodep->ifsp());
|
||||
m_graph.addPathVertex(branchp, "ELSE");
|
||||
@@ -340,9 +341,9 @@ public:
|
||||
enum CheckType : uint8_t { CT_SEQ, CT_COMBO, CT_INITIAL, CT_LATCH };
|
||||
|
||||
private:
|
||||
CheckType m_check; // Combo logic or other
|
||||
AstNode* m_alwaysp; // Always we're under
|
||||
AstNode* m_assignp = nullptr; // In assign
|
||||
const CheckType m_check; // Combo logic or other
|
||||
const AstNode* const m_alwaysp; // Always we're under
|
||||
const AstNode* m_assignp = nullptr; // In assign
|
||||
// VISITORS
|
||||
virtual void visit(AstAssignDly* nodep) override {
|
||||
if (m_check != CT_SEQ) {
|
||||
@@ -355,7 +356,7 @@ private:
|
||||
} else if (m_check == CT_LATCH) {
|
||||
// Suppress. Shouldn't matter that the interior of the latch races
|
||||
} else if (!(VN_IS(nodep->lhsp(), VarRef)
|
||||
&& VN_CAST(nodep->lhsp(), VarRef)->varp()->isLatched())) {
|
||||
&& VN_AS(nodep->lhsp(), VarRef)->varp()->isLatched())) {
|
||||
nodep->v3warn(COMBDLY, "Delayed assignments (<=) in non-clocked"
|
||||
" (non flop or latch) block\n"
|
||||
<< nodep->warnMore()
|
||||
@@ -363,8 +364,8 @@ private:
|
||||
// Conversely, we could also suggest latches use delayed assignments, as
|
||||
// recommended by Cliff Cummings?
|
||||
}
|
||||
AstNode* newp = new AstAssign(nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
|
||||
nodep->rhsp()->unlinkFrBack());
|
||||
AstNode* const newp = new AstAssign(nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
|
||||
nodep->rhsp()->unlinkFrBack());
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
@@ -377,7 +378,7 @@ private:
|
||||
}
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep) override {
|
||||
AstVar* varp = nodep->varp();
|
||||
const AstVar* const varp = nodep->varp();
|
||||
if (m_check == CT_SEQ && m_assignp && !varp->isUsedLoopIdx() // Ignore loop indices
|
||||
&& !varp->isTemp()) {
|
||||
// Allow turning off warnings on the always, or the variable also
|
||||
@@ -437,29 +438,29 @@ private:
|
||||
virtual void visit(AstInitial* nodep) override {
|
||||
// Relink to IACTIVE, unless already under it
|
||||
UINFO(4, " INITIAL " << nodep << endl);
|
||||
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_INITIAL);
|
||||
AstActive* wantactivep = m_namer.getIActive(nodep->fileline());
|
||||
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
|
||||
AstActive* const wantactivep = m_namer.getIActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
}
|
||||
virtual void visit(AstAssignAlias* nodep) override {
|
||||
// Relink to CACTIVE, unless already under it
|
||||
UINFO(4, " ASSIGNW " << nodep << endl);
|
||||
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
AstActive* const wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
}
|
||||
virtual void visit(AstAssignW* nodep) override {
|
||||
// Relink to CACTIVE, unless already under it
|
||||
UINFO(4, " ASSIGNW " << nodep << endl);
|
||||
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
AstActive* const wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
}
|
||||
virtual void visit(AstCoverToggle* nodep) override {
|
||||
// Relink to CACTIVE, unless already under it
|
||||
UINFO(4, " COVERTOGGLE " << nodep << endl);
|
||||
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
AstActive* const wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
}
|
||||
@@ -470,7 +471,7 @@ private:
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_INITIAL);
|
||||
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
|
||||
if (!m_scopeFinalp) {
|
||||
m_scopeFinalp = new AstCFunc(
|
||||
nodep->fileline(), "_final_" + m_namer.scopep()->nameDotless(), m_namer.scopep());
|
||||
@@ -489,8 +490,7 @@ private:
|
||||
// METHODS
|
||||
void visitAlways(AstNode* nodep, AstSenTree* oldsensesp, VAlwaysKwd kwd) {
|
||||
// Move always to appropriate ACTIVE based on its sense list
|
||||
if (oldsensesp && oldsensesp->sensesp() && VN_IS(oldsensesp->sensesp(), SenItem)
|
||||
&& VN_CAST(oldsensesp->sensesp(), SenItem)->isNever()) {
|
||||
if (oldsensesp && oldsensesp->sensesp() && oldsensesp->sensesp()->isNever()) {
|
||||
// Never executing. Kill it.
|
||||
UASSERT_OBJ(!oldsensesp->sensesp()->nextp(), nodep,
|
||||
"Never senitem should be alone, else the never should be eliminated.");
|
||||
@@ -540,14 +540,14 @@ private:
|
||||
|
||||
// Warn and/or convert any delayed assignments
|
||||
if (combo && !sequent) {
|
||||
ActiveLatchCheckVisitor latchvisitor(nodep, kwd);
|
||||
const ActiveLatchCheckVisitor latchvisitor{nodep, kwd};
|
||||
if (kwd == VAlwaysKwd::ALWAYS_LATCH) {
|
||||
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_LATCH);
|
||||
ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_LATCH};
|
||||
} else {
|
||||
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_COMBO);
|
||||
ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_COMBO};
|
||||
}
|
||||
} else if (!combo && sequent) {
|
||||
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_SEQ);
|
||||
ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_SEQ};
|
||||
}
|
||||
}
|
||||
virtual void visit(AstAlways* nodep) override {
|
||||
@@ -578,7 +578,7 @@ private:
|
||||
}
|
||||
virtual void visit(AstSenItem* nodep) override {
|
||||
if (nodep->varrefp()) {
|
||||
if (AstBasicDType* basicp = nodep->varrefp()->dtypep()->basicp()) {
|
||||
if (const AstBasicDType* const basicp = nodep->varrefp()->dtypep()->basicp()) {
|
||||
if (basicp->isEventValue()) {
|
||||
// Events need to be treated as active high so we only activate on event being
|
||||
// 1
|
||||
@@ -620,6 +620,6 @@ public:
|
||||
|
||||
void V3Active::activeAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ ActiveVisitor visitor(nodep); } // Destruct before checking
|
||||
{ ActiveVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("active", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+8
-16
@@ -42,22 +42,15 @@ private:
|
||||
// AstNode::user() bool. True if processed
|
||||
// Each call to V3Const::constify
|
||||
// AstNode::user4() Used by V3Const::constify, called below
|
||||
AstUser1InUse m_inuser1;
|
||||
const AstUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
AstTopScope* m_topscopep = nullptr; // Top scope for adding sentrees under
|
||||
SenTreeFinder m_finder; // Find global sentree's and add them
|
||||
SenTreeFinder m_finder; // Find global sentree's / add them under the AstTopScope
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstTopScope* nodep) override {
|
||||
m_topscopep = nodep;
|
||||
m_finder.init(m_topscopep);
|
||||
iterateChildren(nodep);
|
||||
m_topscopep = nullptr;
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
// Create required actives and add to module
|
||||
// We can start ordering at a module, or a scope
|
||||
@@ -68,10 +61,9 @@ private:
|
||||
UINFO(4, " ACTIVE " << nodep << endl);
|
||||
// Remove duplicate clocks and such; sensesp() may change!
|
||||
V3Const::constifyExpensiveEdit(nodep);
|
||||
AstSenTree* sensesp = nodep->sensesp();
|
||||
AstSenTree* const sensesp = nodep->sensesp();
|
||||
UASSERT_OBJ(sensesp, nodep, "nullptr");
|
||||
if (sensesp->sensesp() && VN_IS(sensesp->sensesp(), SenItem)
|
||||
&& VN_CAST(sensesp->sensesp(), SenItem)->isNever()) {
|
||||
if (sensesp->sensesp() && sensesp->sensesp()->isNever()) {
|
||||
// Never executing. Kill it.
|
||||
UASSERT_OBJ(!sensesp->sensesp()->nextp(), nodep,
|
||||
"Never senitem should be alone, else the never should be eliminated.");
|
||||
@@ -80,16 +72,16 @@ private:
|
||||
}
|
||||
// Copy combo tree to settlement tree with duplicated statements
|
||||
if (sensesp->hasCombo()) {
|
||||
AstSenTree* newsentreep = new AstSenTree(
|
||||
AstSenTree* const newsentreep = new AstSenTree(
|
||||
nodep->fileline(), new AstSenItem(nodep->fileline(), AstSenItem::Settle()));
|
||||
AstActive* newp = new AstActive(nodep->fileline(), "settle", newsentreep);
|
||||
AstActive* const newp = new AstActive(nodep->fileline(), "settle", newsentreep);
|
||||
newp->sensesStorep(newsentreep);
|
||||
if (nodep->stmtsp()) newp->addStmtsp(nodep->stmtsp()->cloneTree(true));
|
||||
nodep->addNextHere(newp);
|
||||
}
|
||||
// Move the SENTREE for each active up to the global level.
|
||||
// This way we'll easily see what clock domains are identical
|
||||
AstSenTree* wantp = m_finder.getSenTree(sensesp);
|
||||
AstSenTree* const wantp = m_finder.getSenTree(sensesp);
|
||||
UINFO(4, " lookdone\n");
|
||||
if (wantp != sensesp) {
|
||||
// Move the active's contents to the other active
|
||||
@@ -136,6 +128,6 @@ public:
|
||||
|
||||
void V3ActiveTop::activeTopAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ ActiveTopVisitor visitor(nodep); } // Destruct before checking
|
||||
{ ActiveTopVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("activetop", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+43
-42
@@ -20,7 +20,6 @@
|
||||
#include "V3Global.h"
|
||||
#include "V3Assert.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3GraphDfa.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
//######################################################################
|
||||
@@ -31,11 +30,11 @@ private:
|
||||
// NODE STATE/TYPES
|
||||
// Cleared on netlist
|
||||
// AstNode::user() -> bool. True if processed
|
||||
AstUser1InUse m_inuser1;
|
||||
const AstUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp = nullptr; // Last module
|
||||
AstBegin* m_beginp = nullptr; // Last begin
|
||||
const AstBegin* m_beginp = nullptr; // Last begin
|
||||
unsigned m_monitorNum = 0; // Global $monitor numbering (not per module)
|
||||
AstVar* m_monitorNumVarp = nullptr; // $monitor number variable
|
||||
AstVar* m_monitorOffVarp = nullptr; // $monitoroff variable
|
||||
@@ -56,14 +55,14 @@ private:
|
||||
nodep->displayType(AstDisplayType::DT_WRITE);
|
||||
nodep->fmtp()->text(assertDisplayMessage(nodep, prefix, nodep->fmtp()->text()));
|
||||
// cppcheck-suppress nullPointer
|
||||
AstNode* timenewp = new AstTime(nodep->fileline(), m_modp->timeunit());
|
||||
if (AstNode* timesp = nodep->fmtp()->exprsp()) {
|
||||
AstNode* const timenewp = new AstTime(nodep->fileline(), m_modp->timeunit());
|
||||
if (AstNode* const timesp = nodep->fmtp()->exprsp()) {
|
||||
timesp->unlinkFrBackWithNext();
|
||||
timenewp->addNext(timesp);
|
||||
}
|
||||
nodep->fmtp()->addExprsp(timenewp);
|
||||
if (!nodep->fmtp()->scopeNamep() && nodep->fmtp()->formatScopeTracking()) {
|
||||
nodep->fmtp()->scopeNamep(new AstScopeName(nodep->fileline()));
|
||||
nodep->fmtp()->scopeNamep(new AstScopeName{nodep->fileline(), true});
|
||||
}
|
||||
}
|
||||
AstVarRef* newMonitorNumVarRefp(AstNode* nodep, VAccess access) {
|
||||
@@ -89,8 +88,8 @@ private:
|
||||
AstNode* newIfAssertOn(AstNode* nodep, bool force) {
|
||||
// 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(
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNode* const newp = new AstIf(
|
||||
fl,
|
||||
(force ? new AstConst(fl, AstConst::BitTrue())
|
||||
: // If assertions are off, have constant propagation rip them out later
|
||||
@@ -106,10 +105,10 @@ private:
|
||||
|
||||
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,
|
||||
nullptr, nullptr);
|
||||
AstDisplay* const dispp = new AstDisplay(nodep->fileline(), AstDisplayType::DT_ERROR,
|
||||
message, nullptr, nullptr);
|
||||
dispp->fmtp()->timeunit(m_modp->timeunit());
|
||||
AstNode* bodysp = dispp;
|
||||
AstNode* const bodysp = dispp;
|
||||
replaceDisplay(dispp, "%%Error"); // Convert to standard DISPLAY format
|
||||
bodysp->addNext(new AstStop(nodep->fileline(), true));
|
||||
return bodysp;
|
||||
@@ -124,8 +123,8 @@ private:
|
||||
void newPslAssertion(AstNodeCoverOrAssert* nodep, AstNode* failsp) {
|
||||
if (m_beginp && nodep->name() == "") nodep->name(m_beginp->name());
|
||||
|
||||
AstNode* propp = nodep->propp()->unlinkFrBackWithNext();
|
||||
AstSenTree* sentreep = nodep->sentreep();
|
||||
AstNode* const propp = nodep->propp()->unlinkFrBackWithNext();
|
||||
AstSenTree* const sentreep = nodep->sentreep();
|
||||
const string& message = nodep->name();
|
||||
AstNode* passsp = nodep->passsp();
|
||||
if (passsp) passsp->unlinkFrBackWithNext();
|
||||
@@ -141,13 +140,13 @@ private:
|
||||
AstNode* bodysp = nullptr;
|
||||
bool selfDestruct = false;
|
||||
AstIf* ifp = nullptr;
|
||||
if (AstCover* snodep = VN_CAST(nodep, Cover)) {
|
||||
if (const AstCover* const snodep = VN_CAST(nodep, Cover)) {
|
||||
++m_statCover;
|
||||
if (!v3Global.opt.coverageUser()) {
|
||||
selfDestruct = true;
|
||||
} else {
|
||||
// V3Coverage assigned us a bucket to increment.
|
||||
AstCoverInc* covincp = VN_CAST(snodep->coverincp(), CoverInc);
|
||||
AstCoverInc* const covincp = VN_AS(snodep->coverincp(), CoverInc);
|
||||
UASSERT_OBJ(covincp, snodep, "Missing AstCoverInc under assertion");
|
||||
covincp->unlinkFrBackWithNext(); // next() might have AstAssign for trace
|
||||
if (message != "") covincp->declp()->comment(message);
|
||||
@@ -199,13 +198,13 @@ private:
|
||||
virtual void visit(AstIf* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
if (nodep->uniquePragma() || nodep->unique0Pragma()) {
|
||||
AstNodeIf* ifp = nodep;
|
||||
const AstNodeIf* ifp = nodep;
|
||||
AstNode* propp = nullptr;
|
||||
bool hasDefaultElse = false;
|
||||
do {
|
||||
// If this statement ends with 'else if', then nextIf will point to the
|
||||
// nextIf statement. Otherwise it will be null.
|
||||
AstNodeIf* nextifp = dynamic_cast<AstNodeIf*>(ifp->elsesp());
|
||||
const AstNodeIf* const nextifp = dynamic_cast<AstNodeIf*>(ifp->elsesp());
|
||||
iterateAndNextNull(ifp->condp());
|
||||
|
||||
// Recurse into the true case.
|
||||
@@ -217,7 +216,7 @@ private:
|
||||
}
|
||||
|
||||
// Build a bitmask of the true predicates
|
||||
AstNode* predp = ifp->condp()->cloneTree(false);
|
||||
AstNode* const predp = ifp->condp()->cloneTree(false);
|
||||
if (propp) {
|
||||
propp = new AstConcat(nodep->fileline(), predp, propp);
|
||||
} else {
|
||||
@@ -230,7 +229,7 @@ private:
|
||||
ifp = nextifp;
|
||||
} while (ifp);
|
||||
|
||||
AstNode* newifp = nodep->cloneTree(false);
|
||||
AstNode* const newifp = nodep->cloneTree(false);
|
||||
const bool allow_none = nodep->unique0Pragma();
|
||||
|
||||
// Empty case means no property
|
||||
@@ -238,10 +237,11 @@ private:
|
||||
|
||||
// Note: if this ends with an 'else', then we don't need to validate that one of the
|
||||
// predicates evaluates to true.
|
||||
AstNode* ohot = ((allow_none || hasDefaultElse)
|
||||
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
|
||||
: static_cast<AstNode*>(new AstOneHot(nodep->fileline(), propp)));
|
||||
AstIf* checkifp
|
||||
AstNode* const ohot
|
||||
= ((allow_none || hasDefaultElse)
|
||||
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
|
||||
: static_cast<AstNode*>(new AstOneHot(nodep->fileline(), propp)));
|
||||
AstIf* const checkifp
|
||||
= new AstIf(nodep->fileline(), new AstLogNot(nodep->fileline(), ohot),
|
||||
newFireAssert(nodep, "'unique if' statement violated"), newifp);
|
||||
checkifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||
@@ -258,7 +258,7 @@ private:
|
||||
if (!nodep->user1SetOnce()) {
|
||||
bool has_default = false;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
itemp = VN_AS(itemp->nextp(), CaseItem)) {
|
||||
if (itemp->isDefault()) has_default = true;
|
||||
}
|
||||
if (nodep->fullPragma() || nodep->priorityPragma()) {
|
||||
@@ -279,10 +279,10 @@ private:
|
||||
} else {
|
||||
AstNode* propp = nullptr;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
itemp = VN_AS(itemp->nextp(), CaseItem)) {
|
||||
for (AstNode* icondp = itemp->condsp(); icondp; icondp = icondp->nextp()) {
|
||||
AstNode* onep;
|
||||
if (AstInsideRange* rcondp = VN_CAST(icondp, InsideRange)) {
|
||||
if (AstInsideRange* const rcondp = VN_CAST(icondp, InsideRange)) {
|
||||
onep = rcondp->newAndFromInside(nodep->exprp(),
|
||||
rcondp->lhsp()->cloneTree(true),
|
||||
rcondp->rhsp()->cloneTree(true));
|
||||
@@ -306,11 +306,11 @@ private:
|
||||
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::BitFalse());
|
||||
|
||||
const bool allow_none = has_default || nodep->unique0Pragma();
|
||||
AstNode* ohot
|
||||
AstNode* const ohot
|
||||
= (allow_none
|
||||
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
|
||||
: static_cast<AstNode*>(new AstOneHot(nodep->fileline(), propp)));
|
||||
AstIf* ifp = new AstIf(
|
||||
AstIf* const ifp = new AstIf(
|
||||
nodep->fileline(), new AstLogNot(nodep->fileline(), ohot),
|
||||
newFireAssert(nodep,
|
||||
"synthesis parallel_case, but multiple matches found"),
|
||||
@@ -329,22 +329,22 @@ private:
|
||||
if (nodep->ticksp()) {
|
||||
UASSERT_OBJ(VN_IS(nodep->ticksp(), Const), nodep,
|
||||
"Expected constant ticks, checked in V3Width");
|
||||
ticks = VN_CAST(nodep->ticksp(), Const)->toUInt();
|
||||
ticks = VN_AS(nodep->ticksp(), Const)->toUInt();
|
||||
}
|
||||
UASSERT_OBJ(ticks >= 1, nodep, "0 tick should have been checked in V3Width");
|
||||
AstNode* inp = nodep->exprp()->unlinkFrBack();
|
||||
AstVar* invarp = nullptr;
|
||||
AstSenTree* sentreep = nodep->sentreep();
|
||||
AstSenTree* const sentreep = nodep->sentreep();
|
||||
sentreep->unlinkFrBack();
|
||||
AstAlways* alwaysp
|
||||
AstAlways* const alwaysp
|
||||
= new AstAlways(nodep->fileline(), VAlwaysKwd::ALWAYS, sentreep, nullptr);
|
||||
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());
|
||||
AstVar* const outvarp = new AstVar(
|
||||
nodep->fileline(), AstVarType::MODULETEMP,
|
||||
"_Vpast_" + cvtToStr(m_modPastNum++) + "_" + cvtToStr(i), inp->dtypep());
|
||||
m_modp->addStmtp(outvarp);
|
||||
AstNode* assp = new AstAssignDly(
|
||||
AstNode* const assp = new AstAssignDly(
|
||||
nodep->fileline(), new AstVarRef(nodep->fileline(), outvarp, VAccess::WRITE), inp);
|
||||
alwaysp->addStmtp(assp);
|
||||
// if (debug() >= 9) assp->dumpTree(cout, "-ass: ");
|
||||
@@ -378,15 +378,15 @@ private:
|
||||
new AstConst{fl, monNum}};
|
||||
nodep->replaceWith(newsetp);
|
||||
// Add "always_comb if (__VmonitorOn && __VmonitorNum==N) $display(...);"
|
||||
AstNode* stmtsp = nodep;
|
||||
AstIf* ifp = new AstIf{
|
||||
AstNode* const stmtsp = nodep;
|
||||
AstIf* const ifp = new AstIf{
|
||||
fl,
|
||||
new AstLogAnd{fl, new AstLogNot{fl, newMonitorOffVarRefp(nodep, VAccess::READ)},
|
||||
new AstEq{fl, new AstConst{fl, monNum},
|
||||
newMonitorNumVarRefp(nodep, VAccess::READ)}},
|
||||
stmtsp, nullptr};
|
||||
ifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||
AstNode* newp = new AstAlwaysPostponed{fl, ifp};
|
||||
AstNode* const newp = new AstAlwaysPostponed{fl, ifp};
|
||||
m_modp->addStmtp(newp);
|
||||
} else if (nodep->displayType() == AstDisplayType::DT_STROBE) {
|
||||
nodep->displayType(AstDisplayType::DT_DISPLAY);
|
||||
@@ -401,10 +401,11 @@ private:
|
||||
new AstConst{fl, AstConst::BitTrue{}}};
|
||||
nodep->replaceWith(newsetp);
|
||||
// Add "always_comb if (__Vstrobe) begin $display(...); __Vstrobe = '0; end"
|
||||
AstNode* stmtsp = nodep;
|
||||
AstIf* ifp = new AstIf{fl, new AstVarRef{fl, varp, VAccess::READ}, stmtsp, nullptr};
|
||||
AstNode* const stmtsp = nodep;
|
||||
AstIf* const ifp
|
||||
= new AstIf{fl, new AstVarRef{fl, varp, VAccess::READ}, stmtsp, nullptr};
|
||||
ifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||
AstNode* newp = new AstAlwaysPostponed{fl, ifp};
|
||||
AstNode* const newp = new AstAlwaysPostponed{fl, ifp};
|
||||
stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
|
||||
new AstConst{fl, AstConst::BitFalse{}}});
|
||||
m_modp->addStmtp(newp);
|
||||
@@ -474,6 +475,6 @@ public:
|
||||
|
||||
void V3Assert::assertAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ AssertVisitor visitor(nodep); } // Destruct before checking
|
||||
{ AssertVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("assert", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+11
-11
@@ -97,10 +97,10 @@ private:
|
||||
virtual void visit(AstFell* nodep) override {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
iterateChildren(nodep);
|
||||
FileLine* fl = nodep->fileline();
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNode* exprp = nodep->exprp()->unlinkFrBack();
|
||||
if (exprp->width() > 1) exprp = new AstSel(fl, exprp, 0, 1);
|
||||
AstNode* past = new AstPast(fl, exprp, nullptr);
|
||||
AstNode* const past = new AstPast(fl, exprp, nullptr);
|
||||
past->dtypeFrom(exprp);
|
||||
exprp = new AstAnd(fl, past, new AstNot(fl, exprp->cloneTree(false)));
|
||||
exprp->dtypeSetBit();
|
||||
@@ -116,10 +116,10 @@ private:
|
||||
virtual void visit(AstRose* nodep) override {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
iterateChildren(nodep);
|
||||
FileLine* fl = nodep->fileline();
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNode* exprp = nodep->exprp()->unlinkFrBack();
|
||||
if (exprp->width() > 1) exprp = new AstSel(fl, exprp, 0, 1);
|
||||
AstNode* past = new AstPast(fl, exprp, nullptr);
|
||||
AstNode* const past = new AstPast(fl, exprp, nullptr);
|
||||
past->dtypeFrom(exprp);
|
||||
exprp = new AstAnd(fl, new AstNot(fl, past), exprp->cloneTree(false));
|
||||
exprp->dtypeSetBit();
|
||||
@@ -130,9 +130,9 @@ private:
|
||||
virtual void visit(AstStable* nodep) override {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
iterateChildren(nodep);
|
||||
FileLine* fl = nodep->fileline();
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNode* exprp = nodep->exprp()->unlinkFrBack();
|
||||
AstNode* past = new AstPast(fl, exprp, nullptr);
|
||||
AstNode* const past = new AstPast(fl, exprp, nullptr);
|
||||
past->dtypeFrom(exprp);
|
||||
exprp = new AstEq(fl, past, exprp->cloneTree(false));
|
||||
exprp->dtypeSetBit();
|
||||
@@ -144,15 +144,15 @@ private:
|
||||
virtual void visit(AstImplication* nodep) override {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
|
||||
FileLine* fl = nodep->fileline();
|
||||
AstNode* rhsp = nodep->rhsp()->unlinkFrBack();
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNode* const rhsp = nodep->rhsp()->unlinkFrBack();
|
||||
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
|
||||
|
||||
if (m_disablep) lhsp = new AstAnd(fl, new AstNot(fl, m_disablep), lhsp);
|
||||
|
||||
AstNode* past = new AstPast(fl, lhsp, nullptr);
|
||||
AstNode* const past = new AstPast(fl, lhsp, nullptr);
|
||||
past->dtypeFrom(lhsp);
|
||||
AstNode* exprp = new AstOr(fl, new AstNot(fl, past), rhsp);
|
||||
AstNode* const exprp = new AstOr(fl, new AstNot(fl, past), rhsp);
|
||||
exprp->dtypeSetBit();
|
||||
nodep->replaceWith(exprp);
|
||||
nodep->sentreep(newSenTree(nodep));
|
||||
@@ -205,6 +205,6 @@ public:
|
||||
|
||||
void V3AssertPre::assertPreAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ AssertPreVisitor visitor(nodep); } // Destruct before checking
|
||||
{ AssertPreVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("assertpre", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+71
-66
@@ -74,9 +74,10 @@ AstNode::AstNode(AstType t, FileLine* fl)
|
||||
m_clonep = nullptr;
|
||||
m_cloneCnt = 0;
|
||||
// Attributes
|
||||
m_didWidth = false;
|
||||
m_doingWidth = false;
|
||||
m_protect = true;
|
||||
m_flags.didWidth = false;
|
||||
m_flags.doingWidth = false;
|
||||
m_flags.protect = true;
|
||||
m_flags.unused = 0; // Initializing this avoids a read-modify-write on construction
|
||||
m_user1u = VNUser(0);
|
||||
m_user1Cnt = 0;
|
||||
m_user2u = VNUser(0);
|
||||
@@ -94,7 +95,7 @@ AstNode* AstNode::abovep() const {
|
||||
// Avoid supporting at other locations as would require walking
|
||||
// list which is likely to cause performance issues.
|
||||
UASSERT_OBJ(!m_nextp || firstAbovep(), this, "abovep() not allowed when in midlist");
|
||||
const AstNode* firstp = firstAbovep() ? this : m_headtailp;
|
||||
const AstNode* const firstp = firstAbovep() ? this : m_headtailp;
|
||||
return firstp->backp();
|
||||
}
|
||||
|
||||
@@ -229,7 +230,8 @@ string AstNode::prettyTypeName() const {
|
||||
//######################################################################
|
||||
// Insertion
|
||||
|
||||
inline void AstNode::debugTreeChange(const char* prefix, int lineno, bool next){
|
||||
inline void AstNode::debugTreeChange(const AstNode* nodep, const char* prefix, int lineno,
|
||||
bool next){
|
||||
#ifdef VL_DEBUG
|
||||
// Called on all major tree changers.
|
||||
// Only for use for those really nasty bugs relating to internals
|
||||
@@ -253,8 +255,8 @@ inline void AstNode::debugTreeChange(const char* prefix, int lineno, bool next){
|
||||
AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp) {
|
||||
// Add to m_nextp, returns this
|
||||
UDEBUGONLY(UASSERT_OBJ(newp, nodep, "Null item passed to addNext"););
|
||||
nodep->debugTreeChange("-addNextThs: ", __LINE__, false);
|
||||
newp->debugTreeChange("-addNextNew: ", __LINE__, true);
|
||||
debugTreeChange(nodep, "-addNextThs: ", __LINE__, false);
|
||||
debugTreeChange(newp, "-addNextNew: ", __LINE__, true);
|
||||
if (!nodep) { // verilog.y and lots of other places assume this
|
||||
return newp;
|
||||
} else {
|
||||
@@ -275,8 +277,8 @@ AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp) {
|
||||
oldtailp->m_nextp = newp;
|
||||
newp->m_backp = oldtailp;
|
||||
// New tail needs the head
|
||||
AstNode* newtailp = newp->m_headtailp;
|
||||
AstNode* headp = oldtailp->m_headtailp;
|
||||
AstNode* const newtailp = newp->m_headtailp;
|
||||
AstNode* const headp = oldtailp->m_headtailp;
|
||||
oldtailp->m_headtailp = nullptr; // May be written again as new head
|
||||
newp->m_headtailp = nullptr; // May be written again as new tail
|
||||
newtailp->m_headtailp = headp;
|
||||
@@ -284,7 +286,7 @@ AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp) {
|
||||
newp->editCountInc();
|
||||
if (oldtailp->m_iterpp) *(oldtailp->m_iterpp) = newp; // Iterate on new item
|
||||
}
|
||||
nodep->debugTreeChange("-addNextOut:", __LINE__, true);
|
||||
debugTreeChange(nodep, "-addNextOut:", __LINE__, true);
|
||||
return nodep;
|
||||
}
|
||||
|
||||
@@ -299,15 +301,15 @@ void AstNode::addNextHere(AstNode* newp) {
|
||||
// New could be head of single node, or list
|
||||
UASSERT(newp, "Null item passed to addNext");
|
||||
UASSERT(!newp->backp(), "New node (back) already assigned?");
|
||||
this->debugTreeChange("-addHereThs: ", __LINE__, false);
|
||||
newp->debugTreeChange("-addHereNew: ", __LINE__, true);
|
||||
debugTreeChange(this, "-addHereThs: ", __LINE__, false);
|
||||
debugTreeChange(newp, "-addHereNew: ", __LINE__, true);
|
||||
newp->editCountInc();
|
||||
|
||||
AstNode* addlastp = newp->m_headtailp; // Last node in list to be added
|
||||
AstNode* const addlastp = newp->m_headtailp; // Last node in list to be added
|
||||
UASSERT(!addlastp->m_nextp, "Headtailp tail isn't at the tail");
|
||||
|
||||
// Forward links
|
||||
AstNode* oldnextp = this->m_nextp;
|
||||
AstNode* const oldnextp = this->m_nextp;
|
||||
this->m_nextp = newp;
|
||||
addlastp->m_nextp = oldnextp; // Perhaps null if 'this' is not list
|
||||
|
||||
@@ -316,7 +318,7 @@ void AstNode::addNextHere(AstNode* newp) {
|
||||
newp->m_backp = this;
|
||||
|
||||
// Head/tail
|
||||
AstNode* oldheadtailp = this->m_headtailp;
|
||||
AstNode* const oldheadtailp = this->m_headtailp;
|
||||
// (!oldheadtailp) // this was&is middle of list
|
||||
// (oldheadtailp==this && !oldnext)// this was head AND tail (one node long list)
|
||||
// (oldheadtailp && oldnextp) // this was&is head of list of not just one node, not
|
||||
@@ -338,7 +340,7 @@ void AstNode::addNextHere(AstNode* newp) {
|
||||
}
|
||||
|
||||
if (this->m_iterpp) *(this->m_iterpp) = newp; // Iterate on new item
|
||||
this->debugTreeChange("-addHereOut: ", __LINE__, true);
|
||||
debugTreeChange(this, "-addHereOut: ", __LINE__, true);
|
||||
}
|
||||
|
||||
void AstNode::setOp1p(AstNode* newp) {
|
||||
@@ -346,12 +348,12 @@ void AstNode::setOp1p(AstNode* newp) {
|
||||
UDEBUGONLY(UASSERT_OBJ(!m_op1p, this, "Adding to non-empty, non-list op1"););
|
||||
UDEBUGONLY(UASSERT_OBJ(!newp->m_backp, newp, "Adding already linked node"););
|
||||
UDEBUGONLY(UASSERT_OBJ(!newp->m_nextp, newp, "Adding list to non-list op1"););
|
||||
this->debugTreeChange("-setOp1pThs: ", __LINE__, false);
|
||||
newp->debugTreeChange("-setOp1pNew: ", __LINE__, true);
|
||||
debugTreeChange(this, "-setOp1pThs: ", __LINE__, false);
|
||||
debugTreeChange(newp, "-setOp1pNew: ", __LINE__, true);
|
||||
m_op1p = newp;
|
||||
newp->editCountInc();
|
||||
newp->m_backp = this;
|
||||
this->debugTreeChange("-setOp1pOut: ", __LINE__, false);
|
||||
debugTreeChange(this, "-setOp1pOut: ", __LINE__, false);
|
||||
}
|
||||
|
||||
void AstNode::setOp2p(AstNode* newp) {
|
||||
@@ -359,12 +361,12 @@ void AstNode::setOp2p(AstNode* newp) {
|
||||
UDEBUGONLY(UASSERT_OBJ(!m_op2p, this, "Adding to non-empty, non-list op2"););
|
||||
UDEBUGONLY(UASSERT_OBJ(!newp->m_backp, newp, "Adding already linked node"););
|
||||
UDEBUGONLY(UASSERT_OBJ(!newp->m_nextp, newp, "Adding list to non-list op2"););
|
||||
this->debugTreeChange("-setOp2pThs: ", __LINE__, false);
|
||||
newp->debugTreeChange("-setOp2pNew: ", __LINE__, true);
|
||||
debugTreeChange(this, "-setOp2pThs: ", __LINE__, false);
|
||||
debugTreeChange(newp, "-setOp2pNew: ", __LINE__, true);
|
||||
m_op2p = newp;
|
||||
newp->editCountInc();
|
||||
newp->m_backp = this;
|
||||
this->debugTreeChange("-setOp2pOut: ", __LINE__, false);
|
||||
debugTreeChange(this, "-setOp2pOut: ", __LINE__, false);
|
||||
}
|
||||
|
||||
void AstNode::setOp3p(AstNode* newp) {
|
||||
@@ -372,12 +374,12 @@ void AstNode::setOp3p(AstNode* newp) {
|
||||
UDEBUGONLY(UASSERT_OBJ(!m_op3p, this, "Adding to non-empty, non-list op3"););
|
||||
UDEBUGONLY(UASSERT_OBJ(!newp->m_backp, newp, "Adding already linked node"););
|
||||
UDEBUGONLY(UASSERT_OBJ(!newp->m_nextp, newp, "Adding list to non-list op3"););
|
||||
this->debugTreeChange("-setOp3pThs: ", __LINE__, false);
|
||||
newp->debugTreeChange("-setOp3pNew: ", __LINE__, true);
|
||||
debugTreeChange(this, "-setOp3pThs: ", __LINE__, false);
|
||||
debugTreeChange(newp, "-setOp3pNew: ", __LINE__, true);
|
||||
m_op3p = newp;
|
||||
newp->editCountInc();
|
||||
newp->m_backp = this;
|
||||
this->debugTreeChange("-setOp3pOut: ", __LINE__, false);
|
||||
debugTreeChange(this, "-setOp3pOut: ", __LINE__, false);
|
||||
}
|
||||
|
||||
void AstNode::setOp4p(AstNode* newp) {
|
||||
@@ -385,12 +387,12 @@ void AstNode::setOp4p(AstNode* newp) {
|
||||
UDEBUGONLY(UASSERT_OBJ(!m_op4p, this, "Adding to non-empty, non-list op4"););
|
||||
UDEBUGONLY(UASSERT_OBJ(!newp->m_backp, newp, "Adding already linked node"););
|
||||
UDEBUGONLY(UASSERT_OBJ(!newp->m_nextp, newp, "Adding list to non-list op4"););
|
||||
this->debugTreeChange("-setOp4pThs: ", __LINE__, false);
|
||||
newp->debugTreeChange("-setOp4pNew: ", __LINE__, true);
|
||||
debugTreeChange(this, "-setOp4pThs: ", __LINE__, false);
|
||||
debugTreeChange(newp, "-setOp4pNew: ", __LINE__, true);
|
||||
m_op4p = newp;
|
||||
newp->editCountInc();
|
||||
newp->m_backp = this;
|
||||
this->debugTreeChange("-setOp4pOut: ", __LINE__, false);
|
||||
debugTreeChange(this, "-setOp4pOut: ", __LINE__, false);
|
||||
}
|
||||
|
||||
void AstNode::addOp1p(AstNode* newp) {
|
||||
@@ -452,11 +454,11 @@ void AstNRelinker::dump(std::ostream& str) const {
|
||||
}
|
||||
|
||||
AstNode* AstNode::unlinkFrBackWithNext(AstNRelinker* linkerp) {
|
||||
this->debugTreeChange("-unlinkWNextThs: ", __LINE__, true);
|
||||
AstNode* oldp = this;
|
||||
debugTreeChange(this, "-unlinkWNextThs: ", __LINE__, true);
|
||||
AstNode* const oldp = this;
|
||||
UASSERT(oldp->m_backp, "Node has no back, already unlinked?");
|
||||
oldp->editCountInc();
|
||||
AstNode* backp = oldp->m_backp;
|
||||
AstNode* const backp = oldp->m_backp;
|
||||
if (linkerp) {
|
||||
linkerp->m_oldp = oldp;
|
||||
linkerp->m_backp = backp;
|
||||
@@ -485,7 +487,7 @@ AstNode* AstNode::unlinkFrBackWithNext(AstNRelinker* linkerp) {
|
||||
AstNode* oldtailp = oldp;
|
||||
while (oldtailp->m_nextp) oldtailp = oldtailp->m_nextp;
|
||||
// Create new head/tail of old list
|
||||
AstNode* oldheadp = oldtailp->m_headtailp;
|
||||
AstNode* const oldheadp = oldtailp->m_headtailp;
|
||||
oldheadp->m_headtailp = oldp->m_backp;
|
||||
oldheadp->m_headtailp->m_headtailp = oldheadp;
|
||||
// Create new head/tail of extracted list
|
||||
@@ -507,16 +509,16 @@ AstNode* AstNode::unlinkFrBackWithNext(AstNRelinker* linkerp) {
|
||||
// Iterator fixup
|
||||
if (oldp->m_iterpp) *(oldp->m_iterpp) = nullptr;
|
||||
oldp->m_iterpp = nullptr;
|
||||
oldp->debugTreeChange("-unlinkWNextOut: ", __LINE__, true);
|
||||
debugTreeChange(oldp, "-unlinkWNextOut: ", __LINE__, true);
|
||||
return oldp;
|
||||
}
|
||||
|
||||
AstNode* AstNode::unlinkFrBack(AstNRelinker* linkerp) {
|
||||
this->debugTreeChange("-unlinkFrBkThs: ", __LINE__, true);
|
||||
AstNode* oldp = this;
|
||||
debugTreeChange(this, "-unlinkFrBkThs: ", __LINE__, true);
|
||||
AstNode* const oldp = this;
|
||||
UASSERT(oldp->m_backp, "Node has no back, already unlinked?");
|
||||
oldp->editCountInc();
|
||||
AstNode* backp = oldp->m_backp;
|
||||
AstNode* const backp = oldp->m_backp;
|
||||
if (linkerp) {
|
||||
linkerp->m_oldp = oldp;
|
||||
linkerp->m_backp = backp;
|
||||
@@ -558,7 +560,7 @@ AstNode* AstNode::unlinkFrBack(AstNRelinker* linkerp) {
|
||||
this->v3fatalSrc("Unlink of node with back not pointing to it.");
|
||||
}
|
||||
if (oldp->m_nextp) {
|
||||
AstNode* newheadp = oldp->m_nextp;
|
||||
AstNode* const newheadp = oldp->m_nextp;
|
||||
newheadp->m_backp = backp;
|
||||
newheadp->m_headtailp = oldp->m_headtailp;
|
||||
newheadp->m_headtailp->m_headtailp = newheadp;
|
||||
@@ -571,7 +573,7 @@ AstNode* AstNode::unlinkFrBack(AstNRelinker* linkerp) {
|
||||
oldp->m_backp = nullptr;
|
||||
oldp->m_headtailp = this;
|
||||
oldp->m_iterpp = nullptr;
|
||||
oldp->debugTreeChange("-unlinkFrBkOut: ", __LINE__, true);
|
||||
debugTreeChange(oldp, "-unlinkFrBkOut: ", __LINE__, true);
|
||||
return oldp;
|
||||
}
|
||||
|
||||
@@ -580,7 +582,7 @@ void AstNode::relink(AstNRelinker* linkerp) {
|
||||
UINFO(0, " EDIT: relink: ");
|
||||
dumpPtrs();
|
||||
}
|
||||
AstNode* newp = this;
|
||||
AstNode* const newp = this;
|
||||
UASSERT(linkerp && linkerp->m_backp, "Need non-empty linker");
|
||||
UASSERT(!newp->backp(), "New node already linked?");
|
||||
newp->editCountInc();
|
||||
@@ -590,9 +592,9 @@ void AstNode::relink(AstNRelinker* linkerp) {
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
AstNode* backp = linkerp->m_backp;
|
||||
this->debugTreeChange("-relinkNew: ", __LINE__, true);
|
||||
backp->debugTreeChange("-relinkTre: ", __LINE__, true);
|
||||
AstNode* const backp = linkerp->m_backp;
|
||||
debugTreeChange(this, "-relinkNew: ", __LINE__, true);
|
||||
debugTreeChange(backp, "-relinkTre: ", __LINE__, true);
|
||||
|
||||
switch (linkerp->m_chg) {
|
||||
case AstNRelinker::RELINK_NEXT: backp->addNextHere(newp); break;
|
||||
@@ -617,7 +619,7 @@ void AstNode::relink(AstNRelinker* linkerp) {
|
||||
}
|
||||
// Empty the linker so not used twice accidentally
|
||||
linkerp->m_backp = nullptr;
|
||||
this->debugTreeChange("-relinkOut: ", __LINE__, true);
|
||||
debugTreeChange(this, "-relinkOut: ", __LINE__, true);
|
||||
}
|
||||
|
||||
void AstNode::relinkOneLink(AstNode*& pointpr, // Ref to pointer that gets set to newp
|
||||
@@ -629,10 +631,10 @@ void AstNode::relinkOneLink(AstNode*& pointpr, // Ref to pointer that gets set
|
||||
// Likewise there may be a old list.
|
||||
// Insert the whole old list following the new node's list.
|
||||
// Thus a unlink without next, followed by relink, gives the same list.
|
||||
AstNode* newlistlastp = newp->m_headtailp;
|
||||
AstNode* const newlistlastp = newp->m_headtailp;
|
||||
UASSERT_OBJ(!(newlistlastp->m_nextp && newlistlastp != newp), newp,
|
||||
"Headtailp tail isn't at the tail");
|
||||
AstNode* oldlistlastp = pointpr->m_headtailp;
|
||||
AstNode* const oldlistlastp = pointpr->m_headtailp;
|
||||
UASSERT_OBJ(!(oldlistlastp->m_nextp && oldlistlastp != pointpr), newp,
|
||||
"Old headtailp tail isn't at the tail");
|
||||
// Next links
|
||||
@@ -669,7 +671,7 @@ void AstNode::swapWith(AstNode* bp) {
|
||||
|
||||
AstNode* AstNode::cloneTreeIter() {
|
||||
// private: Clone single node and children
|
||||
AstNode* newp = this->clone();
|
||||
AstNode* const newp = this->clone();
|
||||
if (this->m_op1p) newp->op1p(this->m_op1p->cloneTreeIterList());
|
||||
if (this->m_op2p) newp->op2p(this->m_op2p->cloneTreeIterList());
|
||||
if (this->m_op3p) newp->op3p(this->m_op3p->cloneTreeIterList());
|
||||
@@ -686,7 +688,7 @@ AstNode* AstNode::cloneTreeIterList() {
|
||||
AstNode* newtailp = nullptr;
|
||||
// Audited to make sure this is never nullptr
|
||||
for (AstNode* oldp = this; oldp; oldp = oldp->m_nextp) {
|
||||
AstNode* newp = oldp->cloneTreeIter();
|
||||
AstNode* const newp = oldp->cloneTreeIter();
|
||||
newp->m_headtailp = nullptr;
|
||||
newp->m_backp = newtailp;
|
||||
if (newtailp) newtailp->m_nextp = newp;
|
||||
@@ -699,7 +701,7 @@ AstNode* AstNode::cloneTreeIterList() {
|
||||
}
|
||||
|
||||
AstNode* AstNode::cloneTree(bool cloneNextLink) {
|
||||
this->debugTreeChange("-cloneThs: ", __LINE__, cloneNextLink);
|
||||
debugTreeChange(this, "-cloneThs: ", __LINE__, cloneNextLink);
|
||||
cloneClearTree();
|
||||
AstNode* newp;
|
||||
if (cloneNextLink && this->m_nextp) {
|
||||
@@ -711,7 +713,7 @@ AstNode* AstNode::cloneTree(bool cloneNextLink) {
|
||||
}
|
||||
newp->m_backp = nullptr;
|
||||
newp->cloneRelinkTree();
|
||||
newp->debugTreeChange("-cloneOut: ", __LINE__, true);
|
||||
debugTreeChange(newp, "-cloneOut: ", __LINE__, true);
|
||||
return newp;
|
||||
}
|
||||
|
||||
@@ -763,7 +765,7 @@ 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, "Delete called on node with backlink still set");
|
||||
this->debugTreeChange("-delTree: ", __LINE__, true);
|
||||
debugTreeChange(this, "-delTree: ", __LINE__, true);
|
||||
this->editCountInc();
|
||||
// MUST be depth first!
|
||||
deleteTreeIter();
|
||||
@@ -775,14 +777,14 @@ void AstNode::deleteTree() {
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
void* AstNode::operator new(size_t size) {
|
||||
// Optimization note: Aligning to cache line is a loss, due to lost packing
|
||||
AstNode* objp = static_cast<AstNode*>(::operator new(size));
|
||||
const AstNode* const objp = static_cast<AstNode*>(::operator new(size));
|
||||
V3Broken::addNewed(objp);
|
||||
return objp;
|
||||
}
|
||||
|
||||
void AstNode::operator delete(void* objp, size_t size) {
|
||||
if (!objp) return;
|
||||
AstNode* nodep = static_cast<AstNode*>(objp);
|
||||
const AstNode* const nodep = static_cast<AstNode*>(objp);
|
||||
V3Broken::deleted(nodep);
|
||||
::operator delete(objp);
|
||||
}
|
||||
@@ -875,7 +877,7 @@ void AstNode::iterateAndNextConst(AstNVisitor& v) {
|
||||
// Keep following the current list even if edits change it
|
||||
AstNode* nodep = this;
|
||||
do {
|
||||
AstNode* nnextp = nodep->m_nextp;
|
||||
AstNode* const nnextp = nodep->m_nextp;
|
||||
ASTNODE_PREFETCH(nnextp);
|
||||
nodep->accept(v);
|
||||
nodep = nnextp;
|
||||
@@ -896,7 +898,7 @@ AstNode* AstNode::iterateSubtreeReturnEdits(AstNVisitor& v) {
|
||||
} else if (!nodep->backp()) {
|
||||
// Calling on standalone tree; insert a shim node so we can keep
|
||||
// track, then delete it on completion
|
||||
AstBegin* tempp = new AstBegin(nodep->fileline(), "[EditWrapper]", nodep);
|
||||
AstBegin* const tempp = new AstBegin(nodep->fileline(), "[EditWrapper]", nodep);
|
||||
{
|
||||
VL_DO_DANGLING(tempp->stmtsp()->accept(v),
|
||||
nodep); // nodep to null as may be replaced
|
||||
@@ -992,8 +994,8 @@ void AstNode::checkTreeIter(AstNode* backp) {
|
||||
void AstNode::checkTreeIterList(AstNode* backp) {
|
||||
// private: Check a (possible) list of nodes, this is always the head of the list
|
||||
// Audited to make sure this is never nullptr
|
||||
AstNode* headp = this;
|
||||
AstNode* tailp = this;
|
||||
AstNode* const headp = this;
|
||||
const AstNode* tailp = this;
|
||||
for (AstNode* nodep = headp; nodep; nodep = nodep->nextp()) {
|
||||
nodep->checkTreeIter(backp);
|
||||
UASSERT_OBJ(headp == this || !nextp(), this,
|
||||
@@ -1123,7 +1125,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo
|
||||
if (doDump) {
|
||||
{ // Write log & close
|
||||
UINFO(2, "Dumping " << filename << endl);
|
||||
const std::unique_ptr<std::ofstream> logsp(V3File::new_ofstream(filename, append));
|
||||
const std::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() << ">\n";
|
||||
@@ -1141,7 +1143,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo
|
||||
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 = VN_CAST(this, Netlist)) V3Broken::brokenAll(netp);
|
||||
if (AstNetlist* const netp = VN_CAST(this, Netlist)) V3Broken::brokenAll(netp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1162,7 +1164,7 @@ string AstNode::locationStr() const {
|
||||
return ""; // LCOV_EXCL_LINE
|
||||
}
|
||||
const AstScope* scopep;
|
||||
if ((scopep = VN_CAST_CONST(backp, Scope))) {
|
||||
if ((scopep = VN_CAST(backp, Scope))) {
|
||||
// The design is flattened and there are no useful scopes
|
||||
// This is probably because of inlining
|
||||
if (scopep->isTop()) break;
|
||||
@@ -1176,10 +1178,10 @@ string AstNode::locationStr() const {
|
||||
while (backp) {
|
||||
const AstModule* modp;
|
||||
const AstNodeVarRef* nvrp;
|
||||
if ((modp = VN_CAST_CONST(backp, Module)) && !modp->hierName().empty()) {
|
||||
if ((modp = VN_CAST(backp, Module)) && !modp->hierName().empty()) {
|
||||
str += modp->hierName();
|
||||
return str;
|
||||
} else if ((nvrp = VN_CAST_CONST(backp, NodeVarRef))) {
|
||||
} else if ((nvrp = VN_CAST(backp, NodeVarRef))) {
|
||||
const string prettyName = nvrp->prettyName();
|
||||
// VarRefs have not been flattened yet and do not contain location information
|
||||
if (prettyName != nvrp->name()) {
|
||||
@@ -1263,17 +1265,20 @@ AstNodeDType* AstNode::findBitRangeDType(const VNumRange& range, int widthMin,
|
||||
AstBasicDType* AstNode::findInsertSameDType(AstBasicDType* nodep) {
|
||||
return v3Global.rootp()->typeTablep()->findInsertSameDType(nodep);
|
||||
}
|
||||
AstNodeDType* AstNode::findVoidDType() const {
|
||||
return v3Global.rootp()->typeTablep()->findVoidDType(fileline());
|
||||
AstNodeDType* AstNode::findEmptyQueueDType() const {
|
||||
return v3Global.rootp()->typeTablep()->findEmptyQueueDType(fileline());
|
||||
}
|
||||
AstNodeDType* AstNode::findQueueIndexDType() const {
|
||||
return v3Global.rootp()->typeTablep()->findQueueIndexDType(fileline());
|
||||
}
|
||||
AstNodeDType* AstNode::findVoidDType() const {
|
||||
return v3Global.rootp()->typeTablep()->findVoidDType(fileline());
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// AstNVisitor
|
||||
// AstNDeleter
|
||||
|
||||
void AstNVisitor::doDeletes() {
|
||||
for (AstNode* nodep : m_deleteps) nodep->deleteTree();
|
||||
void AstNDeleter::doDeletes() {
|
||||
for (AstNode* const nodep : m_deleteps) nodep->deleteTree();
|
||||
m_deleteps.clear();
|
||||
}
|
||||
|
||||
+210
-145
@@ -24,6 +24,7 @@
|
||||
#include "V3FileLine.h"
|
||||
#include "V3Number.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Broken.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
@@ -50,24 +51,33 @@ using MTaskIdSet = std::set<int>; // Set of mtaskIds for Var sorting
|
||||
// For broken() function, return error string if have a match
|
||||
#define BROKEN_RTN(test) \
|
||||
do { \
|
||||
if (VL_UNCOVERABLE(test)) return #test; \
|
||||
if (VL_UNCOVERABLE(test)) return "'" #test "' @ " __FILE__ ":" VL_STRINGIFY(__LINE__); \
|
||||
} while (false)
|
||||
// For broken() function, return error string if a base of this class has a match
|
||||
#define BROKEN_BASE_RTN(test) \
|
||||
do { \
|
||||
const char* reasonp = (test); \
|
||||
const char* const reasonp = (test); \
|
||||
if (VL_UNCOVERABLE(reasonp)) return reasonp; \
|
||||
} while (false)
|
||||
|
||||
// (V)erilator (N)ode is: True if AstNode is of a a given AstType
|
||||
#define VN_IS(nodep, nodetypename) (AstNode::privateIs<Ast##nodetypename>(nodep))
|
||||
// (V)erilator (N)ode is: Returns true if and only if AstNode is of the given AstNode subtype,
|
||||
// and not nullptr.
|
||||
#define VN_IS(nodep, nodetypename) (AstNode::privateIs<Ast##nodetypename, decltype(nodep)>(nodep))
|
||||
|
||||
// (V)erilator (N)ode cast: Cast to given type if can; effectively
|
||||
// dynamic_cast<nodetypename>(nodep)
|
||||
#define VN_CAST(nodep, nodetypename) (AstNode::privateCast<Ast##nodetypename>(nodep))
|
||||
#define VN_CAST_CONST(nodep, nodetypename) (AstNode::privateConstCast<Ast##nodetypename>(nodep))
|
||||
// (V)erilator (N)ode cast: Similar to dynamic_cast, but more efficient, use this instead.
|
||||
// Cast to given type if node is of such type, otherwise returns nullptr. If 'nodep' is nullptr,
|
||||
// return nullptr. Pointer constness is preserved, i.e.: given a 'const AstNode*',
|
||||
// a 'const Ast<nodetypename>*' is returned.
|
||||
#define VN_CAST(nodep, nodetypename) \
|
||||
(AstNode::privateCast<Ast##nodetypename, decltype(nodep)>(nodep))
|
||||
|
||||
// (V)erilator (N)ode deleted: Reference to deleted child (for assertions only)
|
||||
// (V)erilator (N)ode as: Assert node is of given type then cast to that type. Use this to
|
||||
// downcast instead of VN_CAST when you know the true type of the node. If 'nodep' is nullptr,
|
||||
// return nullptr. Pointer constness is preserved, i.e.: given a 'const AstNode*', a 'const
|
||||
// Ast<nodetypename>*' is returned.
|
||||
#define VN_AS(nodep, nodetypename) (AstNode::privateAs<Ast##nodetypename, decltype(nodep)>(nodep))
|
||||
|
||||
// (V)erilator (N)ode deleted: Pointer to deleted AstNode (for assertions only)
|
||||
#define VN_DELETED(nodep) VL_UNLIKELY((vluint64_t)(nodep) == 0x1)
|
||||
|
||||
//######################################################################
|
||||
@@ -238,39 +248,6 @@ inline bool operator==(AstPragmaType::en lhs, const AstPragmaType& rhs) { return
|
||||
|
||||
//######################################################################
|
||||
|
||||
class AstCFuncType final {
|
||||
public:
|
||||
enum en : uint8_t {
|
||||
FT_NORMAL,
|
||||
TRACE_REGISTER,
|
||||
TRACE_INIT,
|
||||
TRACE_INIT_SUB,
|
||||
TRACE_FULL,
|
||||
TRACE_FULL_SUB,
|
||||
TRACE_CHANGE,
|
||||
TRACE_CHANGE_SUB,
|
||||
TRACE_CLEANUP
|
||||
};
|
||||
enum en m_e;
|
||||
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)) {} // Need () or GCC 4.8 false warning
|
||||
operator en() const { return m_e; }
|
||||
// METHODS
|
||||
bool isTrace() const { return m_e != FT_NORMAL; }
|
||||
};
|
||||
inline bool operator==(const AstCFuncType& lhs, const AstCFuncType& rhs) {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
}
|
||||
inline bool operator==(const AstCFuncType& lhs, AstCFuncType::en rhs) { return lhs.m_e == rhs; }
|
||||
inline bool operator==(AstCFuncType::en lhs, const AstCFuncType& rhs) { return lhs == rhs.m_e; }
|
||||
|
||||
//######################################################################
|
||||
|
||||
class VEdgeType final {
|
||||
public:
|
||||
// REMEMBER to edit the strings below too
|
||||
@@ -527,7 +504,8 @@ public:
|
||||
}
|
||||
bool isUnsigned() const {
|
||||
return m_e == CHANDLE || m_e == EVENTVALUE || m_e == STRING || m_e == SCOPEPTR
|
||||
|| m_e == CHARPTR || m_e == UINT32 || m_e == UINT64;
|
||||
|| m_e == CHARPTR || m_e == UINT32 || m_e == UINT64 || m_e == BIT || m_e == LOGIC
|
||||
|| m_e == TIME;
|
||||
}
|
||||
bool isFourstate() const {
|
||||
return m_e == INTEGER || m_e == LOGIC || m_e == LOGIC_IMPLICIT || m_e == TIME;
|
||||
@@ -540,9 +518,6 @@ public:
|
||||
return (m_e == BIT || m_e == BYTE || m_e == INT || m_e == INTEGER || m_e == LOGIC
|
||||
|| m_e == LONGINT || m_e == SHORTINT || m_e == UINT32 || m_e == UINT64);
|
||||
}
|
||||
bool isSloppy() const { // Don't be as anal about width warnings
|
||||
return !(m_e == LOGIC || m_e == BIT);
|
||||
}
|
||||
bool isBitLogic() const { // Bit/logic vector types; can form a packed array
|
||||
return (m_e == LOGIC || m_e == BIT);
|
||||
}
|
||||
@@ -561,6 +536,25 @@ public:
|
||||
bool isEventValue() const { return m_e == EVENTVALUE; }
|
||||
bool isString() const { return m_e == STRING; }
|
||||
bool isMTaskState() const { return m_e == MTASKSTATE; }
|
||||
// Does this represent a C++ LiteralType? (can be constexpr)
|
||||
bool isLiteralType() const {
|
||||
switch (m_e) {
|
||||
case BIT:
|
||||
case BYTE:
|
||||
case CHANDLE:
|
||||
case INT:
|
||||
case INTEGER:
|
||||
case LOGIC:
|
||||
case LONGINT:
|
||||
case DOUBLE:
|
||||
case SHORTINT:
|
||||
case SCOPEPTR:
|
||||
case CHARPTR:
|
||||
case UINT32:
|
||||
case UINT64: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
inline bool operator==(const AstBasicDTypeKwd& lhs, const AstBasicDTypeKwd& rhs) {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
@@ -1014,7 +1008,7 @@ public:
|
||||
// MEMBERS
|
||||
void init(int hi, int lo, bool littleEndian) {
|
||||
if (lo > hi) {
|
||||
int t = hi;
|
||||
const int t = hi;
|
||||
hi = lo;
|
||||
lo = t;
|
||||
}
|
||||
@@ -1033,9 +1027,6 @@ public:
|
||||
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_right == 0 && m_left >= 1));
|
||||
}
|
||||
void dump(std::ostream& str) const {
|
||||
if (ranged()) {
|
||||
str << "[" << left() << ":" << right() << "]";
|
||||
@@ -1086,11 +1077,11 @@ inline std::ostream& operator<<(std::ostream& os, const VUseType& rhs) {
|
||||
|
||||
class VBasicTypeKey final {
|
||||
public:
|
||||
int m_width; // From AstNodeDType: Bit width of operation
|
||||
int m_widthMin; // From AstNodeDType: If unsized, bitwidth of minimum implementation
|
||||
VSigning m_numeric; // From AstNodeDType: Node is signed
|
||||
AstBasicDTypeKwd m_keyword; // From AstBasicDType: What keyword created basic type
|
||||
VNumRange m_nrange; // From AstBasicDType: Numeric msb/lsb (if non-opaque keyword)
|
||||
const int m_width; // From AstNodeDType: Bit width of operation
|
||||
const int m_widthMin; // From AstNodeDType: If unsized, bitwidth of minimum implementation
|
||||
const VSigning m_numeric; // From AstNodeDType: Node is signed
|
||||
const AstBasicDTypeKwd m_keyword; // From AstBasicDType: What keyword created basic type
|
||||
const VNumRange m_nrange; // From AstBasicDType: Numeric msb/lsb (if non-opaque keyword)
|
||||
bool operator==(const VBasicTypeKey& rhs) const {
|
||||
return m_width == rhs.m_width && m_widthMin == rhs.m_widthMin && m_numeric == rhs.m_numeric
|
||||
&& m_keyword == rhs.m_keyword && m_nrange == rhs.m_nrange;
|
||||
@@ -1255,31 +1246,39 @@ public:
|
||||
// clang-format on
|
||||
|
||||
//######################################################################
|
||||
// AstNVisitor -- Allows new functions to be called on each node
|
||||
// type without changing the base classes. See "Modern C++ Design".
|
||||
// Node deleter, deletes all enqueued AstNode* on destruction, or when
|
||||
// explicitly told to do so. This is useful when the deletion of removed
|
||||
// nodes needs to be deferred to a later time, because pointers to the
|
||||
// removed nodes might still exist.
|
||||
|
||||
class AstNVisitor VL_NOT_FINAL {
|
||||
private:
|
||||
class AstNDeleter VL_NOT_FINAL {
|
||||
// MEMBERS
|
||||
std::vector<AstNode*> m_deleteps; // Nodes to delete when doDeletes() called
|
||||
protected:
|
||||
friend class AstNode;
|
||||
std::vector<AstNode*> m_deleteps; // Nodes to delete
|
||||
|
||||
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
|
||||
/// persist for the lifetime of the visitor
|
||||
|
||||
// Enqueue node for deletion on next 'doDelete' (or destruction)
|
||||
void pushDeletep(AstNode* nodep) {
|
||||
UASSERT_STATIC(nodep, "Cannot delete nullptr node");
|
||||
m_deleteps.push_back(nodep);
|
||||
}
|
||||
/// Call deleteTree on all previously pushDeletep()'ed nodes
|
||||
|
||||
// Delete all previously pushed nodes (by callint deleteTree)
|
||||
void doDeletes();
|
||||
|
||||
// Do the deletions on destruction
|
||||
virtual ~AstNDeleter() { doDeletes(); }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// AstNVisitor -- Allows new functions to be called on each node
|
||||
// type without changing the base classes. See "Modern C++ Design".
|
||||
|
||||
class AstNVisitor VL_NOT_FINAL : public AstNDeleter {
|
||||
friend class AstNode;
|
||||
|
||||
public:
|
||||
virtual ~AstNVisitor() { doDeletes(); }
|
||||
/// Call visit()s on nodep
|
||||
void iterate(AstNode* nodep);
|
||||
/// Call visit()s on nodep
|
||||
@@ -1367,8 +1366,32 @@ class AstNode VL_NOT_FINAL {
|
||||
const AstType m_type; // Node sub-type identifier
|
||||
// ^ ASTNODE_PREFETCH depends on above ordering of members
|
||||
|
||||
// padding - 2 extra bytes here after m_type
|
||||
int m_cloneCnt; // Mark of when userp was set
|
||||
// AstType is 2 bytes, so we can stick another 6 bytes after it to utilize what would
|
||||
// otherwise be padding (on a 64-bit system). We stick the attribute flags, broken state,
|
||||
// and the clone count here.
|
||||
|
||||
struct {
|
||||
bool didWidth : 1; // Did V3Width computation
|
||||
bool doingWidth : 1; // Inside V3Width
|
||||
bool protect : 1; // Protect name if protection is on
|
||||
// Space for more flags here (there must be 8 bits in total)
|
||||
uint8_t unused : 5;
|
||||
} m_flags; // Attribute flags
|
||||
|
||||
// State variable used by V3Broken for consistency checking. The top bit of this is byte is a
|
||||
// flag, representing V3Broken is currently proceeding under this node. The bottom 7 bits are
|
||||
// a generation number. This is hot when --debug-checks so we access as a whole to avoid bit
|
||||
// field masking resulting in unnecessary read-modify-write ops.
|
||||
uint8_t m_brokenState = 0;
|
||||
|
||||
int m_cloneCnt; // Sequence number for when last clone was made
|
||||
|
||||
#if defined(__x86_64__) && defined(__gnu_linux__)
|
||||
// Only assert this on known platforms, as it only affects performance, not correctness
|
||||
static_assert(sizeof(m_type) + sizeof(m_flags) + sizeof(m_brokenState) + sizeof(m_cloneCnt)
|
||||
<= sizeof(void*),
|
||||
"packing requires padding");
|
||||
#endif
|
||||
|
||||
AstNodeDType* m_dtypep; // Data type of output or assignment (etc)
|
||||
AstNode* m_headtailp; // When at begin/end of list, the opposite end of the list
|
||||
@@ -1381,12 +1404,6 @@ class AstNode VL_NOT_FINAL {
|
||||
AstNode* m_clonep; // Pointer to clone of/ source of this module (for *LAST* cloneTree() ONLY)
|
||||
static int s_cloneCntGbl; // Count of which userp is set
|
||||
|
||||
// Attributes
|
||||
bool m_didWidth : 1; // Did V3Width computation
|
||||
bool m_doingWidth : 1; // Inside V3Width
|
||||
bool m_protect : 1; // Protect name if protection is on
|
||||
// // Space for more bools here
|
||||
|
||||
// This member ordering both allows 64 bit alignment and puts associated data together
|
||||
VNUser m_user1u; // Contains any information the user iteration routine wants
|
||||
uint32_t m_user1Cnt; // Mark of when userp was set
|
||||
@@ -1432,7 +1449,7 @@ private:
|
||||
public:
|
||||
static void relinkOneLink(AstNode*& pointpr, AstNode* newp);
|
||||
// cppcheck-suppress functionConst
|
||||
void debugTreeChange(const char* prefix, int lineno, bool next);
|
||||
static void debugTreeChange(const AstNode* nodep, const char* prefix, int lineno, bool next);
|
||||
|
||||
protected:
|
||||
// CONSTRUCTORS
|
||||
@@ -1489,9 +1506,14 @@ public:
|
||||
AstNode* firstAbovep() const { // Returns nullptr when second or later in list
|
||||
return ((backp() && backp()->nextp() != this) ? backp() : nullptr);
|
||||
}
|
||||
bool brokeExists() const;
|
||||
bool brokeExistsAbove() const;
|
||||
bool brokeExistsBelow() const;
|
||||
uint8_t brokenState() const { return m_brokenState; }
|
||||
void brokenState(uint8_t value) { m_brokenState = value; }
|
||||
|
||||
// Used by AstNode::broken()
|
||||
bool brokeExists() const { return V3Broken::isLinkable(this); }
|
||||
bool brokeExistsAbove() const { return brokeExists() && (m_brokenState >> 7); }
|
||||
bool brokeExistsBelow() const { return brokeExists() && !(m_brokenState >> 7); }
|
||||
// Note: brokeExistsBelow is not quite precise, as it is true for sibling nodes as well
|
||||
|
||||
// CONSTRUCTORS
|
||||
virtual ~AstNode() = default;
|
||||
@@ -1500,21 +1522,20 @@ public:
|
||||
static void operator delete(void* obj, size_t size);
|
||||
#endif
|
||||
|
||||
// CONSTANT ACCESSORS
|
||||
static int instrCountBranch() { return 4; } ///< Instruction cycles to branch
|
||||
static int instrCountDiv() { return 10; } ///< Instruction cycles to divide
|
||||
static int instrCountDpi() { return 1000; } ///< Instruction cycles to call user function
|
||||
static int instrCountLd() { return 2; } ///< Instruction cycles to load memory
|
||||
static int instrCountMul() { return 3; } ///< Instruction cycles to multiply integers
|
||||
static int instrCountPli() { return 20; } ///< Instruction cycles to call pli routines
|
||||
static int instrCountDouble() { return 8; } ///< Instruction cycles to convert or do floats
|
||||
static int instrCountDoubleDiv() { return 40; } ///< Instruction cycles to divide floats
|
||||
static int instrCountDoubleTrig() { return 200; } ///< Instruction cycles to do trigonomics
|
||||
static int instrCountString() { return 100; } ///< Instruction cycles to do string ops
|
||||
/// Instruction cycles to call subroutine
|
||||
static int instrCountCall() { return instrCountBranch() + 10; }
|
||||
/// Instruction cycles to determine simulation time
|
||||
static int instrCountTime() { return instrCountCall() + 5; }
|
||||
// CONSTANTS
|
||||
// The following are relative dynamic costs (~ execution cycle count) of various operations.
|
||||
// They are used by V3InstCount to estimate the relative execution time of code fragments.
|
||||
static constexpr int INSTR_COUNT_BRANCH = 4; // Branch
|
||||
static constexpr int INSTR_COUNT_CALL = INSTR_COUNT_BRANCH + 10; // Subroutine call
|
||||
static constexpr int INSTR_COUNT_LD = 2; // Load memory
|
||||
static constexpr int INSTR_COUNT_INT_MUL = 3; // Integer multiply
|
||||
static constexpr int INSTR_COUNT_INT_DIV = 10; // Integer divide
|
||||
static constexpr int INSTR_COUNT_DBL = 8; // Convert or do float ops
|
||||
static constexpr int INSTR_COUNT_DBL_DIV = 40; // Double divide
|
||||
static constexpr int INSTR_COUNT_DBL_TRIG = 200; // Double trigonometric ops
|
||||
static constexpr int INSTR_COUNT_STR = 100; // String ops
|
||||
static constexpr int INSTR_COUNT_TIME = INSTR_COUNT_CALL + 5; // Determine simulation time
|
||||
static constexpr int INSTR_COUNT_PLI = 20; // PLI routines
|
||||
|
||||
// ACCESSORS
|
||||
virtual string name() const { return ""; }
|
||||
@@ -1545,17 +1566,17 @@ public:
|
||||
void fileline(FileLine* fl) { m_fileline = fl; }
|
||||
bool width1() const;
|
||||
int widthInstrs() const;
|
||||
void didWidth(bool flag) { m_didWidth = flag; }
|
||||
bool didWidth() const { return m_didWidth; }
|
||||
void didWidth(bool flag) { m_flags.didWidth = flag; }
|
||||
bool didWidth() const { return m_flags.didWidth; }
|
||||
bool didWidthAndSet() {
|
||||
if (didWidth()) return true;
|
||||
didWidth(true);
|
||||
return false;
|
||||
}
|
||||
bool doingWidth() const { return m_doingWidth; }
|
||||
void doingWidth(bool flag) { m_doingWidth = flag; }
|
||||
bool protect() const { return m_protect; }
|
||||
void protect(bool flag) { m_protect = flag; }
|
||||
bool doingWidth() const { return m_flags.doingWidth; }
|
||||
void doingWidth(bool flag) { m_flags.doingWidth = flag; }
|
||||
bool protect() const { return m_flags.protect; }
|
||||
void protect(bool flag) { m_flags.protect = flag; }
|
||||
|
||||
// TODO stomp these width functions out, and call via dtypep() instead
|
||||
int width() const;
|
||||
@@ -1689,6 +1710,7 @@ public:
|
||||
void dtypeSetSigned32() { dtypep(findSigned32DType()); }
|
||||
void dtypeSetUInt32() { dtypep(findUInt32DType()); } // Twostate
|
||||
void dtypeSetUInt64() { dtypep(findUInt64DType()); } // Twostate
|
||||
void dtypeSetEmptyQueue() { dtypep(findEmptyQueueDType()); }
|
||||
void dtypeSetVoid() { dtypep(findVoidDType()); }
|
||||
|
||||
// Data type locators
|
||||
@@ -1699,6 +1721,7 @@ public:
|
||||
AstNodeDType* findUInt32DType() { return findBasicDType(AstBasicDTypeKwd::UINT32); }
|
||||
AstNodeDType* findUInt64DType() { return findBasicDType(AstBasicDTypeKwd::UINT64); }
|
||||
AstNodeDType* findCHandleDType() { return findBasicDType(AstBasicDTypeKwd::CHANDLE); }
|
||||
AstNodeDType* findEmptyQueueDType() const;
|
||||
AstNodeDType* findVoidDType() const;
|
||||
AstNodeDType* findQueueIndexDType() const;
|
||||
AstNodeDType* findBitDType(int width, int widthMin, VSigning numeric) const;
|
||||
@@ -1707,7 +1730,7 @@ public:
|
||||
VSigning numeric) const;
|
||||
AstNodeDType* findBitRangeDType(const VNumRange& range, int widthMin, VSigning numeric) const;
|
||||
AstNodeDType* findBasicDType(AstBasicDTypeKwd kwd) const;
|
||||
AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
|
||||
static AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
|
||||
|
||||
// METHODS - dump and error
|
||||
void v3errorEnd(std::ostringstream& str) const;
|
||||
@@ -1782,8 +1805,6 @@ public:
|
||||
virtual bool isGateOptimizable() const { return true; }
|
||||
// GateDedupable is a slightly larger superset of GateOptimzable (eg, AstNodeIf)
|
||||
virtual bool isGateDedupable() const { return isGateOptimizable(); }
|
||||
// Needs verilated_heavy.h (uses std::string or some others)
|
||||
virtual bool isHeavy() const { return false; }
|
||||
// Else creates output or exits, etc, not unconsumed
|
||||
virtual bool isOutputter() const { return false; }
|
||||
// Else a AstTime etc which output can't be predicted from input
|
||||
@@ -1829,16 +1850,62 @@ protected:
|
||||
private:
|
||||
void iterateListBackwards(AstNVisitor& v);
|
||||
|
||||
// CONVERSION
|
||||
// For internal use only.
|
||||
template <typename T> inline static bool privateTypeTest(const AstNode* nodep);
|
||||
|
||||
template <typename TargetType, typename DeclType> constexpr static bool uselessCast() {
|
||||
using NonRef = typename std::remove_reference<DeclType>::type;
|
||||
using NonPtr = typename std::remove_pointer<NonRef>::type;
|
||||
using NonCV = typename std::remove_cv<NonPtr>::type;
|
||||
return std::is_base_of<TargetType, NonCV>::value;
|
||||
}
|
||||
|
||||
template <typename TargetType, typename DeclType> constexpr static bool impossibleCast() {
|
||||
using NonRef = typename std::remove_reference<DeclType>::type;
|
||||
using NonPtr = typename std::remove_pointer<NonRef>::type;
|
||||
using NonCV = typename std::remove_cv<NonPtr>::type;
|
||||
return !std::is_base_of<NonCV, TargetType>::value;
|
||||
}
|
||||
|
||||
public:
|
||||
// These for use by VN_IS macro only
|
||||
template <class T> static bool privateIs(const AstNode* nodep);
|
||||
// For use via the VN_IS macro only
|
||||
template <typename T, typename E> inline static bool privateIs(const AstNode* nodep) {
|
||||
static_assert(!uselessCast<T, E>(), "Unnecessary VN_IS, node known to have target type.");
|
||||
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_IS, node cannot be this type.");
|
||||
return nodep && privateTypeTest<T>(nodep);
|
||||
}
|
||||
|
||||
// These for use by VN_CAST macro only
|
||||
template <class T> static T* privateCast(AstNode* nodep);
|
||||
// For use via the VN_CAST macro only
|
||||
template <typename T, typename E> inline static T* privateCast(AstNode* nodep) {
|
||||
static_assert(!uselessCast<T, E>(),
|
||||
"Unnecessary VN_CAST, node known to have target type.");
|
||||
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_CAST, node cannot be this type.");
|
||||
return nodep && privateTypeTest<T>(nodep) ? reinterpret_cast<T*>(nodep) : nullptr;
|
||||
}
|
||||
template <typename T, typename E> inline static const T* privateCast(const AstNode* nodep) {
|
||||
static_assert(!uselessCast<T, E>(),
|
||||
"Unnecessary VN_CAST, node known to have target type.");
|
||||
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_CAST, node cannot be this type.");
|
||||
return nodep && privateTypeTest<T>(nodep) ? reinterpret_cast<const T*>(nodep) : nullptr;
|
||||
}
|
||||
|
||||
// These for use by VN_CAST_CONST macro only
|
||||
template <class T> static const T* privateConstCast(const AstNode* nodep);
|
||||
// For use via the VN_AS macro only
|
||||
template <typename T, typename E> inline static T* privateAs(AstNode* nodep) {
|
||||
static_assert(!uselessCast<T, E>(), "Unnecessary VN_AS, node known to have target type.");
|
||||
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_AS, node cannot be this type.");
|
||||
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
|
||||
"AstNode is not of expected type, but instead has type '" << nodep->typeName()
|
||||
<< "'");
|
||||
return reinterpret_cast<T*>(nodep);
|
||||
}
|
||||
template <typename T, typename E> inline static const T* privateAs(const AstNode* nodep) {
|
||||
static_assert(!uselessCast<T, E>(), "Unnecessary VN_AS, node known to have target type.");
|
||||
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_AS, node cannot be this type.");
|
||||
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
|
||||
"AstNode is not of expected type, but instead has type '" << nodep->typeName()
|
||||
<< "'");
|
||||
return reinterpret_cast<const T*>(nodep);
|
||||
}
|
||||
};
|
||||
|
||||
// Specialisations of privateIs/privateCast
|
||||
@@ -1887,7 +1954,7 @@ public:
|
||||
virtual bool cleanOut() const = 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 VN_IS(this, CvtPackString); }
|
||||
bool isOpaque() const { return VN_IS(this, CvtPackString); }
|
||||
};
|
||||
|
||||
class AstNodeTermop VL_NOT_FINAL : public AstNodeMath {
|
||||
@@ -2076,9 +2143,7 @@ public:
|
||||
AstNode* expr1p() const { return op2p(); } // op2 = If true...
|
||||
AstNode* expr2p() const { return op3p(); } // op3 = If false...
|
||||
virtual string emitVerilog() override { return "%k(%l %f? %r %k: %t)"; }
|
||||
virtual string emitC() override {
|
||||
return "VL_COND_%nq%lq%rq%tq(%nw,%lw,%rw,%tw, %P, %li, %ri, %ti)";
|
||||
}
|
||||
virtual string emitC() override { return "VL_COND_%nq%lq%rq%tq(%nw, %P, %li, %ri, %ti)"; }
|
||||
virtual bool cleanOut() const override { return false; } // clean if e1 & e2 clean
|
||||
virtual bool cleanLhs() const override { return true; }
|
||||
virtual bool cleanRhs() const override { return false; }
|
||||
@@ -2086,7 +2151,7 @@ public:
|
||||
virtual bool sizeMattersLhs() const override { return false; }
|
||||
virtual bool sizeMattersRhs() const override { return false; }
|
||||
virtual bool sizeMattersThs() const override { return false; }
|
||||
virtual int instrCount() const override { return instrCountBranch(); }
|
||||
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
virtual AstNode* cloneType(AstNode* condp, AstNode* expr1p, AstNode* expr2p) = 0;
|
||||
};
|
||||
|
||||
@@ -2131,7 +2196,7 @@ public:
|
||||
AstNode* fromp() const { return op1p(); }
|
||||
AstNode* rhsp() const { return op2p(); }
|
||||
AstNode* thsp() const { return op3p(); }
|
||||
AstAttrOf* attrp() const { return VN_CAST(op4p(), AttrOf); }
|
||||
AstAttrOf* attrp() const { return VN_AS(op4p(), AttrOf); }
|
||||
void fromp(AstNode* nodep) { return setOp1p(nodep); }
|
||||
void rhsp(AstNode* nodep) { return setOp2p(nodep); }
|
||||
void thsp(AstNode* nodep) { return setOp3p(nodep); }
|
||||
@@ -2221,7 +2286,7 @@ public:
|
||||
AstNode* incsp() const { return op3p(); } // op3 = increment statements
|
||||
AstNode* bodysp() const { return op4p(); } // op4 = body of loop
|
||||
virtual bool isGateOptimizable() const override { return false; }
|
||||
virtual int instrCount() const override { return instrCountBranch(); }
|
||||
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
};
|
||||
|
||||
@@ -2248,7 +2313,7 @@ public:
|
||||
void addElsesp(AstNode* newp) { addOp3p(newp); }
|
||||
virtual bool isGateOptimizable() const override { return false; }
|
||||
virtual bool isGateDedupable() const override { return true; }
|
||||
virtual int instrCount() const override { return instrCountBranch(); }
|
||||
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
void branchPred(VBranchPred flag) { m_branchPred = flag; }
|
||||
VBranchPred branchPred() const { return m_branchPred; }
|
||||
@@ -2266,10 +2331,10 @@ protected:
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeCase)
|
||||
virtual int instrCount() const override { return instrCountBranch(); }
|
||||
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
AstNode* exprp() const { return op1p(); } // op1 = case condition <expression>
|
||||
AstCaseItem* itemsp() const {
|
||||
return VN_CAST(op2p(), CaseItem);
|
||||
return VN_AS(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); }
|
||||
@@ -2445,6 +2510,7 @@ public:
|
||||
return (isString() ? "N" : isWide() ? "W" : isQuad() ? "Q" : "I");
|
||||
}
|
||||
string cType(const string& name, bool forFunc, bool isRef) const;
|
||||
bool isLiteralType() const; // Does this represent a C++ LiteralType? (can be constexpr)
|
||||
|
||||
private:
|
||||
class CTypeRecursed;
|
||||
@@ -2482,10 +2548,10 @@ public:
|
||||
// For basicp() we reuse the size to indicate a "fake" basic type of same size
|
||||
virtual AstBasicDType* basicp() const override {
|
||||
return (isFourstate()
|
||||
? VN_CAST(findLogicRangeDType(VNumRange{width() - 1, 0}, width(), numeric()),
|
||||
BasicDType)
|
||||
: VN_CAST(findBitRangeDType(VNumRange{width() - 1, 0}, width(), numeric()),
|
||||
BasicDType));
|
||||
? VN_AS(findLogicRangeDType(VNumRange{width() - 1, 0}, width(), numeric()),
|
||||
BasicDType)
|
||||
: VN_AS(findBitRangeDType(VNumRange{width() - 1, 0}, width(), numeric()),
|
||||
BasicDType));
|
||||
}
|
||||
virtual AstNodeDType* skipRefp() const override { return (AstNodeDType*)this; }
|
||||
virtual AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
@@ -2501,7 +2567,7 @@ public:
|
||||
virtual string name() const override { return m_name; }
|
||||
virtual void name(const string& flag) override { m_name = flag; }
|
||||
AstMemberDType* membersp() const {
|
||||
return VN_CAST(op1p(), MemberDType);
|
||||
return VN_AS(op1p(), MemberDType);
|
||||
} // op1 = AstMember list
|
||||
void addMembersp(AstNode* nodep) { addNOp1p(nodep); }
|
||||
bool packed() const { return m_packed; }
|
||||
@@ -2544,18 +2610,18 @@ public:
|
||||
if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep();
|
||||
}
|
||||
virtual bool same(const AstNode* samep) const override {
|
||||
const AstNodeArrayDType* asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
return (hi() == asamep->hi() && subDTypep() == asamep->subDTypep()
|
||||
&& rangenp()->sameTree(asamep->rangenp()));
|
||||
} // HashedDT doesn't recurse, so need to check children
|
||||
virtual bool similarDType(AstNodeDType* samep) const override {
|
||||
const AstNodeArrayDType* asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
return (asamep && type() == samep->type() && hi() == asamep->hi()
|
||||
&& rangenp()->sameTree(asamep->rangenp())
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp()));
|
||||
}
|
||||
virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); }
|
||||
AstNodeDType* childDTypep() const { return VN_AS(op1p(), NodeDType); }
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
virtual AstNodeDType* subDTypep() const override {
|
||||
return m_refDTypep ? m_refDTypep : childDTypep();
|
||||
@@ -2563,7 +2629,7 @@ public:
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||
virtual AstNodeDType* virtRefDTypep() const override { return m_refDTypep; }
|
||||
virtual void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
|
||||
AstRange* rangep() const { return VN_CAST(op2p(), Range); } // op2 = Array(s) of variable
|
||||
AstRange* rangep() const { return VN_AS(op2p(), Range); } // op2 = Array(s) of variable
|
||||
void rangep(AstRange* nodep);
|
||||
// METHODS
|
||||
virtual AstBasicDType* basicp() const override {
|
||||
@@ -2621,7 +2687,6 @@ class AstNodeCCall VL_NOT_FINAL : public AstNodeStmt {
|
||||
// A call of a C++ function, perhaps a AstCFunc or perhaps globally named
|
||||
// Functions are not statements, while tasks are. AstNodeStmt needs isStatement() to deal.
|
||||
AstCFunc* m_funcp;
|
||||
string m_selfPointer; // Output code object pointer (e.g.: 'this')
|
||||
string m_argTypes;
|
||||
|
||||
protected:
|
||||
@@ -2636,9 +2701,9 @@ public:
|
||||
virtual void dump(std::ostream& str = std::cout) const override;
|
||||
virtual void cloneRelink() override;
|
||||
virtual const char* broken() const override;
|
||||
virtual int instrCount() const override { return instrCountCall(); }
|
||||
virtual int instrCount() const override { return INSTR_COUNT_CALL; }
|
||||
virtual bool same(const AstNode* samep) const override {
|
||||
const AstNodeCCall* asamep = static_cast<const AstNodeCCall*>(samep);
|
||||
const AstNodeCCall* const asamep = static_cast<const AstNodeCCall*>(samep);
|
||||
return (funcp() == asamep->funcp() && argTypes() == asamep->argTypes());
|
||||
}
|
||||
AstNode* exprsp() const { return op2p(); } // op2 = expressions to print
|
||||
@@ -2647,9 +2712,6 @@ public:
|
||||
virtual bool isPure() const override;
|
||||
virtual bool isOutputter() const override { return !isPure(); }
|
||||
AstCFunc* funcp() const { return m_funcp; }
|
||||
string selfPointer() const { return m_selfPointer; }
|
||||
void selfPointer(const string& value) { m_selfPointer = value; }
|
||||
string selfPointerProtect(bool useSelfForThis) const;
|
||||
void argTypes(const string& str) { m_argTypes = str; }
|
||||
string argTypes() const { return m_argTypes; }
|
||||
// op1p reserved for AstCMethodCall
|
||||
@@ -2673,11 +2735,13 @@ private:
|
||||
bool m_dpiContext : 1; // DPI import context
|
||||
bool m_dpiOpenChild : 1; // DPI import open array child wrapper
|
||||
bool m_dpiTask : 1; // DPI import task (vs. void function)
|
||||
bool m_dpiTraceInit : 1; // DPI trace_init
|
||||
bool m_isConstructor : 1; // Class constructor
|
||||
bool m_isHideLocal : 1; // Verilog local
|
||||
bool m_isHideProtected : 1; // Verilog protected
|
||||
bool m_pure : 1; // DPI import pure (vs. virtual pure)
|
||||
bool m_pureVirtual : 1; // Pure virtual
|
||||
bool m_underGenerate : 1; // Under generate (for warning)
|
||||
bool m_virtual : 1; // Virtual method in class
|
||||
VLifetime m_lifetime; // Lifetime
|
||||
protected:
|
||||
@@ -2695,11 +2759,13 @@ protected:
|
||||
, m_dpiContext{false}
|
||||
, m_dpiOpenChild{false}
|
||||
, m_dpiTask{false}
|
||||
, m_dpiTraceInit{false}
|
||||
, m_isConstructor{false}
|
||||
, m_isHideLocal{false}
|
||||
, m_isHideProtected{false}
|
||||
, m_pure{false}
|
||||
, m_pureVirtual{false}
|
||||
, m_underGenerate{false}
|
||||
, m_virtual{false} {
|
||||
addNOp3p(stmtsp);
|
||||
cname(name); // Might be overridden by dpi import/export
|
||||
@@ -2728,7 +2794,7 @@ public:
|
||||
AstNode* stmtsp() const { return op3p(); } // op3 = List of statements
|
||||
void addStmtsp(AstNode* nodep) { addNOp3p(nodep); }
|
||||
// op4 = scope name
|
||||
AstScopeName* scopeNamep() const { return VN_CAST(op4p(), ScopeName); }
|
||||
AstScopeName* scopeNamep() const { return VN_AS(op4p(), ScopeName); }
|
||||
// MORE ACCESSORS
|
||||
void dpiOpenParentInc() { ++m_dpiOpenParent; }
|
||||
void dpiOpenParentClear() { m_dpiOpenParent = 0; }
|
||||
@@ -2756,6 +2822,8 @@ public:
|
||||
bool dpiOpenChild() const { return m_dpiOpenChild; }
|
||||
void dpiTask(bool flag) { m_dpiTask = flag; }
|
||||
bool dpiTask() const { return m_dpiTask; }
|
||||
void dpiTraceInit(bool flag) { m_dpiTraceInit = flag; }
|
||||
bool dpiTraceInit() const { return m_dpiTraceInit; }
|
||||
void isConstructor(bool flag) { m_isConstructor = flag; }
|
||||
bool isConstructor() const { return m_isConstructor; }
|
||||
bool isHideLocal() const { return m_isHideLocal; }
|
||||
@@ -2766,6 +2834,8 @@ public:
|
||||
bool pure() const { return m_pure; }
|
||||
void pureVirtual(bool flag) { m_pureVirtual = flag; }
|
||||
bool pureVirtual() const { return m_pureVirtual; }
|
||||
void underGenerate(bool flag) { m_underGenerate = flag; }
|
||||
bool underGenerate() const { return m_underGenerate; }
|
||||
void isVirtual(bool flag) { m_virtual = flag; }
|
||||
bool isVirtual() const { return m_virtual; }
|
||||
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
|
||||
@@ -2806,7 +2876,6 @@ public:
|
||||
return m_taskp && m_taskp->isGateOptimizable();
|
||||
}
|
||||
string dotted() const { return m_dotted; } // * = Scope name or ""
|
||||
string prettyDotted() const { return prettyName(dotted()); }
|
||||
string inlinedDots() const { return m_inlinedDots; }
|
||||
void inlinedDots(const string& flag) { m_inlinedDots = flag; }
|
||||
AstNodeFTask* taskp() const { return m_taskp; } // [After Link] Pointer to variable
|
||||
@@ -2824,7 +2893,7 @@ public:
|
||||
AstNode* pinsp() const { return op3p(); }
|
||||
void addPinsp(AstNode* nodep) { addOp3p(nodep); }
|
||||
// op4 = scope tracking
|
||||
AstScopeName* scopeNamep() const { return VN_CAST(op4p(), ScopeName); }
|
||||
AstScopeName* scopeNamep() const { return VN_AS(op4p(), ScopeName); }
|
||||
void scopeNamep(AstNode* nodep) { setNOp4p(nodep); }
|
||||
};
|
||||
|
||||
@@ -2834,7 +2903,7 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
|
||||
// excluding $unit package stuff
|
||||
private:
|
||||
string m_name; // Name of the module
|
||||
string m_origName; // Name of the module, ignoring name() changes, for dot lookup
|
||||
const string m_origName; // Name of the module, ignoring name() changes, for dot lookup
|
||||
string m_hierName; // Hierarchical name for errors, etc.
|
||||
bool m_modPublic : 1; // Module has public references
|
||||
bool m_modTrace : 1; // Tracing this module
|
||||
@@ -2845,8 +2914,6 @@ private:
|
||||
bool m_recursive : 1; // Recursive module
|
||||
bool m_recursiveClone : 1; // If recursive, what module it clones, otherwise nullptr
|
||||
int m_level = 0; // 1=top module, 2=cell off top module, ...
|
||||
int m_varNum = 0; // Incrementing variable number
|
||||
int m_typeNum = 0; // Incrementing implicit type number
|
||||
VLifetime m_lifetime; // Lifetime
|
||||
VTimescale m_timeunit; // Global time unit
|
||||
VOptionBool m_unconnectedDrive; // State of `unconnected_drive
|
||||
@@ -2871,7 +2938,7 @@ public:
|
||||
virtual string name() const override { return m_name; }
|
||||
virtual bool timescaleMatters() const = 0;
|
||||
AstNode* stmtsp() const { return op2p(); } // op2 = List of statements
|
||||
AstActive* activesp() const { return VN_CAST(op3p(), Active); } // op3 = List of i/sblocks
|
||||
AstActive* activesp() const { return VN_AS(op3p(), Active); } // op3 = List of i/sblocks
|
||||
// METHODS
|
||||
void addInlinesp(AstNode* nodep) { addOp1p(nodep); }
|
||||
void addStmtp(AstNode* nodep) { addNOp2p(nodep); }
|
||||
@@ -2886,8 +2953,6 @@ public:
|
||||
void level(int level) { m_level = level; }
|
||||
int level() const { return m_level; }
|
||||
bool isTop() const { return level() == 1; }
|
||||
int varNumGetInc() { return ++m_varNum; }
|
||||
int typeNumGetInc() { return ++m_typeNum; }
|
||||
void modPublic(bool flag) { m_modPublic = flag; }
|
||||
bool modPublic() const { return m_modPublic; }
|
||||
void modTrace(bool flag) { m_modTrace = flag; }
|
||||
|
||||
+6
-6
@@ -34,7 +34,7 @@ inline int AstNode::widthInstrs() const {
|
||||
return (!dtypep() ? 1 : (dtypep()->isWide() ? dtypep()->widthWords() : 1));
|
||||
}
|
||||
inline bool AstNode::isDouble() const {
|
||||
return dtypep() && VN_IS(dtypep(), BasicDType) && VN_CAST(dtypep(), BasicDType)->isDouble();
|
||||
return dtypep() && VN_IS(dtypep(), BasicDType) && VN_AS(dtypep(), BasicDType)->isDouble();
|
||||
}
|
||||
inline bool AstNode::isString() const {
|
||||
return dtypep() && dtypep()->basicp() && dtypep()->basicp()->isString();
|
||||
@@ -42,19 +42,19 @@ inline bool AstNode::isString() const {
|
||||
inline bool AstNode::isSigned() const { return dtypep() && dtypep()->isSigned(); }
|
||||
|
||||
inline bool AstNode::isZero() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isEqZero());
|
||||
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqZero());
|
||||
}
|
||||
inline bool AstNode::isNeqZero() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isNeqZero());
|
||||
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isNeqZero());
|
||||
}
|
||||
inline bool AstNode::isOne() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isEqOne());
|
||||
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqOne());
|
||||
}
|
||||
inline bool AstNode::isAllOnes() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->isEqAllOnes());
|
||||
return (VN_IS(this, Const) && VN_AS(this, Const)->isEqAllOnes());
|
||||
}
|
||||
inline bool AstNode::isAllOnesV() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->isEqAllOnesV());
|
||||
return (VN_IS(this, Const) && VN_AS(this, Const)->isEqAllOnesV());
|
||||
}
|
||||
inline bool AstNode::sameTree(const AstNode* node2p) const {
|
||||
return sameTreeIter(this, node2p, true, false);
|
||||
|
||||
+117
-110
@@ -44,7 +44,7 @@ const char* AstIfaceRefDType::broken() const {
|
||||
}
|
||||
|
||||
AstIface* AstIfaceRefDType::ifaceViaCellp() const {
|
||||
return ((m_cellp && m_cellp->modp()) ? VN_CAST(m_cellp->modp(), Iface) : m_ifacep);
|
||||
return ((m_cellp && m_cellp->modp()) ? VN_AS(m_cellp->modp(), Iface) : m_ifacep);
|
||||
}
|
||||
|
||||
const char* AstNodeVarRef::broken() const {
|
||||
@@ -73,13 +73,13 @@ const char* AstAddrOfCFunc::broken() const {
|
||||
}
|
||||
|
||||
int AstNodeSel::bitConst() const {
|
||||
AstConst* constp = VN_CAST(bitp(), Const);
|
||||
const AstConst* const constp = VN_AS(bitp(), Const);
|
||||
return (constp ? constp->toSInt() : 0);
|
||||
}
|
||||
|
||||
void AstNodeUOrStructDType::repairMemberCache() {
|
||||
clearCache();
|
||||
for (AstMemberDType* itemp = membersp(); itemp; itemp = VN_CAST(itemp->nextp(), MemberDType)) {
|
||||
for (AstMemberDType* itemp = membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
if (m_members.find(itemp->name()) != m_members.end()) {
|
||||
itemp->v3error("Duplicate declaration of member name: " << itemp->prettyNameQ());
|
||||
} else {
|
||||
@@ -90,7 +90,7 @@ void AstNodeUOrStructDType::repairMemberCache() {
|
||||
|
||||
const char* AstNodeUOrStructDType::broken() const {
|
||||
std::unordered_set<AstMemberDType*> exists;
|
||||
for (AstMemberDType* itemp = membersp(); itemp; itemp = VN_CAST(itemp->nextp(), MemberDType)) {
|
||||
for (AstMemberDType* itemp = membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
exists.insert(itemp);
|
||||
}
|
||||
for (MemberNameMap::const_iterator it = m_members.begin(); it != m_members.end(); ++it) {
|
||||
@@ -121,7 +121,8 @@ const char* AstNodeCCall::broken() const {
|
||||
return nullptr;
|
||||
}
|
||||
bool AstNodeCCall::isPure() const { return funcp()->pure(); }
|
||||
string AstNodeCCall::selfPointerProtect(bool useSelfForThis) const {
|
||||
|
||||
string AstCCall::selfPointerProtect(bool useSelfForThis) const {
|
||||
const string& sp
|
||||
= useSelfForThis ? VString::replaceWord(selfPointer(), "this", "vlSelf") : selfPointer();
|
||||
return VIdProtect::protectWordsIf(sp, protect());
|
||||
@@ -196,30 +197,6 @@ AstNodeBiop* AstEq::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
||||
}
|
||||
}
|
||||
|
||||
AstNodeBiop* AstGte::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
||||
if (lhsp->isString() && rhsp->isString()) {
|
||||
return new AstGteN(fl, lhsp, rhsp);
|
||||
} else if (lhsp->isDouble() && rhsp->isDouble()) {
|
||||
return new AstGteD(fl, lhsp, rhsp);
|
||||
} else if (lhsp->isSigned() && rhsp->isSigned()) {
|
||||
return new AstGteS(fl, lhsp, rhsp);
|
||||
} else {
|
||||
return new AstGte(fl, lhsp, rhsp);
|
||||
}
|
||||
}
|
||||
|
||||
AstNodeBiop* AstLte::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
||||
if (lhsp->isString() && rhsp->isString()) {
|
||||
return new AstLteN(fl, lhsp, rhsp);
|
||||
} else if (lhsp->isDouble() && rhsp->isDouble()) {
|
||||
return new AstLteD(fl, lhsp, rhsp);
|
||||
} else if (lhsp->isSigned() && rhsp->isSigned()) {
|
||||
return new AstLteS(fl, lhsp, rhsp);
|
||||
} else {
|
||||
return new AstLte(fl, lhsp, rhsp);
|
||||
}
|
||||
}
|
||||
|
||||
AstNodeBiop* AstEqWild::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
||||
if (lhsp->isString() && rhsp->isString()) {
|
||||
return new AstEqN(fl, lhsp, rhsp);
|
||||
@@ -237,11 +214,11 @@ AstExecGraph::AstExecGraph(FileLine* fileline)
|
||||
AstExecGraph::~AstExecGraph() { VL_DO_DANGLING(delete m_depGraphp, m_depGraphp); }
|
||||
|
||||
AstNode* AstInsideRange::newAndFromInside(AstNode* exprp, AstNode* lhsp, AstNode* rhsp) {
|
||||
AstNode* ap = new AstGte(fileline(), exprp->cloneTree(true), lhsp);
|
||||
AstNode* bp = new AstLte(fileline(), exprp->cloneTree(true), rhsp);
|
||||
AstNode* const ap = new AstGte(fileline(), exprp->cloneTree(true), lhsp);
|
||||
AstNode* const bp = new AstLte(fileline(), exprp->cloneTree(true), rhsp);
|
||||
ap->fileline()->modifyWarnOff(V3ErrorCode::UNSIGNED, true);
|
||||
bp->fileline()->modifyWarnOff(V3ErrorCode::CMPCONST, true);
|
||||
AstNode* newp = new AstAnd(fileline(), ap, bp);
|
||||
AstNode* const newp = new AstAnd(fileline(), ap, bp);
|
||||
return newp;
|
||||
}
|
||||
|
||||
@@ -249,11 +226,11 @@ AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) {
|
||||
bool success = false;
|
||||
if (literal[0] == '"') {
|
||||
// This is a string
|
||||
string v = literal.substr(1, literal.find('"', 1) - 1);
|
||||
const string v = literal.substr(1, literal.find('"', 1) - 1);
|
||||
return new AstConst(fl, AstConst::VerilogStringLiteral(), v);
|
||||
} else if (literal.find_first_of(".eEpP") != string::npos) {
|
||||
// This may be a real
|
||||
double v = VString::parseDouble(literal, &success);
|
||||
const double v = VString::parseDouble(literal, &success);
|
||||
if (success) return new AstConst(fl, AstConst::RealDouble(), v);
|
||||
}
|
||||
if (!success) {
|
||||
@@ -265,9 +242,9 @@ AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) {
|
||||
// the string after the number, this is invalid C and we try
|
||||
// the Verilog literal parser.
|
||||
char* endp;
|
||||
int v = strtol(literal.c_str(), &endp, 0);
|
||||
const int v = strtol(literal.c_str(), &endp, 0);
|
||||
if ((v != 0) && (endp[0] == 0)) { // C literal
|
||||
return new AstConst(fl, AstConst::WidthedValue(), 32, v);
|
||||
return new AstConst(fl, AstConst::Signed32(), v);
|
||||
} else { // Try a Verilog literal (fatals if not)
|
||||
return new AstConst(fl, AstConst::StringToParse(), literal.c_str());
|
||||
}
|
||||
@@ -359,7 +336,7 @@ string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string&
|
||||
|
||||
string AstVar::vlEnumType() const {
|
||||
string arg;
|
||||
AstBasicDType* bdtypep = basicp();
|
||||
const AstBasicDType* const bdtypep = basicp();
|
||||
const bool strtype = bdtypep && bdtypep->keyword() == AstBasicDTypeKwd::STRING;
|
||||
if (bdtypep && bdtypep->keyword() == AstBasicDTypeKwd::CHARPTR) {
|
||||
return "VLVT_PTR";
|
||||
@@ -400,7 +377,7 @@ string AstVar::vlEnumDir() const {
|
||||
out += "|VLVF_PUB_RD";
|
||||
}
|
||||
//
|
||||
if (AstBasicDType* bdtypep = basicp()) {
|
||||
if (const AstBasicDType* const bdtypep = basicp()) {
|
||||
if (bdtypep->keyword().isDpiCLayout()) out += "|VLVF_DPI_CLAY";
|
||||
}
|
||||
return out;
|
||||
@@ -412,7 +389,7 @@ string AstVar::vlPropDecl(const string& propName) const {
|
||||
std::vector<int> ulims; // Unpacked dimension limits
|
||||
for (const AstNodeDType* dtp = dtypep(); dtp;) {
|
||||
dtp = dtp->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
|
||||
if (const AstNodeArrayDType* const adtypep = VN_CAST_CONST(dtp, NodeArrayDType)) {
|
||||
if (const AstNodeArrayDType* const adtypep = VN_CAST(dtp, NodeArrayDType)) {
|
||||
ulims.push_back(adtypep->declRange().left());
|
||||
ulims.push_back(adtypep->declRange().right());
|
||||
dtp = adtypep->subDTypep();
|
||||
@@ -498,7 +475,7 @@ public:
|
||||
string convert(const AstVar* varp) const {
|
||||
if (varp->isDpiOpenArray()) {
|
||||
return openArray(varp);
|
||||
} else if (const AstBasicDType* basicp = varp->basicp()) {
|
||||
} else if (const AstBasicDType* const basicp = varp->basicp()) {
|
||||
if (basicp->isDpiBitVec() || basicp->isDpiLogicVec()) {
|
||||
return bitLogicVector(varp, basicp->isDpiBitVec());
|
||||
} else {
|
||||
@@ -538,9 +515,9 @@ string AstVar::dpiArgType(bool named, bool forReturn) const {
|
||||
|
||||
string AstVar::dpiTmpVarType(const string& varName) const {
|
||||
class converter final : public dpiTypesToStringConverter {
|
||||
string m_name;
|
||||
const string m_name;
|
||||
string arraySuffix(const AstVar* varp, size_t n) const {
|
||||
if (AstUnpackArrayDType* unpackp
|
||||
if (AstUnpackArrayDType* const unpackp
|
||||
= VN_CAST(varp->dtypep()->skipRefp(), UnpackArrayDType)) {
|
||||
// Convert multi dimensional unpacked array to 1D array
|
||||
if (n == 0) n = 1;
|
||||
@@ -606,15 +583,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 = VN_CAST(nodep, Var)) {
|
||||
if (AstVar* const anodep = VN_CAST(nodep, Var)) {
|
||||
if (anodep->isSc()) {
|
||||
return anodep;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
} else if (VN_IS(nodep, VarRef)) {
|
||||
if (VN_CAST(nodep, VarRef)->varp()->isSc()) {
|
||||
return VN_CAST(nodep, VarRef)->varp();
|
||||
if (VN_AS(nodep, VarRef)->varp()->isSc()) {
|
||||
return VN_AS(nodep, VarRef)->varp();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -665,29 +642,29 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
|
||||
// Legacy compound argument currently just passed through and unused
|
||||
CTypeRecursed info;
|
||||
|
||||
const AstNodeDType* dtypep = this->skipRefp();
|
||||
if (const auto* adtypep = VN_CAST_CONST(dtypep, AssocArrayDType)) {
|
||||
const AstNodeDType* const dtypep = this->skipRefp();
|
||||
if (const auto* const adtypep = VN_CAST(dtypep, AssocArrayDType)) {
|
||||
const CTypeRecursed key = adtypep->keyDTypep()->cTypeRecurse(true);
|
||||
const CTypeRecursed val = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
info.m_type = "VlAssocArray<" + key.m_type + ", " + val.m_type + ">";
|
||||
} else if (const auto* adtypep = VN_CAST_CONST(dtypep, DynArrayDType)) {
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, DynArrayDType)) {
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
info.m_type = "VlQueue<" + sub.m_type + ">";
|
||||
} else if (const auto* adtypep = VN_CAST_CONST(dtypep, QueueDType)) {
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, QueueDType)) {
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
info.m_type = "VlQueue<" + sub.m_type;
|
||||
// + 1 below as VlQueue uses 0 to mean unlimited, 1 to mean size() max is 1
|
||||
if (adtypep->boundp()) info.m_type += ", " + cvtToStr(adtypep->boundConst() + 1);
|
||||
info.m_type += ">";
|
||||
} else if (const auto* adtypep = VN_CAST_CONST(dtypep, ClassRefDType)) {
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, ClassRefDType)) {
|
||||
info.m_type = "VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(adtypep) + ">";
|
||||
} else if (const auto* adtypep = VN_CAST_CONST(dtypep, UnpackArrayDType)) {
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
if (adtypep->isCompound()) compound = true;
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(compound);
|
||||
info.m_type = "VlUnpacked<" + sub.m_type;
|
||||
info.m_type += ", " + cvtToStr(adtypep->declRange().elements());
|
||||
info.m_type += ">";
|
||||
} else if (const AstBasicDType* bdtypep = dtypep->basicp()) {
|
||||
} else if (const AstBasicDType* const bdtypep = dtypep->basicp()) {
|
||||
// We don't print msb()/lsb() as multidim packed would require recursion,
|
||||
// and may confuse users as C++ data is stored always with bit 0 used
|
||||
const string bitvec = (!bdtypep->isOpaque() && !v3Global.opt.protectIds())
|
||||
@@ -727,7 +704,7 @@ uint32_t AstNodeDType::arrayUnpackedElements() {
|
||||
uint32_t entries = 1;
|
||||
for (AstNodeDType* dtypep = this; dtypep;) {
|
||||
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
|
||||
if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
if (AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
entries *= adtypep->elementsConst();
|
||||
dtypep = adtypep->subDTypep();
|
||||
} else {
|
||||
@@ -744,7 +721,7 @@ std::pair<uint32_t, uint32_t> AstNodeDType::dimensions(bool includeBasic) {
|
||||
uint32_t unpacked = 0;
|
||||
for (AstNodeDType* dtypep = this; dtypep;) {
|
||||
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
|
||||
if (const AstNodeArrayDType* adtypep = VN_CAST(dtypep, NodeArrayDType)) {
|
||||
if (const AstNodeArrayDType* const adtypep = VN_CAST(dtypep, NodeArrayDType)) {
|
||||
if (VN_IS(adtypep, PackArrayDType)) {
|
||||
++packed;
|
||||
} else {
|
||||
@@ -752,11 +729,11 @@ std::pair<uint32_t, uint32_t> AstNodeDType::dimensions(bool includeBasic) {
|
||||
}
|
||||
dtypep = adtypep->subDTypep();
|
||||
continue;
|
||||
} else if (const AstQueueDType* qdtypep = VN_CAST(dtypep, QueueDType)) {
|
||||
} else if (const AstQueueDType* const qdtypep = VN_CAST(dtypep, QueueDType)) {
|
||||
unpacked++;
|
||||
dtypep = qdtypep->subDTypep();
|
||||
continue;
|
||||
} else if (const AstBasicDType* adtypep = VN_CAST(dtypep, BasicDType)) {
|
||||
} else if (const AstBasicDType* const adtypep = VN_CAST(dtypep, BasicDType)) {
|
||||
if (includeBasic && (adtypep->isRanged() || adtypep->isString())) packed++;
|
||||
} else if (VN_IS(dtypep, StructDType)) {
|
||||
packed++;
|
||||
@@ -768,37 +745,51 @@ std::pair<uint32_t, uint32_t> AstNodeDType::dimensions(bool includeBasic) {
|
||||
|
||||
int AstNodeDType::widthPow2() const {
|
||||
// I.e. width 30 returns 32, width 32 returns 32.
|
||||
uint32_t width = this->width();
|
||||
const uint32_t width = this->width();
|
||||
for (int p2 = 30; p2 >= 0; p2--) {
|
||||
if (width > (1UL << p2)) return (1UL << (p2 + 1));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool AstNodeDType::isLiteralType() const {
|
||||
if (const auto* const dtypep = VN_CAST(skipRefp(), BasicDType)) {
|
||||
return dtypep->keyword().isLiteralType();
|
||||
} else if (const auto* const dtypep = VN_CAST(skipRefp(), UnpackArrayDType)) {
|
||||
return dtypep->basicp()->isLiteralType();
|
||||
} else if (const auto* const dtypep = VN_CAST(skipRefp(), StructDType)) {
|
||||
// Currently all structs are packed, later this can be expanded to
|
||||
// 'forall members _.isLiteralType()'
|
||||
return dtypep->packed();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// What is the base variable (or const) this dereferences?
|
||||
AstNode* AstArraySel::baseFromp(AstNode* nodep, bool overMembers) {
|
||||
// Else AstArraySel etc; search for the base
|
||||
while (nodep) {
|
||||
if (VN_IS(nodep, ArraySel)) {
|
||||
nodep = VN_CAST(nodep, ArraySel)->fromp();
|
||||
nodep = VN_AS(nodep, ArraySel)->fromp();
|
||||
continue;
|
||||
} else if (VN_IS(nodep, Sel)) {
|
||||
nodep = VN_CAST(nodep, Sel)->fromp();
|
||||
nodep = VN_AS(nodep, Sel)->fromp();
|
||||
continue;
|
||||
} else if (overMembers && VN_IS(nodep, MemberSel)) {
|
||||
nodep = VN_CAST(nodep, MemberSel)->fromp();
|
||||
nodep = VN_AS(nodep, MemberSel)->fromp();
|
||||
continue;
|
||||
}
|
||||
// AstNodeSelPre stashes the associated variable under an ATTROF
|
||||
// of AstAttrType::VAR_BASE/MEMBER_BASE so it isn't constified
|
||||
else if (VN_IS(nodep, AttrOf)) {
|
||||
nodep = VN_CAST(nodep, AttrOf)->fromp();
|
||||
nodep = VN_AS(nodep, AttrOf)->fromp();
|
||||
continue;
|
||||
} else if (VN_IS(nodep, NodePreSel)) {
|
||||
if (VN_CAST(nodep, NodePreSel)->attrp()) {
|
||||
nodep = VN_CAST(nodep, NodePreSel)->attrp();
|
||||
if (VN_AS(nodep, NodePreSel)->attrp()) {
|
||||
nodep = VN_AS(nodep, NodePreSel)->attrp();
|
||||
} else {
|
||||
nodep = VN_CAST(nodep, NodePreSel)->fromp();
|
||||
nodep = VN_AS(nodep, NodePreSel)->fromp();
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
@@ -839,7 +830,7 @@ string AstScope::nameDotless() const {
|
||||
|
||||
string AstScopeName::scopePrettyNameFormatter(AstText* scopeTextp) const {
|
||||
string out;
|
||||
for (AstText* textp = scopeTextp; textp; textp = VN_CAST(textp->nextp(), Text)) {
|
||||
for (AstText* textp = scopeTextp; textp; textp = VN_AS(textp->nextp(), Text)) {
|
||||
out += textp->text();
|
||||
}
|
||||
// TOP will be replaced by top->name()
|
||||
@@ -850,7 +841,7 @@ string AstScopeName::scopePrettyNameFormatter(AstText* scopeTextp) const {
|
||||
}
|
||||
string AstScopeName::scopeNameFormatter(AstText* scopeTextp) const {
|
||||
string out;
|
||||
for (AstText* textp = scopeTextp; textp; textp = VN_CAST(textp->nextp(), Text)) {
|
||||
for (AstText* textp = scopeTextp; textp; textp = VN_AS(textp->nextp(), Text)) {
|
||||
out += textp->text();
|
||||
}
|
||||
if (out.substr(0, 10) == "__DOT__TOP") out.replace(0, 10, "");
|
||||
@@ -864,28 +855,28 @@ string AstScopeName::scopeNameFormatter(AstText* scopeTextp) const {
|
||||
|
||||
bool AstSenTree::hasClocked() const {
|
||||
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
|
||||
for (AstSenItem* senp = sensesp(); senp; senp = VN_CAST(senp->nextp(), SenItem)) {
|
||||
for (AstSenItem* senp = sensesp(); senp; senp = VN_AS(senp->nextp(), SenItem)) {
|
||||
if (senp->isClocked()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool AstSenTree::hasSettle() const {
|
||||
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
|
||||
for (AstSenItem* senp = sensesp(); senp; senp = VN_CAST(senp->nextp(), SenItem)) {
|
||||
for (AstSenItem* senp = sensesp(); senp; senp = VN_AS(senp->nextp(), SenItem)) {
|
||||
if (senp->isSettle()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool AstSenTree::hasInitial() const {
|
||||
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
|
||||
for (AstSenItem* senp = sensesp(); senp; senp = VN_CAST(senp->nextp(), SenItem)) {
|
||||
for (AstSenItem* senp = sensesp(); senp; senp = VN_AS(senp->nextp(), SenItem)) {
|
||||
if (senp->isInitial()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool AstSenTree::hasCombo() const {
|
||||
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
|
||||
for (AstSenItem* senp = sensesp(); senp; senp = VN_CAST(senp->nextp(), SenItem)) {
|
||||
for (AstSenItem* senp = sensesp(); senp; senp = VN_AS(senp->nextp(), SenItem)) {
|
||||
if (senp->isCombo()) return true;
|
||||
}
|
||||
return false;
|
||||
@@ -903,7 +894,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 = VN_CAST(nodep, BasicDType)) bdtypep->generic(false);
|
||||
if (AstBasicDType* const bdtypep = VN_CAST(nodep, BasicDType)) bdtypep->generic(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -911,15 +902,24 @@ 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 = VN_CAST(nodep, BasicDType)) {
|
||||
if (AstBasicDType* const bdtypep = VN_CAST(nodep, BasicDType)) {
|
||||
(void)findInsertSameDType(bdtypep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AstEmptyQueueDType* AstTypeTable::findEmptyQueueDType(FileLine* fl) {
|
||||
if (VL_UNLIKELY(!m_emptyQueuep)) {
|
||||
AstEmptyQueueDType* const newp = new AstEmptyQueueDType{fl};
|
||||
addTypesp(newp);
|
||||
m_emptyQueuep = newp;
|
||||
}
|
||||
return m_emptyQueuep;
|
||||
}
|
||||
|
||||
AstVoidDType* AstTypeTable::findVoidDType(FileLine* fl) {
|
||||
if (VL_UNLIKELY(!m_voidp)) {
|
||||
AstVoidDType* newp = new AstVoidDType(fl);
|
||||
AstVoidDType* const newp = new AstVoidDType{fl};
|
||||
addTypesp(newp);
|
||||
m_voidp = newp;
|
||||
}
|
||||
@@ -928,7 +928,7 @@ AstVoidDType* AstTypeTable::findVoidDType(FileLine* fl) {
|
||||
|
||||
AstQueueDType* AstTypeTable::findQueueIndexDType(FileLine* fl) {
|
||||
if (VL_UNLIKELY(!m_queueIndexp)) {
|
||||
AstQueueDType* newp = new AstQueueDType(fl, AstNode::findUInt32DType(), nullptr);
|
||||
AstQueueDType* const newp = new AstQueueDType(fl, AstNode::findUInt32DType(), nullptr);
|
||||
addTypesp(newp);
|
||||
m_queueIndexp = newp;
|
||||
}
|
||||
@@ -938,11 +938,11 @@ AstQueueDType* AstTypeTable::findQueueIndexDType(FileLine* fl) {
|
||||
AstBasicDType* AstTypeTable::findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd) {
|
||||
if (m_basicps[kwd]) return m_basicps[kwd];
|
||||
//
|
||||
AstBasicDType* new1p = new AstBasicDType(fl, kwd);
|
||||
AstBasicDType* const new1p = new AstBasicDType(fl, kwd);
|
||||
// Because the detailed map doesn't update this map,
|
||||
// check the detailed map for this same node
|
||||
// Also adds this new node to the detailed map
|
||||
AstBasicDType* newp = findInsertSameDType(new1p);
|
||||
AstBasicDType* const newp = findInsertSameDType(new1p);
|
||||
if (newp != new1p) {
|
||||
VL_DO_DANGLING(new1p->deleteTree(), new1p);
|
||||
} else {
|
||||
@@ -955,8 +955,8 @@ AstBasicDType* AstTypeTable::findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd)
|
||||
|
||||
AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd, int width,
|
||||
int widthMin, VSigning numeric) {
|
||||
AstBasicDType* new1p = new AstBasicDType(fl, kwd, numeric, width, widthMin);
|
||||
AstBasicDType* newp = findInsertSameDType(new1p);
|
||||
AstBasicDType* const new1p = new AstBasicDType(fl, kwd, numeric, width, widthMin);
|
||||
AstBasicDType* const newp = findInsertSameDType(new1p);
|
||||
if (newp != new1p) {
|
||||
VL_DO_DANGLING(new1p->deleteTree(), new1p);
|
||||
} else {
|
||||
@@ -968,8 +968,8 @@ AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kw
|
||||
AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd,
|
||||
const VNumRange& range, int widthMin,
|
||||
VSigning numeric) {
|
||||
AstBasicDType* new1p = new AstBasicDType(fl, kwd, numeric, range, widthMin);
|
||||
AstBasicDType* newp = findInsertSameDType(new1p);
|
||||
AstBasicDType* const new1p = new AstBasicDType(fl, kwd, numeric, range, widthMin);
|
||||
AstBasicDType* const newp = findInsertSameDType(new1p);
|
||||
if (newp != new1p) {
|
||||
VL_DO_DANGLING(new1p->deleteTree(), new1p);
|
||||
} else {
|
||||
@@ -979,8 +979,8 @@ AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kw
|
||||
}
|
||||
|
||||
AstBasicDType* AstTypeTable::findInsertSameDType(AstBasicDType* nodep) {
|
||||
VBasicTypeKey key(nodep->width(), nodep->widthMin(), nodep->numeric(), nodep->keyword(),
|
||||
nodep->nrange());
|
||||
const VBasicTypeKey key(nodep->width(), nodep->widthMin(), nodep->numeric(), nodep->keyword(),
|
||||
nodep->nrange());
|
||||
DetailedMap& mapr = m_detailedMap;
|
||||
const auto it = mapr.find(key);
|
||||
if (it != mapr.end()) return it->second;
|
||||
@@ -1017,10 +1017,8 @@ static bool sameInit(const AstInitArray* ap, const AstInitArray* bp) {
|
||||
// - the default/inititem children might be in different order yet still yield the same table
|
||||
// See note in AstInitArray::same about the same. This function instead compares by initializer
|
||||
// value, rather than by tree structure.
|
||||
const AstUnpackArrayDType* const aDTypep = VN_CAST(ap->dtypep(), UnpackArrayDType);
|
||||
const AstUnpackArrayDType* const bDTypep = VN_CAST(bp->dtypep(), UnpackArrayDType);
|
||||
UASSERT_STATIC(aDTypep, "Bad type in array initializer");
|
||||
UASSERT_STATIC(bDTypep, "Bad type in array initializer");
|
||||
const AstUnpackArrayDType* const aDTypep = VN_AS(ap->dtypep(), UnpackArrayDType);
|
||||
const AstUnpackArrayDType* const bDTypep = VN_AS(bp->dtypep(), UnpackArrayDType);
|
||||
if (!aDTypep->subDTypep()->sameTree(bDTypep->subDTypep())) { // Element types differ
|
||||
return false;
|
||||
}
|
||||
@@ -1039,33 +1037,33 @@ static bool sameInit(const AstInitArray* ap, const AstInitArray* bp) {
|
||||
}
|
||||
|
||||
AstVarScope* AstConstPool::findTable(AstInitArray* initp) {
|
||||
AstNode* const defaultp = initp->defaultp();
|
||||
const AstNode* const defaultp = initp->defaultp();
|
||||
// Verify initializer is well formed
|
||||
UASSERT_OBJ(VN_IS(initp->dtypep(), UnpackArrayDType), initp,
|
||||
"Const pool table must have AstUnpackArrayDType dtype");
|
||||
UASSERT_OBJ(!defaultp || VN_IS(defaultp, Const), initp,
|
||||
"Const pool table default must be Const");
|
||||
for (AstNode* nodep = initp->initsp(); nodep; nodep = nodep->nextp()) {
|
||||
AstNode* const valuep = VN_CAST(nodep, InitItem)->valuep();
|
||||
const AstNode* const valuep = VN_AS(nodep, InitItem)->valuep();
|
||||
UASSERT_OBJ(VN_IS(valuep, Const), valuep, "Const pool table entry must be Const");
|
||||
}
|
||||
// Try to find an existing table with the same content
|
||||
const uint32_t hash = V3Hasher::uncachedHash(initp).value();
|
||||
const auto& er = m_tables.equal_range(hash);
|
||||
const V3Hash hash = V3Hasher::uncachedHash(initp);
|
||||
const auto& er = m_tables.equal_range(hash.value());
|
||||
for (auto it = er.first; it != er.second; ++it) {
|
||||
AstVarScope* const varScopep = it->second;
|
||||
const AstInitArray* const init2p = VN_CAST(varScopep->varp()->valuep(), InitArray);
|
||||
const AstInitArray* const init2p = VN_AS(varScopep->varp()->valuep(), InitArray);
|
||||
if (sameInit(initp, init2p)) {
|
||||
return varScopep; // Found identical table
|
||||
}
|
||||
}
|
||||
// No such table yet, create it.
|
||||
string name = "TABLE_";
|
||||
name += cvtToHex(hash);
|
||||
name += hash.toString();
|
||||
name += "_";
|
||||
name += cvtToStr(std::distance(er.first, er.second));
|
||||
AstVarScope* const varScopep = createNewEntry(name, initp);
|
||||
m_tables.emplace(hash, varScopep);
|
||||
m_tables.emplace(hash.value(), varScopep);
|
||||
return varScopep;
|
||||
}
|
||||
|
||||
@@ -1079,11 +1077,11 @@ static bool sameInit(const AstConst* ap, const AstConst* bp) {
|
||||
|
||||
AstVarScope* AstConstPool::findConst(AstConst* initp, bool mergeDType) {
|
||||
// Try to find an existing constant with the same value
|
||||
const uint32_t hash = initp->num().toHash().value();
|
||||
const auto& er = m_consts.equal_range(hash);
|
||||
const V3Hash hash = initp->num().toHash();
|
||||
const auto& er = m_consts.equal_range(hash.value());
|
||||
for (auto it = er.first; it != er.second; ++it) {
|
||||
AstVarScope* const varScopep = it->second;
|
||||
const AstConst* const init2p = VN_CAST(varScopep->varp()->valuep(), Const);
|
||||
const AstConst* const init2p = VN_AS(varScopep->varp()->valuep(), Const);
|
||||
if (sameInit(initp, init2p)
|
||||
&& (mergeDType || varScopep->dtypep()->sameTree(initp->dtypep()))) {
|
||||
return varScopep; // Found identical constant
|
||||
@@ -1091,11 +1089,11 @@ AstVarScope* AstConstPool::findConst(AstConst* initp, bool mergeDType) {
|
||||
}
|
||||
// No such constant yet, create it.
|
||||
string name = "CONST_";
|
||||
name += cvtToHex(hash);
|
||||
name += hash.toString();
|
||||
name += "_";
|
||||
name += cvtToStr(std::distance(er.first, er.second));
|
||||
AstVarScope* const varScopep = createNewEntry(name, initp);
|
||||
m_consts.emplace(hash, varScopep);
|
||||
m_consts.emplace(hash.value(), varScopep);
|
||||
return varScopep;
|
||||
}
|
||||
|
||||
@@ -1194,7 +1192,7 @@ void AstNode::dump(std::ostream& str) const {
|
||||
} else {
|
||||
str << " @dt=" << nodeAddr(dtypep()) << "@";
|
||||
}
|
||||
if (AstNodeDType* dtp = dtypep()) dtp->dumpSmall(str);
|
||||
if (AstNodeDType* const dtp = dtypep()) dtp->dumpSmall(str);
|
||||
} else { // V3Broken will throw an error
|
||||
if (dtypep()) str << " %Error-dtype-exp=null,got=" << nodeAddr(dtypep());
|
||||
}
|
||||
@@ -1260,9 +1258,8 @@ const char* AstClassPackage::broken() const {
|
||||
return nullptr;
|
||||
}
|
||||
void AstClass::insertCache(AstNode* nodep) {
|
||||
const bool doit
|
||||
= (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef)
|
||||
|| (VN_IS(nodep, NodeFTask) && !VN_CAST(nodep, NodeFTask)->isExternProto()));
|
||||
const bool doit = (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef)
|
||||
|| (VN_IS(nodep, NodeFTask) && !VN_AS(nodep, NodeFTask)->isExternProto()));
|
||||
if (doit) {
|
||||
if (m_members.find(nodep->name()) != m_members.end()) {
|
||||
nodep->v3error("Duplicate declaration of member name: " << nodep->prettyNameQ());
|
||||
@@ -1288,9 +1285,9 @@ void AstClass::dump(std::ostream& str) const {
|
||||
if (isVirtual()) str << " [VIRT]";
|
||||
}
|
||||
AstClass* AstClassExtends::classp() const {
|
||||
AstClassRefDType* refp = VN_CAST(dtypep(), ClassRefDType);
|
||||
const AstClassRefDType* refp = VN_CAST(dtypep(), ClassRefDType);
|
||||
if (VL_UNLIKELY(!refp)) { // LinkDot uses this for 'super.'
|
||||
refp = VN_CAST(childDTypep(), ClassRefDType);
|
||||
refp = VN_AS(childDTypep(), ClassRefDType);
|
||||
}
|
||||
UASSERT_OBJ(refp, this, "class extends non-ref");
|
||||
return refp->classp();
|
||||
@@ -1314,7 +1311,7 @@ void AstNodeCoverOrAssert::dump(std::ostream& str) const {
|
||||
}
|
||||
void AstDisplay::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
// str<<" "<<displayType().ascii();
|
||||
// str << " " << displayType().ascii();
|
||||
}
|
||||
void AstEnumItemRef::dump(std::ostream& str) const {
|
||||
this->AstNodeMath::dump(str);
|
||||
@@ -1477,7 +1474,7 @@ void AstNodeUOrStructDType::dump(std::ostream& str) const {
|
||||
void AstNodeDType::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (generic()) str << " [GENERIC]";
|
||||
if (AstNodeDType* dtp = virtRefDTypep()) {
|
||||
if (AstNodeDType* const dtp = virtRefDTypep()) {
|
||||
str << " refdt=" << nodeAddr(dtp);
|
||||
dtp->dumpSmall(str);
|
||||
}
|
||||
@@ -1491,7 +1488,7 @@ void AstNodeDType::dumpSmall(std::ostream& str) const {
|
||||
}
|
||||
void AstNodeArrayDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
if (auto* adtypep = VN_CAST_CONST(this, UnpackArrayDType)) {
|
||||
if (auto* const adtypep = VN_CAST(this, UnpackArrayDType)) {
|
||||
// uc = packed compound object, u = unpacked POD
|
||||
str << (adtypep->isCompound() ? "uc" : "u");
|
||||
} else {
|
||||
@@ -1527,7 +1524,7 @@ std::vector<AstUnpackArrayDType*> AstUnpackArrayDType::unpackDimensions() {
|
||||
std::vector<AstUnpackArrayDType*> dims;
|
||||
for (AstUnpackArrayDType* unpackp = this; unpackp;) {
|
||||
dims.push_back(unpackp);
|
||||
if (AstNodeDType* subp = unpackp->subDTypep()) {
|
||||
if (AstNodeDType* const subp = unpackp->subDTypep()) {
|
||||
unpackp = VN_CAST(subp, UnpackArrayDType);
|
||||
} else {
|
||||
unpackp = nullptr;
|
||||
@@ -1584,7 +1581,7 @@ void AstMTaskBody::dump(std::ostream& str) const {
|
||||
void AstTypeTable::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
for (int i = 0; i < static_cast<int>(AstBasicDTypeKwd::_ENUM_MAX); ++i) {
|
||||
if (AstBasicDType* subnodep = m_basicps[i]) {
|
||||
if (AstBasicDType* const subnodep = m_basicps[i]) {
|
||||
str << '\n'; // Newline from caller, so newline first
|
||||
str << "\t\t" << std::setw(8) << AstBasicDTypeKwd(i).ascii();
|
||||
str << " -> ";
|
||||
@@ -1594,7 +1591,7 @@ void AstTypeTable::dump(std::ostream& str) const {
|
||||
{
|
||||
const DetailedMap& mapr = m_detailedMap;
|
||||
for (const auto& itr : mapr) {
|
||||
AstBasicDType* dtypep = itr.second;
|
||||
AstBasicDType* const dtypep = itr.second;
|
||||
str << '\n'; // Newline from caller, so newline first
|
||||
str << "\t\tdetailed -> ";
|
||||
dtypep->dump(str);
|
||||
@@ -1627,6 +1624,10 @@ void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "[]";
|
||||
}
|
||||
void AstEmptyQueueDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "emptyq";
|
||||
}
|
||||
void AstVoidDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "void";
|
||||
@@ -1699,6 +1700,11 @@ void AstScope::dump(std::ostream& str) const {
|
||||
str << " [cellp=" << reinterpret_cast<const void*>(aboveCellp()) << "]";
|
||||
str << " [modp=" << reinterpret_cast<const void*>(modp()) << "]";
|
||||
}
|
||||
void AstScopeName::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (dpiExport()) str << " [DPIEX]";
|
||||
if (forFormat()) str << " [FMT]";
|
||||
}
|
||||
void AstSenTree::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (isMulti()) str << " [MULTI]";
|
||||
@@ -1827,6 +1833,7 @@ void AstCFunc::dump(std::ostream& str) const {
|
||||
if (dpiExportImpl()) str << " [DPIEI]";
|
||||
if (dpiImportPrototype()) str << " [DPIIP]";
|
||||
if (dpiImportWrapper()) str << " [DPIIW]";
|
||||
if (dpiContext()) str << " [DPICTX]";
|
||||
if (isConstructor()) str << " [CTOR]";
|
||||
if (isDestructor()) str << " [DTOR]";
|
||||
if (isVirtual()) str << " [VIRT]";
|
||||
|
||||
+350
-280
File diff suppressed because it is too large
Load Diff
@@ -33,7 +33,7 @@ template <class T_Node, class T_Data, int T_UserN> class AstUserAllocatorBase VL
|
||||
private:
|
||||
std::vector<T_Data*> m_allocated;
|
||||
|
||||
inline T_Data* getUserp(T_Node* nodep) const {
|
||||
inline T_Data* getUserp(const T_Node* nodep) const {
|
||||
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'.
|
||||
if (T_UserN == 1) {
|
||||
const VNUser user = nodep->user1u();
|
||||
@@ -100,6 +100,13 @@ public:
|
||||
}
|
||||
return *userp;
|
||||
}
|
||||
|
||||
// Get a reference to the user data
|
||||
T_Data& operator()(const T_Node* nodep) {
|
||||
T_Data* const userp = getUserp(nodep);
|
||||
UASSERT_OBJ(userp, nodep, "Missing User data on const AstNode");
|
||||
return *userp;
|
||||
}
|
||||
};
|
||||
|
||||
// User pointer allocator classes. T_Node is the type of node the allocator should be applied to
|
||||
|
||||
+50
-26
@@ -41,7 +41,7 @@ private:
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstNodeFTask::user1 -> bool, 1=processed
|
||||
AstUser1InUse m_inuser1;
|
||||
const AstUser1InUse m_inuser1;
|
||||
bool m_anyFuncInBegin = false;
|
||||
|
||||
public:
|
||||
@@ -59,9 +59,11 @@ public:
|
||||
class BeginVisitor final : public AstNVisitor {
|
||||
private:
|
||||
// STATE
|
||||
BeginState* m_statep; // Current global state
|
||||
BeginState* const m_statep; // Current global state
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
||||
const AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
||||
AstNode* m_liftedp = nullptr; // Local nodes we are lifting into m_ftaskp
|
||||
string m_displayScope; // Name of %m in $display/AstScopeName
|
||||
string m_namedScope; // Name of begin blocks above us
|
||||
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
|
||||
int m_ifDepth = 0; // Current if depth
|
||||
@@ -74,6 +76,21 @@ private:
|
||||
return a + "__DOT__" + b;
|
||||
}
|
||||
|
||||
void liftNode(AstNode* nodep) {
|
||||
nodep->unlinkFrBack();
|
||||
if (m_ftaskp) {
|
||||
// AstBegin under ftask, just move into the ftask
|
||||
if (!m_liftedp) {
|
||||
m_liftedp = nodep;
|
||||
} else {
|
||||
m_liftedp->addNext(nodep);
|
||||
}
|
||||
} else {
|
||||
// Move to module
|
||||
m_modp->addStmtp(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
@@ -95,19 +112,32 @@ private:
|
||||
// naming; so that any begin's inside the function will rename
|
||||
// inside the function.
|
||||
// Process children
|
||||
VL_RESTORER(m_displayScope);
|
||||
VL_RESTORER(m_namedScope);
|
||||
VL_RESTORER(m_unnamedScope);
|
||||
{
|
||||
m_displayScope = dot(m_displayScope, nodep->name());
|
||||
m_namedScope = "";
|
||||
m_unnamedScope = "";
|
||||
m_ftaskp = nodep;
|
||||
m_liftedp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
if (m_liftedp) {
|
||||
// Place lifted nodes at beginning of stmtsp, so Var nodes appear before referenced
|
||||
if (AstNode* const stmtsp = nodep->stmtsp()) {
|
||||
stmtsp->unlinkFrBackWithNext();
|
||||
m_liftedp->addNext(stmtsp);
|
||||
}
|
||||
nodep->addStmtsp(m_liftedp);
|
||||
m_liftedp = nullptr;
|
||||
}
|
||||
m_ftaskp = nullptr;
|
||||
}
|
||||
}
|
||||
virtual void visit(AstBegin* nodep) override {
|
||||
// Begin blocks were only useful in variable creation, change names and delete
|
||||
UINFO(8, " " << nodep << endl);
|
||||
VL_RESTORER(m_displayScope);
|
||||
VL_RESTORER(m_namedScope);
|
||||
VL_RESTORER(m_unnamedScope);
|
||||
{
|
||||
@@ -119,11 +149,14 @@ private:
|
||||
while ((pos = dottedname.find("__DOT__")) != string::npos) {
|
||||
const string ident = dottedname.substr(0, pos);
|
||||
dottedname = dottedname.substr(pos + strlen("__DOT__"));
|
||||
if (nodep->name() != "") m_namedScope = dot(m_namedScope, ident);
|
||||
if (nodep->name() != "") {
|
||||
m_displayScope = dot(m_displayScope, ident);
|
||||
m_namedScope = dot(m_namedScope, ident);
|
||||
}
|
||||
m_unnamedScope = dot(m_unnamedScope, ident);
|
||||
// Create CellInline for dotted var resolution
|
||||
if (!m_ftaskp) {
|
||||
AstCellInline* inlinep = new AstCellInline(
|
||||
AstCellInline* const inlinep = new AstCellInline(
|
||||
nodep->fileline(), m_unnamedScope, "__BEGIN__", m_modp->timeunit());
|
||||
m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells
|
||||
}
|
||||
@@ -136,7 +169,7 @@ private:
|
||||
|
||||
// Cleanup
|
||||
AstNode* addsp = nullptr;
|
||||
if (AstNode* stmtsp = nodep->stmtsp()) {
|
||||
if (AstNode* const stmtsp = nodep->stmtsp()) {
|
||||
stmtsp->unlinkFrBackWithNext();
|
||||
if (addsp) {
|
||||
addsp = addsp->addNextNull(stmtsp);
|
||||
@@ -157,13 +190,8 @@ private:
|
||||
// Rename it
|
||||
nodep->name(dot(m_unnamedScope, nodep->name()));
|
||||
m_statep->userMarkChanged(nodep);
|
||||
// Move to module
|
||||
nodep->unlinkFrBack();
|
||||
if (m_ftaskp) {
|
||||
m_ftaskp->addStmtsp(nodep); // Begins under funcs just move into the func
|
||||
} else {
|
||||
m_modp->addStmtp(nodep);
|
||||
}
|
||||
// Move it under enclosing tree
|
||||
liftNode(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstTypedef* nodep) override {
|
||||
@@ -171,14 +199,8 @@ private:
|
||||
// Rename it
|
||||
nodep->name(dot(m_unnamedScope, nodep->name()));
|
||||
m_statep->userMarkChanged(nodep);
|
||||
// Move to module
|
||||
nodep->unlinkFrBack();
|
||||
// Begins under funcs just move into the func
|
||||
if (m_ftaskp) {
|
||||
m_ftaskp->addStmtsp(nodep);
|
||||
} else {
|
||||
m_modp->addStmtp(nodep);
|
||||
}
|
||||
// Move it under enclosing tree
|
||||
liftNode(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCell* nodep) override {
|
||||
@@ -205,11 +227,13 @@ private:
|
||||
// If there's a %m in the display text, we add a special node that will contain the name()
|
||||
// Similar code in V3Inline
|
||||
if (nodep->user1SetOnce()) return; // Don't double-add text's
|
||||
if (m_namedScope != "") {
|
||||
// DPI svGetScope doesn't include function name, but %m does
|
||||
const string scname = nodep->forFormat() ? m_displayScope : m_namedScope;
|
||||
if (!scname.empty()) {
|
||||
// To keep correct visual order, must add before other Text's
|
||||
AstNode* afterp = nodep->scopeAttrp();
|
||||
AstNode* const 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__") + scname});
|
||||
if (afterp) nodep->scopeAttrp(afterp);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
@@ -293,8 +317,8 @@ void V3Begin::debeginAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{
|
||||
BeginState state;
|
||||
{ BeginVisitor bvisitor(nodep, &state); }
|
||||
if (state.anyFuncInBegin()) { BeginRelinkVisitor brvisitor(nodep, &state); }
|
||||
{ BeginVisitor{nodep, &state}; }
|
||||
if (state.anyFuncInBegin()) { BeginRelinkVisitor{nodep, &state}; }
|
||||
} // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("begin", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+4
-4
@@ -40,11 +40,11 @@ private:
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstFTask::user1() -> int. Number of references
|
||||
AstUser1InUse m_inuser1;
|
||||
const AstUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
int m_likely; // Excuses for branch likely taken
|
||||
int m_unlikely; // Excuses for branch likely not taken
|
||||
int m_likely = false; // Excuses for branch likely taken
|
||||
int m_unlikely = false; // Excuses for branch likely not taken
|
||||
std::vector<AstCFunc*> m_cfuncsp; // List of all tasks
|
||||
|
||||
// METHODS
|
||||
@@ -123,5 +123,5 @@ public:
|
||||
|
||||
void V3Branch::branchAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
BranchVisitor visitor(nodep);
|
||||
{ BranchVisitor{nodep}; }
|
||||
}
|
||||
|
||||
+210
-186
@@ -18,6 +18,7 @@
|
||||
// Entire netlist
|
||||
// Mark all nodes
|
||||
// Check all links point to marked nodes
|
||||
// Check local variables in CFuncs appear before they are referenced
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
@@ -31,201 +32,129 @@
|
||||
// This visitor does not edit nodes, and is called at error-exit, so should use constant iterators
|
||||
#include "V3AstConstOnly.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
//######################################################################
|
||||
// Generation counter for AstNode::m_brokenState
|
||||
|
||||
class BrokenTable VL_NOT_FINAL : public AstNVisitor {
|
||||
// Table of brokenExists node pointers
|
||||
static class BrokenCntGlobal {
|
||||
// This is a 7 bit generation counter, stored in the bottom 7 bits of AstNode::m_brokenState,
|
||||
// used to mark a node as being present under the root AstNetlist in the current traversal. A
|
||||
// value 0 is invalid, as the AstNode constructor uses that to initialize m_brokenState
|
||||
static constexpr uint8_t MIN_VALUE = 1;
|
||||
static constexpr uint8_t MAX_VALUE = 127;
|
||||
|
||||
uint8_t m_count = MIN_VALUE;
|
||||
|
||||
public:
|
||||
uint8_t get() {
|
||||
UASSERT(MIN_VALUE <= m_count && m_count <= MAX_VALUE, "Invalid generation number");
|
||||
return m_count;
|
||||
}
|
||||
|
||||
void inc() {
|
||||
++m_count;
|
||||
if (m_count > MAX_VALUE) m_count = MIN_VALUE;
|
||||
}
|
||||
} s_brokenCntGlobal;
|
||||
|
||||
//######################################################################
|
||||
// Table of allocated AstNode pointers
|
||||
|
||||
static class AllocTable final {
|
||||
private:
|
||||
// MEMBERS
|
||||
// For each node, we keep if it exists or not.
|
||||
using NodeMap = std::unordered_map<const AstNode*, int>; // Performance matters (when --debug)
|
||||
static NodeMap s_nodes; // Set of all nodes that exist
|
||||
// BITMASK
|
||||
enum { FLAG_ALLOCATED = 0x01 }; // new() and not delete()ed
|
||||
enum { FLAG_IN_TREE = 0x02 }; // Is in netlist tree
|
||||
enum { FLAG_LINKABLE = 0x04 }; // Is in netlist tree, can be linked to
|
||||
enum { FLAG_LEAKED = 0x08 }; // Known to have been leaked
|
||||
enum { FLAG_UNDER_NOW = 0x10 }; // Is in tree as parent of current node
|
||||
std::unordered_set<const AstNode*> m_allocated; // Set of all nodes allocated but not freed
|
||||
|
||||
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: " << cvtToHex(nodep) << endl;
|
||||
const auto iter = s_nodes.find(nodep);
|
||||
UASSERT_OBJ(!(iter == s_nodes.end() || !(iter->second & FLAG_ALLOCATED)),
|
||||
reinterpret_cast<const AstNode*>(nodep),
|
||||
"Deleting AstNode object that was never tracked or already deleted");
|
||||
if (iter != s_nodes.end()) s_nodes.erase(iter);
|
||||
}
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 4
|
||||
// GCC 4.4.* compiler warning bug, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39390
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#endif
|
||||
static void addNewed(const AstNode* nodep) {
|
||||
void addNewed(const AstNode* nodep) {
|
||||
// Called by operator new on any node - only if VL_LEAK_CHECKS
|
||||
if (debug() >= 9) cout << "-nodeNew: " << cvtToHex(nodep) << endl;
|
||||
const auto iter = s_nodes.find(nodep);
|
||||
UASSERT_OBJ(!(iter != s_nodes.end() && (iter->second & FLAG_ALLOCATED)), nodep,
|
||||
"Newing AstNode object that is already allocated");
|
||||
if (iter == s_nodes.end()) {
|
||||
const int flags = FLAG_ALLOCATED; // This int needed to appease GCC 4.1.2
|
||||
s_nodes.emplace(nodep, flags);
|
||||
// LCOV_EXCL_START
|
||||
if (VL_UNCOVERABLE(!m_allocated.emplace(nodep).second)) {
|
||||
nodep->v3fatalSrc("Newing AstNode object that is already allocated");
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
static void setUnder(const AstNode* nodep, bool flag) {
|
||||
// Called by BrokenCheckVisitor when each node entered/exited
|
||||
if (!okIfLinkedTo(nodep)) return;
|
||||
const auto iter = s_nodes.find(nodep);
|
||||
if (iter != s_nodes.end()) {
|
||||
iter->second &= ~FLAG_UNDER_NOW;
|
||||
if (flag) iter->second |= FLAG_UNDER_NOW;
|
||||
void deleted(const AstNode* nodep) {
|
||||
// Called by operator delete on any node - only if VL_LEAK_CHECKS
|
||||
// LCOV_EXCL_START
|
||||
if (VL_UNCOVERABLE(m_allocated.erase(nodep) == 0)) {
|
||||
nodep->v3fatalSrc("Deleting AstNode object that was not allocated or already freed");
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
static void addInTree(AstNode* nodep, bool linkable) {
|
||||
#ifndef VL_LEAK_CHECKS
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!linkable) return; // save some time, else the map will get huge!
|
||||
#endif
|
||||
const auto iter = s_nodes.find(nodep);
|
||||
if (VL_UNCOVERABLE(iter == s_nodes.end())) {
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
nodep->v3fatalSrc("AstNode is in tree, but not allocated");
|
||||
#endif
|
||||
} else {
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
UASSERT_OBJ(iter->second & FLAG_ALLOCATED, nodep,
|
||||
"AstNode is in tree, but not allocated");
|
||||
#endif
|
||||
UASSERT_OBJ(!(iter->second & FLAG_IN_TREE), nodep,
|
||||
"AstNode is already in tree at another location");
|
||||
}
|
||||
const int or_flags = FLAG_IN_TREE | (linkable ? FLAG_LINKABLE : 0);
|
||||
if (iter == s_nodes.end()) {
|
||||
s_nodes.emplace(nodep, or_flags);
|
||||
} else {
|
||||
iter->second |= or_flags;
|
||||
}
|
||||
}
|
||||
static bool isAllocated(const AstNode* nodep) {
|
||||
// Some generic node has a pointer to this node. Is it allocated?
|
||||
// Use this when might not be in tree; otherwise use okIfLinkedTo().
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
const auto iter = s_nodes.find(nodep);
|
||||
if (iter == s_nodes.end()) return false;
|
||||
if (!(iter->second & FLAG_ALLOCATED)) return false;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
static bool okIfLinkedTo(const AstNode* nodep) {
|
||||
// Some node in tree has a pointer to this node. Is it kosher?
|
||||
const auto iter = s_nodes.find(nodep);
|
||||
if (iter == s_nodes.end()) return false;
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
if (!(iter->second & FLAG_ALLOCATED)) return false;
|
||||
#endif
|
||||
if (!(iter->second & FLAG_IN_TREE)) return false;
|
||||
if (!(iter->second & FLAG_LINKABLE)) return false;
|
||||
return true;
|
||||
}
|
||||
static bool okIfAbove(const AstNode* nodep) {
|
||||
// Must be linked to and below current node
|
||||
if (!okIfLinkedTo(nodep)) return false;
|
||||
const auto iter = s_nodes.find(nodep);
|
||||
if (iter == s_nodes.end()) return false;
|
||||
if ((iter->second & FLAG_UNDER_NOW)) return false;
|
||||
return true;
|
||||
}
|
||||
static bool okIfBelow(const AstNode* nodep) {
|
||||
// Must be linked to and below current node
|
||||
if (!okIfLinkedTo(nodep)) return false;
|
||||
const auto iter = s_nodes.find(nodep);
|
||||
if (iter == s_nodes.end()) return false;
|
||||
if (!(iter->second & FLAG_UNDER_NOW)) return false;
|
||||
return true;
|
||||
}
|
||||
static void prepForTree() {
|
||||
#ifndef VL_LEAK_CHECKS
|
||||
s_nodes.clear();
|
||||
#else
|
||||
for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
|
||||
it->second &= ~FLAG_IN_TREE;
|
||||
it->second &= ~FLAG_LINKABLE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
static void doneWithTree() {
|
||||
for (int backs = 0; backs < 2;
|
||||
backs++) { // Those with backp() are probably under one leaking without
|
||||
for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
|
||||
// LCOV_EXCL_START
|
||||
if (VL_UNCOVERABLE((it->second & FLAG_ALLOCATED) && !(it->second & FLAG_IN_TREE)
|
||||
&& !(it->second & FLAG_LEAKED)
|
||||
&& (it->first->backp() ? backs == 1 : backs == 0))) {
|
||||
bool isAllocated(const AstNode* nodep) const { return m_allocated.count(nodep) != 0; }
|
||||
void checkForLeaks() {
|
||||
if (!v3Global.opt.debugCheck()) return;
|
||||
|
||||
const uint8_t brokenCntCurrent = s_brokenCntGlobal.get();
|
||||
|
||||
// Those with backp() are probably under a parent that was leaked and has no backp()
|
||||
for (const bool withBack : {false, true}) {
|
||||
for (const AstNode* const nodep : m_allocated) {
|
||||
// LCOV_EXCL_START
|
||||
// Most likely not leaked, so check that first
|
||||
if (VL_UNCOVERABLE(nodep->brokenState() != brokenCntCurrent)) {
|
||||
const bool hasBack = nodep->backp() != nullptr;
|
||||
if (hasBack != withBack) continue;
|
||||
// Use only AstNode::dump instead of the virtual one, as there
|
||||
// may be varp() and other cross links that are bad.
|
||||
if (v3Global.opt.debugCheck()) {
|
||||
// When get this message, find what forgot to delete the
|
||||
// node by running GDB, where for node "<e###>" use:
|
||||
// watch AstNode::s_editCntGbl==####
|
||||
// run
|
||||
// bt
|
||||
std::cerr << "%Error: LeakedNode"
|
||||
<< (it->first->backp() ? "Back: " : ": ");
|
||||
AstNode* rawp
|
||||
= const_cast<AstNode*>(static_cast<const AstNode*>(it->first));
|
||||
rawp->AstNode::dump(std::cerr);
|
||||
std::cerr << endl;
|
||||
V3Error::incErrors();
|
||||
}
|
||||
it->second |= FLAG_LEAKED;
|
||||
// When get this message, find what forgot to delete the
|
||||
// node by running GDB, where for node "<e###>" use:
|
||||
// watch AstNode::s_editCntGbl==####
|
||||
// run
|
||||
// bt
|
||||
std::cerr << "%Error: LeakedNode" << (withBack ? "with back pointer: " : ": ");
|
||||
nodep->AstNode::dump(std::cerr);
|
||||
std::cerr << endl;
|
||||
V3Error::incErrors();
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
}
|
||||
} s_allocTable;
|
||||
|
||||
// CONSTRUCTORS
|
||||
BrokenTable() = default;
|
||||
virtual ~BrokenTable() override = default;
|
||||
};
|
||||
|
||||
BrokenTable::NodeMap BrokenTable::s_nodes;
|
||||
|
||||
bool AstNode::brokeExists() const {
|
||||
// Called by node->broken() routines to do table lookup
|
||||
return BrokenTable::okIfLinkedTo(this);
|
||||
}
|
||||
bool AstNode::brokeExistsAbove() const {
|
||||
// Called by node->broken() routines to do table lookup
|
||||
return BrokenTable::okIfBelow(this);
|
||||
}
|
||||
bool AstNode::brokeExistsBelow() const {
|
||||
// Called by node->broken() routines to do table lookup
|
||||
return BrokenTable::okIfAbove(this);
|
||||
}
|
||||
void V3Broken::addNewed(const AstNode* nodep) { s_allocTable.addNewed(nodep); }
|
||||
void V3Broken::deleted(const AstNode* nodep) { s_allocTable.deleted(nodep); }
|
||||
|
||||
//######################################################################
|
||||
// Table of AstNode pointers that can be linked to via member pointers
|
||||
|
||||
static class LinkableTable final {
|
||||
private:
|
||||
// MEMBERS
|
||||
std::unordered_set<const AstNode*> m_linkable; // Set of all nodes allocated but not freed
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
void clear() { m_linkable.clear(); }
|
||||
inline void addLinkable(const AstNode* nodep) { m_linkable.emplace(nodep); }
|
||||
inline bool isLinkable(const AstNode* nodep) const { return m_linkable.count(nodep) != 0; }
|
||||
} s_linkableTable;
|
||||
|
||||
bool V3Broken::isLinkable(const AstNode* nodep) { return s_linkableTable.isLinkable(nodep); }
|
||||
|
||||
//######################################################################
|
||||
// Mark every node in the tree
|
||||
|
||||
class BrokenMarkVisitor final : public AstNVisitor {
|
||||
// Mark every node in the tree
|
||||
private:
|
||||
// NODE STATE
|
||||
// Nothing! // This may be called deep inside other routines
|
||||
// // so userp and friends may not be used
|
||||
// METHODS
|
||||
void processAndIterate(AstNode* nodep) {
|
||||
BrokenTable::addInTree(nodep, nodep->maybePointedTo());
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
const uint8_t m_brokenCntCurrent = s_brokenCntGlobal.get();
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
// Process not just iterate
|
||||
processAndIterate(nodep);
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
UASSERT_OBJ(s_allocTable.isAllocated(nodep), nodep,
|
||||
"AstNode is in tree, but not allocated");
|
||||
#endif
|
||||
UASSERT_OBJ(nodep->brokenState() != m_brokenCntCurrent, nodep,
|
||||
"AstNode is already in tree at another location");
|
||||
if (nodep->maybePointedTo()) s_linkableTable.addLinkable(nodep);
|
||||
nodep->brokenState(m_brokenCntCurrent);
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -235,26 +164,40 @@ public:
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Broken state, as a visitor of each AstNode
|
||||
// Check every node in tree
|
||||
|
||||
class BrokenCheckVisitor final : public AstNVisitor {
|
||||
bool m_inScope = false; // Under AstScope
|
||||
|
||||
// Constants for marking we are under/not under a node
|
||||
const uint8_t m_brokenCntCurrentNotUnder = s_brokenCntGlobal.get(); // Top bit is clear
|
||||
const uint8_t m_brokenCntCurrentUnder = m_brokenCntCurrentNotUnder | 0x80; // Top bit is set
|
||||
|
||||
// Current CFunc, if any
|
||||
const AstCFunc* m_cfuncp = nullptr;
|
||||
// All local variables declared in current function
|
||||
std::unordered_set<const AstVar*> m_localVars;
|
||||
// Variable references in current function that do not reference an in-scope local
|
||||
std::unordered_map<const AstVar*, const AstNodeVarRef*> m_suspectRefs;
|
||||
// Local variables declared in the scope of the current statement
|
||||
std::vector<std::unordered_set<const AstVar*>> m_localsStack;
|
||||
|
||||
private:
|
||||
static void checkWidthMin(const AstNode* nodep) {
|
||||
UASSERT_OBJ(nodep->width() == nodep->widthMin()
|
||||
|| v3Global.widthMinUsage() != VWidthMinUsage::MATCHES_WIDTH,
|
||||
nodep, "Width != WidthMin");
|
||||
}
|
||||
void processAndIterate(AstNode* nodep) {
|
||||
BrokenTable::setUnder(nodep, true);
|
||||
const char* whyp = nodep->broken();
|
||||
|
||||
void processEnter(AstNode* nodep) {
|
||||
nodep->brokenState(m_brokenCntCurrentUnder);
|
||||
const char* const whyp = nodep->broken();
|
||||
UASSERT_OBJ(!whyp, nodep,
|
||||
"Broken link in node (or something without maybePointedTo): " << whyp);
|
||||
if (nodep->dtypep()) {
|
||||
UASSERT_OBJ(nodep->dtypep()->brokeExists(), nodep,
|
||||
"Broken link in node->dtypep() to " << cvtToHex(nodep->dtypep()));
|
||||
UASSERT_OBJ(VN_IS(nodep->dtypep(), NodeDType), nodep,
|
||||
UASSERT_OBJ(nodep->dtypep(), nodep,
|
||||
"Non-dtype link in node->dtypep() to " << cvtToHex(nodep->dtypep()));
|
||||
}
|
||||
if (v3Global.assertDTypesResolved()) {
|
||||
@@ -267,17 +210,40 @@ private:
|
||||
}
|
||||
UASSERT_OBJ(!nodep->getChildDTypep(), nodep,
|
||||
"childDTypep() non-null on node after should have removed");
|
||||
if (const AstNodeDType* dnodep = VN_CAST(nodep, NodeDType)) checkWidthMin(dnodep);
|
||||
if (const AstNodeDType* const dnodep = VN_CAST(nodep, NodeDType))
|
||||
checkWidthMin(dnodep);
|
||||
}
|
||||
checkWidthMin(nodep);
|
||||
}
|
||||
void processExit(AstNode* nodep) { nodep->brokenState(m_brokenCntCurrentNotUnder); }
|
||||
void processAndIterate(AstNode* nodep) {
|
||||
processEnter(nodep);
|
||||
iterateChildrenConst(nodep);
|
||||
BrokenTable::setUnder(nodep, false);
|
||||
processExit(nodep);
|
||||
}
|
||||
void processAndIterateList(AstNode* nodep) {
|
||||
while (nodep) {
|
||||
processAndIterate(nodep);
|
||||
nodep = nodep->nextp();
|
||||
};
|
||||
}
|
||||
void pushLocalScope() {
|
||||
if (m_cfuncp) m_localsStack.emplace_back();
|
||||
}
|
||||
void popLocalScope() {
|
||||
if (m_cfuncp) m_localsStack.pop_back();
|
||||
}
|
||||
bool isInScopeLocal(const AstVar* varp) const {
|
||||
for (const auto& set : m_localsStack) {
|
||||
if (set.count(varp)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep) override {
|
||||
processAndIterate(nodep);
|
||||
UASSERT_OBJ(!(v3Global.assertDTypesResolved() && nodep->brokeLhsMustBeLvalue()
|
||||
&& VN_IS(nodep->lhsp(), NodeVarRef)
|
||||
&& !VN_CAST(nodep->lhsp(), NodeVarRef)->access().isWriteOrRW()),
|
||||
&& !VN_AS(nodep->lhsp(), NodeVarRef)->access().isWriteOrRW()),
|
||||
nodep, "Assignment LHS is not an lvalue");
|
||||
}
|
||||
virtual void visit(AstScope* nodep) override {
|
||||
@@ -295,6 +261,65 @@ private:
|
||||
UASSERT_OBJ(
|
||||
!(v3Global.assertScoped() && m_inScope && nodep->varp() && !nodep->varScopep()), nodep,
|
||||
"VarRef missing VarScope pointer");
|
||||
if (m_cfuncp) {
|
||||
// Check if variable is an in-scope local, otherwise mark as suspect
|
||||
if (const AstVar* const varp = nodep->varp()) {
|
||||
if (!isInScopeLocal(varp)) {
|
||||
// This only stores the first ref for each Var, which is what we want
|
||||
m_suspectRefs.emplace(varp, nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
UASSERT_OBJ(!m_cfuncp, nodep, "Nested AstCFunc");
|
||||
m_cfuncp = nodep;
|
||||
m_localVars.clear();
|
||||
m_suspectRefs.clear();
|
||||
m_localsStack.clear();
|
||||
pushLocalScope();
|
||||
|
||||
processAndIterate(nodep);
|
||||
|
||||
// Check suspect references are all to non-locals
|
||||
for (const auto& pair : m_suspectRefs) {
|
||||
UASSERT_OBJ(m_localVars.count(pair.first) == 0, pair.second,
|
||||
"Local variable not in scope where referenced: " << pair.first);
|
||||
}
|
||||
|
||||
m_cfuncp = nullptr;
|
||||
}
|
||||
virtual void visit(AstNodeIf* nodep) override {
|
||||
// Each branch is a separate local variable scope
|
||||
pushLocalScope();
|
||||
processEnter(nodep);
|
||||
processAndIterate(nodep->condp());
|
||||
if (AstNode* const ifsp = nodep->ifsp()) {
|
||||
pushLocalScope();
|
||||
processAndIterateList(ifsp);
|
||||
popLocalScope();
|
||||
}
|
||||
if (AstNode* const elsesp = nodep->elsesp()) {
|
||||
pushLocalScope();
|
||||
processAndIterateList(elsesp);
|
||||
popLocalScope();
|
||||
}
|
||||
processExit(nodep);
|
||||
popLocalScope();
|
||||
}
|
||||
virtual void visit(AstNodeStmt* nodep) override {
|
||||
// For local variable checking act as if any statement introduces a new scope.
|
||||
// This is aggressive but conservatively correct.
|
||||
pushLocalScope();
|
||||
processAndIterate(nodep);
|
||||
popLocalScope();
|
||||
}
|
||||
virtual void visit(AstVar* nodep) override {
|
||||
processAndIterate(nodep);
|
||||
if (m_cfuncp) {
|
||||
m_localVars.insert(nodep);
|
||||
m_localsStack.back().insert(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
// Process not just iterate
|
||||
@@ -308,7 +333,7 @@ public:
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Broken class functions
|
||||
// Broken check entry point
|
||||
|
||||
void V3Broken::brokenAll(AstNetlist* nodep) {
|
||||
// UINFO(9, __FUNCTION__ << ": " << endl);
|
||||
@@ -318,24 +343,23 @@ void V3Broken::brokenAll(AstNetlist* nodep) {
|
||||
UINFO(1, "Broken called under broken, skipping recursion.\n"); // LCOV_EXCL_LINE
|
||||
} else {
|
||||
inBroken = true;
|
||||
BrokenTable::prepForTree();
|
||||
BrokenMarkVisitor mvisitor(nodep);
|
||||
BrokenCheckVisitor cvisitor(nodep);
|
||||
BrokenTable::doneWithTree();
|
||||
const BrokenMarkVisitor mvisitor{nodep};
|
||||
const BrokenCheckVisitor cvisitor{nodep};
|
||||
s_allocTable.checkForLeaks();
|
||||
s_linkableTable.clear();
|
||||
s_brokenCntGlobal.inc();
|
||||
inBroken = false;
|
||||
}
|
||||
}
|
||||
|
||||
void V3Broken::addNewed(AstNode* nodep) { BrokenTable::addNewed(nodep); }
|
||||
void V3Broken::deleted(AstNode* nodep) { BrokenTable::deleted(nodep); }
|
||||
bool V3Broken::isAllocated(AstNode* nodep) { return BrokenTable::isAllocated(nodep); }
|
||||
//######################################################################
|
||||
// Self test
|
||||
|
||||
void V3Broken::selfTest() {
|
||||
// Warmup addNewed and deleted for coverage, as otherwise only with VL_LEAK_CHECKS
|
||||
FileLine* fl = new FileLine(FileLine::commandLineFilename());
|
||||
auto* newp = new AstBegin(fl, "[EditWrapper]", nullptr);
|
||||
#ifndef VL_LEAK_CHECKS
|
||||
// Exercise addNewed and deleted for coverage, as otherwise only used with VL_LEAK_CHECKS
|
||||
FileLine* const fl = new FileLine(FileLine::commandLineFilename());
|
||||
const AstNode* const newp = new AstBegin(fl, "[EditWrapper]", nullptr);
|
||||
addNewed(newp);
|
||||
deleted(newp);
|
||||
#endif
|
||||
VL_DO_DANGLING(delete newp, newp);
|
||||
}
|
||||
|
||||
+7
-6
@@ -20,17 +20,18 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
// Forward declare so we can include this in V3Ast.h
|
||||
class AstNode;
|
||||
class AstNetlist;
|
||||
|
||||
class V3Broken final {
|
||||
public:
|
||||
static void brokenAll(AstNetlist* nodep);
|
||||
static void addNewed(AstNode* nodep);
|
||||
static void deleted(AstNode* nodep);
|
||||
static bool isAllocated(AstNode* nodep);
|
||||
static bool isLinkable(const AstNode* nodep);
|
||||
static void addNewed(const AstNode* nodep);
|
||||
static void deleted(const AstNode* nodep);
|
||||
static void selfTest();
|
||||
};
|
||||
|
||||
|
||||
+31
-28
@@ -39,7 +39,7 @@ public:
|
||||
enum en : uint8_t { MODULE, CLASS, COVERAGE };
|
||||
|
||||
private:
|
||||
enum en m_e;
|
||||
const enum en m_e;
|
||||
|
||||
public:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
@@ -60,7 +60,7 @@ private:
|
||||
AstCFunc* makeNewFunc() {
|
||||
const int funcNum = m_newFunctions.size();
|
||||
const string funcName = m_basename + "_" + cvtToStr(funcNum);
|
||||
AstCFunc* const funcp = new AstCFunc(m_modp->fileline(), funcName, nullptr, "void");
|
||||
AstCFunc* const funcp = new AstCFunc{m_modp->fileline(), funcName, nullptr, "void"};
|
||||
funcp->isStatic(false);
|
||||
funcp->isLoose(!m_type.isClass());
|
||||
funcp->declPrivate(true);
|
||||
@@ -74,7 +74,7 @@ private:
|
||||
preventUnusedStmt = "if (false && first) {} // Prevent unused\n";
|
||||
}
|
||||
if (!preventUnusedStmt.empty()) {
|
||||
funcp->addStmtsp(new AstCStmt(m_modp->fileline(), preventUnusedStmt));
|
||||
funcp->addStmtsp(new AstCStmt{m_modp->fileline(), preventUnusedStmt});
|
||||
}
|
||||
m_modp->addStmtp(funcp);
|
||||
m_numStmts = 0;
|
||||
@@ -91,9 +91,9 @@ public:
|
||||
}
|
||||
|
||||
V3CCtorsBuilder(AstNodeModule* nodep, const string& basename, VCtorType type)
|
||||
: m_modp(nodep)
|
||||
: m_modp{nodep}
|
||||
, m_basename{basename}
|
||||
, m_type(type) {
|
||||
, m_type{type} {
|
||||
// Note: The constructor is always called, even if empty, so we must always create at least
|
||||
// one.
|
||||
m_newFunctions.push_back(makeNewFunc());
|
||||
@@ -108,7 +108,7 @@ public:
|
||||
AstCFunc* const rootFuncp = makeNewFunc();
|
||||
rootFuncp->name(m_basename);
|
||||
for (AstCFunc* const funcp : m_newFunctions) {
|
||||
AstCCall* const callp = new AstCCall(m_modp->fileline(), funcp);
|
||||
AstCCall* const callp = new AstCCall{m_modp->fileline(), funcp};
|
||||
if (m_type.isClass()) {
|
||||
callp->argTypes("vlSymsp");
|
||||
} else {
|
||||
@@ -127,8 +127,9 @@ private:
|
||||
//######################################################################
|
||||
|
||||
void V3CCtors::evalAsserts() {
|
||||
AstNodeModule* modp = v3Global.rootp()->modulesp(); // Top module wrapper
|
||||
AstCFunc* funcp = new AstCFunc(modp->fileline(), "_eval_debug_assertions", nullptr, "void");
|
||||
AstNodeModule* const modp = v3Global.rootp()->modulesp(); // Top module wrapper
|
||||
AstCFunc* const funcp
|
||||
= new AstCFunc{modp->fileline(), "_eval_debug_assertions", nullptr, "void"};
|
||||
funcp->declPrivate(true);
|
||||
funcp->isStatic(false);
|
||||
funcp->isLoose(true);
|
||||
@@ -136,30 +137,31 @@ void V3CCtors::evalAsserts() {
|
||||
funcp->ifdef("VL_DEBUG");
|
||||
modp->addStmtp(funcp);
|
||||
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
||||
if (AstVar* varp = VN_CAST(np, Var)) {
|
||||
if (AstVar* const varp = VN_CAST(np, Var)) {
|
||||
if (varp->isPrimaryInish() && !varp->isSc()) {
|
||||
if (AstBasicDType* basicp = VN_CAST(varp->dtypeSkipRefp(), BasicDType)) {
|
||||
if (const AstBasicDType* const basicp
|
||||
= VN_CAST(varp->dtypeSkipRefp(), BasicDType)) {
|
||||
const int storedWidth = basicp->widthAlignBytes() * 8;
|
||||
const int lastWordWidth = varp->width() % storedWidth;
|
||||
if (lastWordWidth != 0) {
|
||||
// if (signal & CONST(upper_non_clean_mask)) { fail; }
|
||||
AstVarRef* const vrefp
|
||||
= new AstVarRef(varp->fileline(), varp, VAccess::READ);
|
||||
= new AstVarRef{varp->fileline(), varp, VAccess::READ};
|
||||
vrefp->selfPointer("this");
|
||||
AstNode* newp = vrefp;
|
||||
if (varp->isWide()) {
|
||||
newp = new AstWordSel(
|
||||
newp = new AstWordSel{
|
||||
varp->fileline(), newp,
|
||||
new AstConst(varp->fileline(), varp->widthWords() - 1));
|
||||
new AstConst(varp->fileline(), varp->widthWords() - 1)};
|
||||
}
|
||||
uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth);
|
||||
newp = new AstAnd(varp->fileline(), newp,
|
||||
const uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth);
|
||||
newp = new AstAnd{varp->fileline(), newp,
|
||||
new AstConst(varp->fileline(), AstConst::WidthedValue(),
|
||||
storedWidth, value));
|
||||
AstNodeIf* ifp = new AstIf(
|
||||
storedWidth, value)};
|
||||
AstNodeIf* const ifp = new AstIf{
|
||||
varp->fileline(), newp,
|
||||
new AstCStmt(varp->fileline(), "Verilated::overWidthError(\""
|
||||
+ varp->prettyName() + "\");"));
|
||||
new AstCStmt{varp->fileline(), "Verilated::overWidthError(\""
|
||||
+ varp->prettyName() + "\");"}};
|
||||
ifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||
newp = ifp;
|
||||
funcp->addStmtsp(newp);
|
||||
@@ -174,23 +176,24 @@ void V3CCtors::cctorsAll() {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
evalAsserts();
|
||||
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp;
|
||||
modp = VN_CAST(modp->nextp(), NodeModule)) {
|
||||
modp = VN_AS(modp->nextp(), NodeModule)) {
|
||||
// Process each module in turn
|
||||
{
|
||||
V3CCtorsBuilder var_reset(modp, "_ctor_var_reset",
|
||||
VN_IS(modp, Class) ? VCtorType::CLASS : VCtorType::MODULE);
|
||||
V3CCtorsBuilder var_reset{modp, "_ctor_var_reset",
|
||||
VN_IS(modp, Class) ? VCtorType::CLASS : VCtorType::MODULE};
|
||||
|
||||
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
||||
if (AstVar* const varp = VN_CAST(np, Var)) {
|
||||
if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset()) {
|
||||
const auto vrefp = new AstVarRef(varp->fileline(), varp, VAccess::WRITE);
|
||||
var_reset.add(new AstCReset(varp->fileline(), vrefp));
|
||||
if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset()
|
||||
&& !varp->isParam()) {
|
||||
const auto vrefp = new AstVarRef{varp->fileline(), varp, VAccess::WRITE};
|
||||
var_reset.add(new AstCReset{varp->fileline(), vrefp});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (v3Global.opt.coverage()) {
|
||||
V3CCtorsBuilder configure_coverage(modp, "_configure_coverage", VCtorType::COVERAGE);
|
||||
V3CCtorsBuilder configure_coverage{modp, "_configure_coverage", VCtorType::COVERAGE};
|
||||
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
||||
if (AstCoverDecl* const coverp = VN_CAST(np, CoverDecl)) {
|
||||
np = coverp->backp();
|
||||
@@ -198,8 +201,8 @@ void V3CCtors::cctorsAll() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (AstClass* classp = VN_CAST(modp, Class)) {
|
||||
AstCFunc* funcp = new AstCFunc(modp->fileline(), "~", nullptr, "");
|
||||
if (const AstClass* const classp = VN_CAST(modp, Class)) {
|
||||
AstCFunc* const funcp = new AstCFunc{modp->fileline(), "~", nullptr, ""};
|
||||
funcp->isDestructor(true);
|
||||
funcp->isStatic(false);
|
||||
// If can be referred to by base pointer, need virtual delete
|
||||
|
||||
+36
-166
@@ -13,14 +13,12 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
// V3Class's Transformations:
|
||||
// V3CUse's Transformations:
|
||||
//
|
||||
// Each module:
|
||||
// Each cell:
|
||||
// Create CUse for cell forward declaration
|
||||
// Each class:
|
||||
// Create string access functions
|
||||
// Search for dtypes referencing class, and create CUse for forward declaraion
|
||||
// Search for dtypes referencing class, and create CUse for forward declaration
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
@@ -30,61 +28,37 @@
|
||||
#include "V3Global.h"
|
||||
#include "V3CUse.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3EmitCBase.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
|
||||
//######################################################################
|
||||
|
||||
class CUseState final {
|
||||
private:
|
||||
// MEMBERS
|
||||
AstNodeModule* m_modInsertp; // Current module to insert AstCUse under
|
||||
using UseString = std::pair<VUseType, std::string>;
|
||||
std::map<const UseString, AstCUse*> m_didUse; // What we already used
|
||||
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstClass::user1() -> bool. True if class needs to_string dumper
|
||||
AstUser1InUse m_inuser1;
|
||||
// AstClass::user2() -> bool. True if iterated
|
||||
AstUser2InUse m_inuser2;
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
public:
|
||||
AstCUse* newUse(AstNode* nodep, VUseType useType, const string& name) {
|
||||
UseString key(useType, name);
|
||||
if (m_didUse.find(key) == m_didUse.end()) {
|
||||
AstCUse* newp = new AstCUse(nodep->fileline(), useType, name);
|
||||
m_modInsertp->addStmtp(newp);
|
||||
UINFO(8, "Insert " << newp << endl);
|
||||
m_didUse[key] = newp;
|
||||
}
|
||||
return m_didUse[key];
|
||||
}
|
||||
|
||||
// CONSTRUCTORS
|
||||
explicit CUseState(AstNodeModule* nodep)
|
||||
: m_modInsertp{nodep} {}
|
||||
virtual ~CUseState() = default;
|
||||
VL_UNCOPYABLE(CUseState);
|
||||
};
|
||||
|
||||
// Visit within a module all nodes and data types they reference, finding
|
||||
// any classes so we can make sure they are defined when Verilated code
|
||||
// compiles
|
||||
class CUseDTypeVisitor final : public AstNVisitor {
|
||||
class CUseVisitor final : public AstNVisitor {
|
||||
// NODE STATE
|
||||
// AstNode::user1() -> bool. True if already visited
|
||||
const AstUser1InUse m_inuser1;
|
||||
|
||||
// MEMBERS
|
||||
CUseState& m_stater; // State for inserter
|
||||
bool m_impOnly = false; // In details needed only for implementation
|
||||
AstNodeModule* const m_modp; // Current module
|
||||
std::set<std::pair<VUseType, std::string>> m_didUse; // What we already used
|
||||
|
||||
// METHODS
|
||||
void addNewUse(AstNode* nodep, VUseType useType, const string& name) {
|
||||
if (m_didUse.emplace(useType, name).second) {
|
||||
AstCUse* const newp = new AstCUse{nodep->fileline(), useType, name};
|
||||
m_modp->addStmtp(newp);
|
||||
UINFO(8, "Insert " << newp << endl);
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstClassRefDType* nodep) override {
|
||||
if (nodep->user2SetOnce()) return; // Process once
|
||||
if (!m_impOnly) m_stater.newUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name());
|
||||
// No class.h, it's inside the class package's h file
|
||||
m_stater.newUse(nodep, VUseType::IMP_INCLUDE, nodep->classp()->classOrPackagep()->name());
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (!m_impOnly) addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name());
|
||||
// Need to include extends() when we implement, but no need for pointers to know
|
||||
VL_RESTORER(m_impOnly);
|
||||
{
|
||||
@@ -93,131 +67,27 @@ class CUseDTypeVisitor final : public AstNVisitor {
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeDType* nodep) override {
|
||||
if (nodep->user2SetOnce()) return; // Process once
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (nodep->virtRefDTypep()) iterate(nodep->virtRefDTypep());
|
||||
if (nodep->virtRefDType2p()) iterate(nodep->virtRefDType2p());
|
||||
}
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
if (nodep->user2SetOnce()) return; // Process once
|
||||
if (nodep->dtypep() && !nodep->dtypep()->user2()) iterate(nodep->dtypep());
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (nodep->dtypep() && !nodep->dtypep()->user1()) iterate(nodep->dtypep());
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCell* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
// Currently no IMP_INCLUDE because we include __Syms which has them all
|
||||
addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->modp()->name());
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CUseDTypeVisitor(AstNodeModule* nodep, CUseState& stater)
|
||||
: m_stater(stater) { // Need () or GCC 4.8 false warning
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CUseDTypeVisitor() override = default;
|
||||
VL_UNCOPYABLE(CUseDTypeVisitor);
|
||||
};
|
||||
|
||||
class CUseVisitor final : public AstNVisitor {
|
||||
// MEMBERS
|
||||
CUseState m_state; // Inserter state
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// Module use builders
|
||||
void makeUseCells(AstNodeModule* nodep) {
|
||||
for (AstNode* itemp = nodep->stmtsp(); itemp; itemp = itemp->nextp()) {
|
||||
if (AstCell* cellp = VN_CAST(itemp, Cell)) {
|
||||
// Currently no include because we include __Syms which has them all
|
||||
m_state.newUse(nodep, VUseType::INT_FWD_CLASS, cellp->modp()->name());
|
||||
}
|
||||
}
|
||||
}
|
||||
void makeVlToString(AstClass* nodep) {
|
||||
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "VL_TO_STRING", nullptr, "std::string");
|
||||
funcp->argTypes("const VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(nodep)
|
||||
+ ">& obj");
|
||||
funcp->isMethod(false);
|
||||
funcp->isConst(false);
|
||||
funcp->isStatic(false);
|
||||
funcp->protect(false);
|
||||
AstNode* exprp = new AstCMath(nodep->fileline(), "obj ? obj->to_string() : \"null\"", 0);
|
||||
exprp->dtypeSetString();
|
||||
funcp->addStmtsp(new AstCReturn(nodep->fileline(), exprp));
|
||||
nodep->addStmtp(funcp);
|
||||
}
|
||||
void makeToString(AstClass* nodep) {
|
||||
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "to_string", nullptr, "std::string");
|
||||
funcp->isConst(true);
|
||||
funcp->isStatic(false);
|
||||
funcp->protect(false);
|
||||
AstNode* exprp = new AstCMath(nodep->fileline(),
|
||||
R"(std::string("'{") + to_string_middle() + "}")", 0);
|
||||
exprp->dtypeSetString();
|
||||
funcp->addStmtsp(new AstCReturn(nodep->fileline(), exprp));
|
||||
nodep->addStmtp(funcp);
|
||||
}
|
||||
void makeToStringMiddle(AstClass* nodep) {
|
||||
AstCFunc* funcp
|
||||
= new AstCFunc(nodep->fileline(), "to_string_middle", nullptr, "std::string");
|
||||
funcp->isConst(true);
|
||||
funcp->isStatic(false);
|
||||
funcp->protect(false);
|
||||
funcp->addStmtsp(new AstCStmt(nodep->fileline(), "std::string out;\n"));
|
||||
std::string comma;
|
||||
for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) {
|
||||
if (auto* varp = VN_CAST(itemp, Var)) {
|
||||
if (!varp->isParam()) {
|
||||
string stmt = "out += \"";
|
||||
stmt += comma;
|
||||
comma = ", ";
|
||||
stmt += itemp->origNameProtect();
|
||||
stmt += ":\" + ";
|
||||
if (itemp->isWide()) {
|
||||
stmt += "VL_TO_STRING_W(";
|
||||
stmt += cvtToStr(itemp->widthWords());
|
||||
stmt += ", ";
|
||||
} else {
|
||||
stmt += "VL_TO_STRING(";
|
||||
}
|
||||
stmt += itemp->nameProtect();
|
||||
stmt += ");\n";
|
||||
nodep->user1(true); // So what we extend dumps this
|
||||
funcp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nodep->extendsp() && nodep->extendsp()->classp()->user1()) {
|
||||
string stmt = "out += \"";
|
||||
if (!comma.empty()) stmt += "\", \"+ ";
|
||||
// comma = ", "; // Nothing further so not needed
|
||||
stmt += nodep->extendsp()->dtypep()->nameProtect();
|
||||
stmt += "::to_string_middle();\n";
|
||||
nodep->user1(true); // So what we extend dumps this
|
||||
funcp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
|
||||
}
|
||||
funcp->addStmtsp(new AstCStmt(nodep->fileline(), "return out;\n"));
|
||||
nodep->addStmtp(funcp);
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
if (v3Global.opt.trace()) {
|
||||
AstCUse* usep
|
||||
= m_state.newUse(nodep, VUseType::INT_FWD_CLASS, v3Global.opt.traceClassBase());
|
||||
usep->protect(false);
|
||||
}
|
||||
makeUseCells(nodep);
|
||||
{ CUseDTypeVisitor dtypeVisitor(nodep, m_state); }
|
||||
if (AstClass* classp = VN_CAST(nodep, Class)) {
|
||||
makeVlToString(classp);
|
||||
makeToString(classp);
|
||||
makeToStringMiddle(classp);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNode*) override {} // All in AstNodeModule
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CUseVisitor(AstNodeModule* nodep)
|
||||
: m_state{nodep} {
|
||||
iterate(nodep);
|
||||
explicit CUseVisitor(AstNodeModule* modp)
|
||||
: m_modp(modp) {
|
||||
iterate(modp);
|
||||
}
|
||||
virtual ~CUseVisitor() override = default;
|
||||
VL_UNCOPYABLE(CUseVisitor);
|
||||
@@ -230,10 +100,10 @@ void V3CUse::cUseAll() {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
// Call visitor separately for each module, so visitor state is cleared
|
||||
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp;
|
||||
modp = VN_CAST(modp->nextp(), NodeModule)) {
|
||||
modp = VN_AS(modp->nextp(), NodeModule)) {
|
||||
// Insert under this module; someday we should e.g. make Ast
|
||||
// for each output file and put under that
|
||||
CUseVisitor visitor(modp);
|
||||
CUseVisitor{modp};
|
||||
}
|
||||
V3Global::dumpCheckGlobalTree("cuse", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+44
-42
@@ -52,20 +52,20 @@
|
||||
|
||||
class CaseLintVisitor final : public AstNVisitor {
|
||||
private:
|
||||
AstNodeCase* m_caseExprp
|
||||
const AstNodeCase* m_caseExprp
|
||||
= nullptr; // Under a CASE value node, if so the relevant case statement
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
virtual void visit(AstNodeCase* nodep) override {
|
||||
if (VN_IS(nodep, Case) && VN_CAST(nodep, Case)->casex()) {
|
||||
if (VN_IS(nodep, Case) && VN_AS(nodep, Case)->casex()) {
|
||||
nodep->v3warn(CASEX, "Suggest casez (with ?'s) in place of casex (with X's)");
|
||||
}
|
||||
// Detect multiple defaults
|
||||
bool hitDefault = false;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
itemp = VN_AS(itemp->nextp(), CaseItem)) {
|
||||
if (itemp->isDefault()) {
|
||||
if (hitDefault) {
|
||||
itemp->v3error("Multiple default statements in case statement.");
|
||||
@@ -79,7 +79,7 @@ private:
|
||||
m_caseExprp = nodep;
|
||||
iterate(nodep->exprp());
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
itemp = VN_AS(itemp->nextp(), CaseItem)) {
|
||||
iterateAndNextNull(itemp->condsp());
|
||||
}
|
||||
m_caseExprp = nullptr;
|
||||
@@ -91,11 +91,11 @@ private:
|
||||
if (VN_IS(m_caseExprp, GenCase)) {
|
||||
nodep->v3error("Use of x/? constant in generate case statement, "
|
||||
"(no such thing as 'generate casez')");
|
||||
} else if (VN_IS(m_caseExprp, Case) && VN_CAST(m_caseExprp, Case)->casex()) {
|
||||
} else if (VN_IS(m_caseExprp, Case) && VN_AS(m_caseExprp, Case)->casex()) {
|
||||
// Don't sweat it, we already complained about casex in general
|
||||
} else if (VN_IS(m_caseExprp, Case)
|
||||
&& (VN_CAST(m_caseExprp, Case)->casez()
|
||||
|| VN_CAST(m_caseExprp, Case)->caseInside())) {
|
||||
&& (VN_AS(m_caseExprp, Case)->casez()
|
||||
|| VN_AS(m_caseExprp, Case)->caseInside())) {
|
||||
if (nodep->num().isAnyX()) {
|
||||
nodep->v3warn(CASEWITHX, "Use of x constant in casez statement, "
|
||||
"(perhaps intended ?/z in constant)");
|
||||
@@ -122,12 +122,12 @@ private:
|
||||
// NODE STATE
|
||||
// Cleared each Case
|
||||
// AstIf::user3() -> bool. Set true to indicate clone not needed
|
||||
AstUser3InUse m_inuser3;
|
||||
const AstUser3InUse m_inuser3;
|
||||
|
||||
// STATE
|
||||
VDouble0 m_statCaseFast; // Statistic tracking
|
||||
VDouble0 m_statCaseSlow; // Statistic tracking
|
||||
AstNode* m_alwaysp = nullptr; // Always in which case is located
|
||||
const AstNode* m_alwaysp = nullptr; // Always in which case is located
|
||||
|
||||
// Per-CASE
|
||||
int m_caseWidth = 0; // Width of valueItems
|
||||
@@ -145,7 +145,7 @@ private:
|
||||
m_caseItems = 0;
|
||||
m_caseNoOverlapsAllCovered = true;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
itemp = VN_AS(itemp->nextp(), CaseItem)) {
|
||||
for (AstNode* icondp = itemp->condsp(); icondp; icondp = icondp->nextp()) {
|
||||
if (icondp->width() > width) width = icondp->width();
|
||||
if (icondp->isDouble()) opaque = true;
|
||||
@@ -159,7 +159,7 @@ private:
|
||||
return false; // Too wide for analysis
|
||||
}
|
||||
UINFO(8, "Simple case statement: " << nodep << endl);
|
||||
uint32_t numCases = 1UL << m_caseWidth;
|
||||
const uint32_t numCases = 1UL << m_caseWidth;
|
||||
// Zero list of items for each value
|
||||
for (uint32_t i = 0; i < numCases; ++i) m_valueItem[i] = nullptr;
|
||||
// Now pick up the values for each assignment
|
||||
@@ -167,20 +167,20 @@ private:
|
||||
bool reportedOverlap = false;
|
||||
bool reportedSubcase = false;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
itemp = VN_AS(itemp->nextp(), CaseItem)) {
|
||||
for (AstNode* icondp = itemp->condsp(); icondp; icondp = icondp->nextp()) {
|
||||
// if (debug() >= 9) icondp->dumpTree(cout, " caseitem: ");
|
||||
AstConst* iconstp = VN_CAST(icondp, Const);
|
||||
AstConst* const iconstp = VN_AS(icondp, Const);
|
||||
UASSERT_OBJ(iconstp, nodep, "above 'can't parse' should have caught this");
|
||||
if (neverItem(nodep, iconstp)) {
|
||||
// X in casez can't ever be executed
|
||||
} else {
|
||||
V3Number nummask(itemp, iconstp->width());
|
||||
nummask.opBitsNonX(iconstp->num());
|
||||
uint32_t mask = nummask.toUInt();
|
||||
const uint32_t mask = nummask.toUInt();
|
||||
V3Number numval(itemp, iconstp->width());
|
||||
numval.opBitsOne(iconstp->num());
|
||||
uint32_t val = numval.toUInt();
|
||||
const uint32_t val = numval.toUInt();
|
||||
|
||||
uint32_t firstOverlap = 0;
|
||||
bool foundOverlap = false;
|
||||
@@ -190,7 +190,7 @@ private:
|
||||
if (!m_valueItem[i]) {
|
||||
m_valueItem[i] = itemp;
|
||||
foundHit = true;
|
||||
} else if (!foundOverlap && !itemp->ignoreOverlap()) {
|
||||
} else if (!foundOverlap) {
|
||||
firstOverlap = i;
|
||||
foundOverlap = true;
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
@@ -244,7 +244,7 @@ private:
|
||||
// Convert valueItem from AstCaseItem* to the expression
|
||||
// Not done earlier, as we may now have a nullptr because it's just a ";" NOP branch
|
||||
for (uint32_t i = 0; i < numCases; ++i) {
|
||||
m_valueItem[i] = VN_CAST(m_valueItem[i], CaseItem)->bodysp();
|
||||
m_valueItem[i] = VN_AS(m_valueItem[i], CaseItem)->bodysp();
|
||||
}
|
||||
return true; // All is fine
|
||||
}
|
||||
@@ -289,10 +289,11 @@ private:
|
||||
// V3Number nummask (cexprp, cexprp->width(), (1UL<<msb));
|
||||
// AstNode* and1p = new AstAnd(cexprp->fileline(), cexprp->cloneTree(false),
|
||||
// new AstConst(cexprp->fileline(), nummask));
|
||||
AstNode* and1p = new AstSel(cexprp->fileline(), cexprp->cloneTree(false), msb, 1);
|
||||
AstNode* eqp
|
||||
AstNode* const and1p
|
||||
= new AstSel(cexprp->fileline(), cexprp->cloneTree(false), msb, 1);
|
||||
AstNode* const eqp
|
||||
= new AstNeq(cexprp->fileline(), new AstConst(cexprp->fileline(), 0), and1p);
|
||||
AstIf* ifp = new AstIf(cexprp->fileline(), eqp, tree1p, tree0p);
|
||||
AstIf* const ifp = new AstIf(cexprp->fileline(), eqp, tree1p, tree0p);
|
||||
ifp->user3(1); // So we don't bother to clone it
|
||||
return ifp;
|
||||
}
|
||||
@@ -302,11 +303,11 @@ private:
|
||||
// CASEx(cexpr,....
|
||||
// -> tree of IF(msb, IF(msb-1, 11, 10)
|
||||
// IF(msb-1, 01, 00))
|
||||
AstNode* cexprp = nodep->exprp()->unlinkFrBack();
|
||||
AstNode* const cexprp = nodep->exprp()->unlinkFrBack();
|
||||
|
||||
if (debug() >= 9) { // LCOV_EXCL_START
|
||||
for (uint32_t i = 0; i < (1UL << m_caseWidth); ++i) {
|
||||
if (AstNode* itemp = m_valueItem[i]) {
|
||||
if (const AstNode* const itemp = m_valueItem[i]) {
|
||||
UINFO(9, "Value " << std::hex << i << " " << itemp << endl);
|
||||
}
|
||||
}
|
||||
@@ -335,13 +336,13 @@ private:
|
||||
// -> IF((cexpr==icond1),istmts1,
|
||||
// IF((EQ (AND MASK cexpr) (AND MASK icond1)
|
||||
// ,istmts2, istmts3
|
||||
AstNode* cexprp = nodep->exprp()->unlinkFrBack();
|
||||
AstNode* const cexprp = nodep->exprp()->unlinkFrBack();
|
||||
// We'll do this in two stages. First stage, convert the conditions to
|
||||
// the appropriate IF AND terms.
|
||||
if (debug() >= 9) nodep->dumpTree(cout, " _comp_IN: ");
|
||||
bool hadDefault = false;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
itemp = VN_AS(itemp->nextp(), CaseItem)) {
|
||||
if (!itemp->condsp()) {
|
||||
// Default clause. Just make true, we'll optimize it away later
|
||||
itemp->condsp(new AstConst(itemp->fileline(), AstConst::BitTrue()));
|
||||
@@ -355,7 +356,7 @@ private:
|
||||
icondp->unlinkFrBack();
|
||||
|
||||
AstNode* condp = nullptr; // Default is to use and1p/and2p
|
||||
AstConst* iconstp = VN_CAST(icondp, Const);
|
||||
AstConst* const iconstp = VN_CAST(icondp, Const);
|
||||
if (iconstp && neverItem(nodep, iconstp)) {
|
||||
// X in casez can't ever be executed
|
||||
VL_DO_DANGLING(icondp->deleteTree(), icondp);
|
||||
@@ -363,7 +364,7 @@ private:
|
||||
// For simplicity, make expression that is not equal, and let later
|
||||
// optimizations remove it
|
||||
condp = new AstConst(itemp->fileline(), AstConst::BitFalse());
|
||||
} else if (AstInsideRange* irangep = VN_CAST(icondp, InsideRange)) {
|
||||
} else if (AstInsideRange* const irangep = VN_CAST(icondp, InsideRange)) {
|
||||
// Similar logic in V3Width::visit(AstInside)
|
||||
condp = irangep->newAndFromInside(cexprp, irangep->lhsp()->unlinkFrBack(),
|
||||
irangep->rhsp()->unlinkFrBack());
|
||||
@@ -373,18 +374,19 @@ private:
|
||||
nummask.opBitsNonX(iconstp->num());
|
||||
V3Number numval(itemp, iconstp->width());
|
||||
numval.opBitsOne(iconstp->num());
|
||||
AstNode* and1p = new AstAnd(itemp->fileline(), cexprp->cloneTree(false),
|
||||
new AstConst(itemp->fileline(), nummask));
|
||||
AstNode* and2p = new AstAnd(itemp->fileline(),
|
||||
new AstConst(itemp->fileline(), numval),
|
||||
new AstConst(itemp->fileline(), nummask));
|
||||
AstNode* const and1p
|
||||
= new AstAnd(itemp->fileline(), cexprp->cloneTree(false),
|
||||
new AstConst(itemp->fileline(), nummask));
|
||||
AstNode* const and2p = new AstAnd(
|
||||
itemp->fileline(), new AstConst(itemp->fileline(), numval),
|
||||
new AstConst(itemp->fileline(), nummask));
|
||||
VL_DO_DANGLING(icondp->deleteTree(), icondp);
|
||||
VL_DANGLING(iconstp);
|
||||
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
|
||||
} else {
|
||||
// Not a caseX mask, we can simply build CASEEQ(cexpr icond)
|
||||
AstNode* and1p = cexprp->cloneTree(false);
|
||||
AstNode* and2p = icondp;
|
||||
AstNode* const and1p = cexprp->cloneTree(false);
|
||||
AstNode* const and2p = icondp;
|
||||
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
|
||||
}
|
||||
if (!ifexprp) {
|
||||
@@ -416,16 +418,16 @@ private:
|
||||
AstIf* groupnextp = nullptr;
|
||||
AstIf* itemnextp = nullptr;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
AstNode* istmtsp = itemp->bodysp(); // Maybe null -- no action.
|
||||
itemp = VN_AS(itemp->nextp(), CaseItem)) {
|
||||
AstNode* const istmtsp = itemp->bodysp(); // Maybe null -- no action.
|
||||
if (istmtsp) istmtsp->unlinkFrBackWithNext();
|
||||
// Expressioned clause
|
||||
AstNode* ifexprp = itemp->condsp()->unlinkFrBack();
|
||||
AstNode* const ifexprp = itemp->condsp()->unlinkFrBack();
|
||||
{ // Prepare for next group
|
||||
if (++depth > CASE_ENCODER_GROUP_DEPTH) depth = 1;
|
||||
if (depth == 1) { // First group or starting new group
|
||||
itemnextp = nullptr;
|
||||
AstIf* newp
|
||||
AstIf* const newp
|
||||
= new AstIf(itemp->fileline(), ifexprp->cloneTree(true), nullptr, nullptr);
|
||||
if (groupnextp) {
|
||||
groupnextp->addElsesp(newp);
|
||||
@@ -434,7 +436,7 @@ private:
|
||||
}
|
||||
groupnextp = newp;
|
||||
} else { // Continue group, modify if condition to OR in this new condition
|
||||
AstNode* condp = groupnextp->condp()->unlinkFrBack();
|
||||
AstNode* const condp = groupnextp->condp()->unlinkFrBack();
|
||||
groupnextp->condp(
|
||||
new AstOr(ifexprp->fileline(), condp, ifexprp->cloneTree(true)));
|
||||
}
|
||||
@@ -446,7 +448,7 @@ private:
|
||||
VL_DO_DANGLING(itemexprp->deleteTree(), itemexprp);
|
||||
itemexprp = new AstConst(itemp->fileline(), AstConst::BitTrue());
|
||||
}
|
||||
AstIf* newp = new AstIf(itemp->fileline(), itemexprp, istmtsp, nullptr);
|
||||
AstIf* const newp = new AstIf(itemp->fileline(), itemexprp, istmtsp, nullptr);
|
||||
if (itemnextp) {
|
||||
itemnextp->addElsesp(newp);
|
||||
} else {
|
||||
@@ -474,7 +476,7 @@ private:
|
||||
// covered, we're done with it.
|
||||
// Else, convert to a normal statement parallel with the case statement.
|
||||
if (nodep->notParallelp() && !noOverlapsAllCovered) {
|
||||
AstNode* parp = nodep->notParallelp()->unlinkFrBackWithNext();
|
||||
AstNode* const parp = nodep->notParallelp()->unlinkFrBackWithNext();
|
||||
nodep->addNextHere(parp);
|
||||
}
|
||||
}
|
||||
@@ -530,10 +532,10 @@ public:
|
||||
|
||||
void V3Case::caseAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CaseVisitor visitor(nodep); } // Destruct before checking
|
||||
{ CaseVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("case", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
void V3Case::caseLint(AstNodeCase* nodep) {
|
||||
UINFO(4, __FUNCTION__ << ": " << endl);
|
||||
CaseLintVisitor visitor(nodep);
|
||||
{ CaseLintVisitor{nodep}; }
|
||||
}
|
||||
|
||||
+9
-7
@@ -54,7 +54,7 @@ private:
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstNode::user() // bool. Indicates node is of known size
|
||||
AstUser1InUse m_inuser1;
|
||||
const AstUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
|
||||
@@ -66,7 +66,8 @@ private:
|
||||
AstNRelinker relinkHandle;
|
||||
nodep->unlinkFrBack(&relinkHandle);
|
||||
//
|
||||
AstCCast* castp = new AstCCast(nodep->fileline(), nodep, needsize, nodep->widthMin());
|
||||
AstCCast* const castp
|
||||
= new AstCCast{nodep->fileline(), nodep, needsize, nodep->widthMin()};
|
||||
relinkHandle.relink(castp);
|
||||
// if (debug() > 8) castp->dumpTree(cout, "-castins: ");
|
||||
//
|
||||
@@ -103,7 +104,7 @@ private:
|
||||
if (!VN_IS(nodep->backp(), NullCheck)) {
|
||||
AstNRelinker relinkHandle;
|
||||
nodep->unlinkFrBack(&relinkHandle);
|
||||
AstNode* newp = new AstNullCheck(nodep->fileline(), nodep);
|
||||
AstNode* const newp = new AstNullCheck{nodep->fileline(), nodep};
|
||||
relinkHandle.relink(newp);
|
||||
}
|
||||
}
|
||||
@@ -154,9 +155,10 @@ private:
|
||||
}
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep) override {
|
||||
if (nodep->access().isReadOnly() && !VN_IS(nodep->backp(), CCast)
|
||||
&& VN_IS(nodep->backp(), NodeMath) && !VN_IS(nodep->backp(), ArraySel)
|
||||
&& nodep->backp()->width() && castSize(nodep) != castSize(nodep->varp())) {
|
||||
const AstNode* const backp = nodep->backp();
|
||||
if (nodep->access().isReadOnly() && !VN_IS(backp, CCast) && VN_IS(backp, NodeMath)
|
||||
&& !VN_IS(backp, ArraySel) && !VN_IS(backp, RedXor) && 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));
|
||||
@@ -201,6 +203,6 @@ public:
|
||||
|
||||
void V3Cast::castAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CastVisitor visitor(nodep); } // Destruct before checking
|
||||
{ CastVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("cast", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+41
-38
@@ -50,8 +50,8 @@ public:
|
||||
// Graph support classes
|
||||
|
||||
class CdcEitherVertex VL_NOT_FINAL : public V3GraphVertex {
|
||||
AstScope* m_scopep;
|
||||
AstNode* m_nodep;
|
||||
AstScope* const m_scopep;
|
||||
AstNode* const m_nodep;
|
||||
AstSenTree* m_srcDomainp = nullptr;
|
||||
AstSenTree* m_dstDomainp = nullptr;
|
||||
bool m_srcDomainSet : 1;
|
||||
@@ -84,7 +84,7 @@ public:
|
||||
};
|
||||
|
||||
class CdcVarVertex final : public CdcEitherVertex {
|
||||
AstVarScope* m_varScp;
|
||||
AstVarScope* const m_varScp;
|
||||
int m_cntAsyncRst = 0;
|
||||
bool m_fromFlop = false;
|
||||
|
||||
@@ -217,15 +217,15 @@ private:
|
||||
// AstVarScope::user2 -> bool Used in sensitivity list
|
||||
// {statement}Node::user1p -> CdcLogicVertex* for this statement
|
||||
// AstNode::user3 -> bool True indicates to print %% (via V3EmitV)
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
AstUser3InUse m_inuser3;
|
||||
const AstUser1InUse m_inuser1;
|
||||
const AstUser2InUse m_inuser2;
|
||||
const AstUser3InUse m_inuser3;
|
||||
|
||||
// STATE
|
||||
V3Graph m_graph; // Scoreboard of var usages/dependencies
|
||||
CdcLogicVertex* m_logicVertexp = nullptr; // Current statement being tracked, nullptr=ignored
|
||||
AstScope* m_scopep = nullptr; // Current scope being processed
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
const AstNodeModule* m_modp = nullptr; // Current module
|
||||
AstSenTree* m_domainp = nullptr; // Current sentree
|
||||
bool m_inDly = false; // In delayed assign
|
||||
int m_inSenItem = 0; // Number of senitems
|
||||
@@ -266,7 +266,7 @@ private:
|
||||
if (varscp->varp()->isPrimaryIO()) {
|
||||
// Create IO vertex - note it's relative to the pointed to var, not where we are
|
||||
// now This allows reporting to easily print the input statement
|
||||
CdcLogicVertex* ioVertexp
|
||||
CdcLogicVertex* const ioVertexp
|
||||
= new CdcLogicVertex(&m_graph, varscp->scopep(), varscp->varp(), nullptr);
|
||||
if (varscp->varp()->isWritable()) {
|
||||
new V3GraphEdge(&m_graph, vertexp, ioVertexp, 1);
|
||||
@@ -337,7 +337,7 @@ private:
|
||||
|
||||
int filelineWidth() {
|
||||
if (!m_filelineWidth) {
|
||||
CdcWidthVisitor visitor(v3Global.rootp());
|
||||
const CdcWidthVisitor visitor{v3Global.rootp()};
|
||||
m_filelineWidth = visitor.maxWidth();
|
||||
}
|
||||
return m_filelineWidth;
|
||||
@@ -351,12 +351,12 @@ private:
|
||||
// userClearVertices is very slow, so we use a generation count instead
|
||||
m_graph.userClearVertices(); // user1: uint32_t - was analyzed generation
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (CdcVarVertex* vvertexp = dynamic_cast<CdcVarVertex*>(itp)) {
|
||||
if (CdcVarVertex* const vvertexp = dynamic_cast<CdcVarVertex*>(itp)) {
|
||||
if (vvertexp->cntAsyncRst()) {
|
||||
m_userGeneration++; // Effectively a userClearVertices()
|
||||
UINFO(8, " Trace One async: " << vvertexp << endl);
|
||||
// Twice, as we need to detect, then propagate
|
||||
CdcEitherVertex* markp = traceAsyncRecurse(vvertexp, false);
|
||||
CdcEitherVertex* const markp = traceAsyncRecurse(vvertexp, false);
|
||||
if (markp) { // Mark is non-nullptr if something bad on this path
|
||||
UINFO(9, " Trace One bad! " << vvertexp << endl);
|
||||
m_userGeneration++; // Effectively a userClearVertices()
|
||||
@@ -381,17 +381,18 @@ private:
|
||||
// Clear out in prep for marking next path
|
||||
if (!mark) vertexp->asyncPath(false);
|
||||
|
||||
if (CdcLogicVertex* vvertexp = dynamic_cast<CdcLogicVertex*>(vertexp)) {
|
||||
if (CdcLogicVertex* const vvertexp = dynamic_cast<CdcLogicVertex*>(vertexp)) {
|
||||
// Any logic considered bad, at the moment, anyhow
|
||||
if (vvertexp->hazard() && !mark_outp) mark_outp = vvertexp;
|
||||
// And keep tracing back so the user can understand what's up
|
||||
} else if (CdcVarVertex* vvertexp = dynamic_cast<CdcVarVertex*>(vertexp)) {
|
||||
} else if (CdcVarVertex* const vvertexp = dynamic_cast<CdcVarVertex*>(vertexp)) {
|
||||
if (mark) vvertexp->asyncPath(true);
|
||||
// If primary I/O, it's ok here back
|
||||
if (vvertexp->varScp()->varp()->isPrimaryInish()) {
|
||||
// Show the source "input" statement if it exists
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
CdcEitherVertex* eFromVertexp = static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
CdcEitherVertex* const eFromVertexp
|
||||
= static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
eFromVertexp->asyncPath(true);
|
||||
}
|
||||
return nullptr;
|
||||
@@ -399,7 +400,8 @@ 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 = static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
CdcEitherVertex* const eFromVertexp
|
||||
= static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
eFromVertexp->asyncPath(true);
|
||||
}
|
||||
return nullptr;
|
||||
@@ -407,8 +409,8 @@ private:
|
||||
}
|
||||
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
CdcEitherVertex* eFromVertexp = static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
CdcEitherVertex* submarkp = traceAsyncRecurse(eFromVertexp, mark);
|
||||
CdcEitherVertex* const eFromVertexp = static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
CdcEitherVertex* const submarkp = traceAsyncRecurse(eFromVertexp, mark);
|
||||
if (submarkp && !mark_outp) mark_outp = submarkp;
|
||||
}
|
||||
|
||||
@@ -417,14 +419,14 @@ private:
|
||||
}
|
||||
|
||||
void dumpAsync(CdcVarVertex* vertexp, CdcEitherVertex* markp) {
|
||||
AstNode* nodep = vertexp->varScp();
|
||||
const AstNode* const nodep = vertexp->varScp();
|
||||
*m_ofp << "\n";
|
||||
*m_ofp << "\n";
|
||||
CdcEitherVertex* targetp = vertexp; // One example destination flop (of possibly many)
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
CdcEitherVertex* eToVertexp = static_cast<CdcEitherVertex*>(edgep->top());
|
||||
CdcEitherVertex* const eToVertexp = static_cast<CdcEitherVertex*>(edgep->top());
|
||||
if (!eToVertexp) targetp = eToVertexp;
|
||||
if (CdcLogicVertex* vvertexp = dynamic_cast<CdcLogicVertex*>(eToVertexp)) {
|
||||
if (const CdcLogicVertex* const vvertexp = dynamic_cast<CdcLogicVertex*>(eToVertexp)) {
|
||||
if (vvertexp->isFlop() // IE the target flop that is upsetting us
|
||||
&& edgep->weight() >= CDC_WEIGHT_ASYNC) { // And var feeds an async reset line
|
||||
targetp = eToVertexp;
|
||||
@@ -452,14 +454,14 @@ private:
|
||||
const string cont = prefix + sep;
|
||||
string nextsep = " ";
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
CdcEitherVertex* eFromVertexp = static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
CdcEitherVertex* const eFromVertexp = static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
if (dumpAsyncRecurse(eFromVertexp, cont, nextsep, level + 1)) nextsep = " | ";
|
||||
}
|
||||
|
||||
// Dump single variable/logic block
|
||||
// See also OrderGraph::loopsVertexCb(V3GraphVertex* vertexp)
|
||||
AstNode* nodep = vertexp->nodep();
|
||||
string front
|
||||
AstNode* const nodep = vertexp->nodep();
|
||||
const string front
|
||||
= pad(filelineWidth(), nodep->fileline()->ascii() + ":") + " " + prefix + " +- ";
|
||||
if (VN_IS(nodep, VarScope)) {
|
||||
*m_ofp << front << "Variable: " << nodep->prettyName() << '\n';
|
||||
@@ -473,7 +475,7 @@ private:
|
||||
if (level)
|
||||
*m_ofp << V3OutFile::indentSpaces(filelineWidth()) << " " << prefix << nextsep << "\n";
|
||||
|
||||
if (CdcLogicVertex* vvertexp = dynamic_cast<CdcLogicVertex*>(vertexp)) {
|
||||
if (CdcLogicVertex* const vvertexp = dynamic_cast<CdcLogicVertex*>(vertexp)) {
|
||||
// Now that we've printed a path with this hazard, don't bother to print any more
|
||||
// Otherwise, we'd get a path for almost every destination flop
|
||||
vvertexp->clearHazard();
|
||||
@@ -495,11 +497,11 @@ private:
|
||||
UINFO(3, __FUNCTION__ << ": " << endl);
|
||||
|
||||
// Trace all sources and sinks
|
||||
for (int traceDests = 0; traceDests < 2; ++traceDests) {
|
||||
for (const bool& traceDests : {false, true}) {
|
||||
UINFO(9, " Trace Direction " << (traceDests ? "dst" : "src") << endl);
|
||||
m_graph.userClearVertices(); // user1: bool - was analyzed
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (CdcVarVertex* vvertexp = dynamic_cast<CdcVarVertex*>(itp)) {
|
||||
if (CdcVarVertex* const vvertexp = dynamic_cast<CdcVarVertex*>(itp)) {
|
||||
UINFO(9, " Trace One edge: " << vvertexp << endl);
|
||||
edgeDomainRecurse(vvertexp, traceDests, 0);
|
||||
}
|
||||
@@ -508,14 +510,14 @@ private:
|
||||
|
||||
const string filename
|
||||
= v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__cdc_edges.txt";
|
||||
const std::unique_ptr<std::ofstream> ofp(V3File::new_ofstream(filename));
|
||||
const std::unique_ptr<std::ofstream> ofp{V3File::new_ofstream(filename)};
|
||||
if (ofp->fail()) v3fatal("Can't write " << filename);
|
||||
*ofp << "Edge Report for " << v3Global.opt.prefix() << '\n';
|
||||
|
||||
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();
|
||||
if (const CdcVarVertex* const vvertexp = dynamic_cast<CdcVarVertex*>(itp)) {
|
||||
const AstVar* const varp = vvertexp->varScp()->varp();
|
||||
{
|
||||
string what = "wire";
|
||||
if (varp->isPrimaryIO()) what = varp->direction().prettyName();
|
||||
@@ -558,11 +560,11 @@ private:
|
||||
} // Fully computed
|
||||
|
||||
std::set<AstSenTree*> senouts; // List of all sensitivities for new signal
|
||||
if (CdcLogicVertex* vvertexp = dynamic_cast<CdcLogicVertex*>(vertexp)) {
|
||||
if (const CdcLogicVertex* const vvertexp = dynamic_cast<CdcLogicVertex*>(vertexp)) {
|
||||
if (vvertexp) {} // Unused
|
||||
} else if (CdcVarVertex* vvertexp = dynamic_cast<CdcVarVertex*>(vertexp)) {
|
||||
} else if (const CdcVarVertex* const vvertexp = dynamic_cast<CdcVarVertex*>(vertexp)) {
|
||||
// If primary I/O, give it domain of the input
|
||||
AstVar* varp = vvertexp->varScp()->varp();
|
||||
const AstVar* const varp = vvertexp->varScp()->varp();
|
||||
if (varp->isPrimaryIO() && varp->isNonOutput() && !traceDests) {
|
||||
senouts.insert(new AstSenTree(
|
||||
varp->fileline(), new AstSenItem(varp->fileline(), AstSenItem::Combo())));
|
||||
@@ -572,13 +574,14 @@ private:
|
||||
// Now combine domains of sources/dests
|
||||
if (traceDests) {
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
CdcEitherVertex* eToVertexp = static_cast<CdcEitherVertex*>(edgep->top());
|
||||
CdcEitherVertex* const 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 = static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
CdcEitherVertex* const eFromVertexp
|
||||
= static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
edgeDomainRecurse(eFromVertexp, traceDests, level + 1);
|
||||
if (eFromVertexp->srcDomainp()) senouts.insert(eFromVertexp->srcDomainp());
|
||||
}
|
||||
@@ -599,7 +602,7 @@ private:
|
||||
}
|
||||
}
|
||||
// If multiple domains need to do complicated optimizations
|
||||
if (senedited) senoutp = VN_CAST(V3Const::constifyExpensiveEdit(senoutp), SenTree);
|
||||
if (senedited) senoutp = VN_AS(V3Const::constifyExpensiveEdit(senoutp), SenTree);
|
||||
if (traceDests) {
|
||||
vertexp->dstDomainSet(true); // Note it's set - domainp may be null, so can't use that
|
||||
vertexp->dstDomainp(senoutp);
|
||||
@@ -653,9 +656,9 @@ private:
|
||||
virtual void visit(AstNodeVarRef* nodep) override {
|
||||
if (m_scopep) {
|
||||
UASSERT_OBJ(m_logicVertexp, nodep, "Var ref not under a logic block");
|
||||
AstVarScope* varscp = nodep->varScopep();
|
||||
AstVarScope* const varscp = nodep->varScopep();
|
||||
UASSERT_OBJ(varscp, nodep, "Var didn't get varscoped in V3Scope.cpp");
|
||||
CdcVarVertex* varvertexp = makeVarVertex(varscp);
|
||||
CdcVarVertex* const varvertexp = makeVarVertex(varscp);
|
||||
UINFO(5, " VARREF to " << varscp << endl);
|
||||
// We use weight of one for normal edges,
|
||||
// Weight of CDC_WEIGHT_ASYNC to indicate feeds async (for reporting)
|
||||
@@ -758,5 +761,5 @@ public:
|
||||
|
||||
void V3Cdc::cdcAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
CdcVisitor visitor(nodep);
|
||||
{ CdcVisitor{nodep}; }
|
||||
}
|
||||
|
||||
+60
-44
@@ -47,11 +47,33 @@ public:
|
||||
AstCFunc* m_tlChgFuncp = nullptr; // Top level change function we're building
|
||||
int m_numStmts = 0; // Number of statements added to m_chgFuncp
|
||||
int m_funcNum = 0; // Number of change functions emitted
|
||||
bool m_madeTopChg = false;
|
||||
|
||||
ChangedState() = default;
|
||||
~ChangedState() = default;
|
||||
|
||||
void maybeCreateChgFuncp() {
|
||||
maybeCreateTopChg();
|
||||
maybeCreateMidChg();
|
||||
}
|
||||
void maybeCreateTopChg() {
|
||||
if (m_madeTopChg) return;
|
||||
m_madeTopChg = true;
|
||||
v3Global.rootp()->changeRequest(true);
|
||||
|
||||
// Create a wrapper change detection function that calls each change detection function
|
||||
m_tlChgFuncp
|
||||
= new AstCFunc{m_scopetopp->fileline(), "_change_request", m_scopetopp, "QData"};
|
||||
m_tlChgFuncp->isStatic(false);
|
||||
m_tlChgFuncp->isLoose(true);
|
||||
m_tlChgFuncp->declPrivate(true);
|
||||
m_scopetopp->addActivep(m_tlChgFuncp);
|
||||
// Each change detection function needs at least one AstChangeDet
|
||||
// to ensure that V3EmitC outputs the necessary code.
|
||||
maybeCreateMidChg();
|
||||
m_chgFuncp->addStmtsp(new AstChangeDet{m_scopetopp->fileline(), nullptr, nullptr});
|
||||
}
|
||||
void maybeCreateMidChg() {
|
||||
// Don't create an extra function call if splitting is disabled
|
||||
if (!v3Global.opt.outputSplitCFuncs()) {
|
||||
m_chgFuncp = m_tlChgFuncp;
|
||||
@@ -59,29 +81,29 @@ public:
|
||||
}
|
||||
if (!m_chgFuncp || v3Global.opt.outputSplitCFuncs() < m_numStmts) {
|
||||
m_chgFuncp
|
||||
= new AstCFunc(m_scopetopp->fileline(), "_change_request_" + cvtToStr(++m_funcNum),
|
||||
m_scopetopp, "QData");
|
||||
= new AstCFunc{m_scopetopp->fileline(), "_change_request_" + cvtToStr(++m_funcNum),
|
||||
m_scopetopp, "QData"};
|
||||
m_chgFuncp->isStatic(false);
|
||||
m_chgFuncp->isLoose(true);
|
||||
m_chgFuncp->declPrivate(true);
|
||||
m_scopetopp->addActivep(m_chgFuncp);
|
||||
|
||||
// Add a top call to it
|
||||
AstCCall* callp = new AstCCall(m_scopetopp->fileline(), m_chgFuncp);
|
||||
AstCCall* const callp = new AstCCall{m_scopetopp->fileline(), m_chgFuncp};
|
||||
|
||||
if (!m_tlChgFuncp->stmtsp()) {
|
||||
m_tlChgFuncp->addStmtsp(new AstCReturn(m_scopetopp->fileline(), callp));
|
||||
m_tlChgFuncp->addStmtsp(new AstCReturn{m_scopetopp->fileline(), callp});
|
||||
} else {
|
||||
AstCReturn* returnp = VN_CAST(m_tlChgFuncp->stmtsp(), CReturn);
|
||||
AstCReturn* const returnp = VN_AS(m_tlChgFuncp->stmtsp(), CReturn);
|
||||
UASSERT_OBJ(returnp, m_scopetopp, "Lost CReturn in top change function");
|
||||
// This is currently using AstLogOr which will shortcut the
|
||||
// evaluation if any function returns true. This is likely what
|
||||
// we want and is similar to the logic already in use inside
|
||||
// V3EmitC, however, it also means that verbose logging may
|
||||
// miss to print change detect variables.
|
||||
AstNode* newp = new AstCReturn(
|
||||
AstNode* const newp = new AstCReturn{
|
||||
m_scopetopp->fileline(),
|
||||
new AstLogOr(m_scopetopp->fileline(), callp, returnp->lhsp()->unlinkFrBack()));
|
||||
new AstLogOr{m_scopetopp->fileline(), callp, returnp->lhsp()->unlinkFrBack()}};
|
||||
returnp->replaceWith(newp);
|
||||
VL_DO_DANGLING(returnp->deleteTree(), returnp);
|
||||
}
|
||||
@@ -123,14 +145,17 @@ private:
|
||||
}
|
||||
m_statep->maybeCreateChgFuncp();
|
||||
|
||||
AstChangeDet* changep = new AstChangeDet(m_vscp->fileline(), m_varEqnp->cloneTree(true),
|
||||
m_newRvEqnp->cloneTree(true), false);
|
||||
AstChangeDet* const changep = new AstChangeDet{
|
||||
m_vscp->fileline(), m_varEqnp->cloneTree(true), m_newRvEqnp->cloneTree(true)};
|
||||
m_statep->m_chgFuncp->addStmtsp(changep);
|
||||
AstAssign* initp = new AstAssign(m_vscp->fileline(), m_newLvEqnp->cloneTree(true),
|
||||
m_varEqnp->cloneTree(true));
|
||||
AstAssign* const 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();
|
||||
|
||||
// Later code will expand words which adds to GCC compile time,
|
||||
// so add penalty based on word width also
|
||||
const EmitCBaseCounterVisitor visitor{initp};
|
||||
m_statep->m_numStmts += visitor.count() + m_varEqnp->widthWords();
|
||||
}
|
||||
|
||||
virtual void visit(AstBasicDType*) override { //
|
||||
@@ -145,11 +170,11 @@ private:
|
||||
VL_RESTORER(m_newLvEqnp);
|
||||
VL_RESTORER(m_newRvEqnp);
|
||||
{
|
||||
m_varEqnp = new AstArraySel(nodep->fileline(), m_varEqnp->cloneTree(true), index);
|
||||
m_varEqnp = new AstArraySel{nodep->fileline(), m_varEqnp->cloneTree(true), index};
|
||||
m_newLvEqnp
|
||||
= new AstArraySel(nodep->fileline(), m_newLvEqnp->cloneTree(true), index);
|
||||
= new AstArraySel{nodep->fileline(), m_newLvEqnp->cloneTree(true), index};
|
||||
m_newRvEqnp
|
||||
= new AstArraySel(nodep->fileline(), m_newRvEqnp->cloneTree(true), index);
|
||||
= new AstArraySel{nodep->fileline(), m_newRvEqnp->cloneTree(true), index};
|
||||
|
||||
iterate(nodep->subDTypep()->skipRefp());
|
||||
|
||||
@@ -180,26 +205,30 @@ private:
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
ChangedInsertVisitor(AstVarScope* vscp, ChangedState* statep) {
|
||||
// DPI export trigger should never need change detect. See similar assertions in V3Order
|
||||
// (OrderVisitor::nodeMarkCircular), and V3GenClk (GenClkRenameVisitor::genInpClk).
|
||||
UASSERT_OBJ(vscp != v3Global.rootp()->dpiExportTriggerp(), vscp,
|
||||
"DPI export trigger should not need change detect");
|
||||
m_statep = statep;
|
||||
m_vscp = vscp;
|
||||
m_detects = 0;
|
||||
{
|
||||
AstVar* varp = m_vscp->varp();
|
||||
string newvarname
|
||||
= ("__Vchglast__" + m_vscp->scopep()->nameDotless() + "__" + varp->shortName());
|
||||
AstVar* const varp = m_vscp->varp();
|
||||
const string newvarname{"__Vchglast__" + m_vscp->scopep()->nameDotless() + "__"
|
||||
+ varp->shortName()};
|
||||
// Create: VARREF(_last)
|
||||
// ASSIGN(VARREF(_last), VARREF(var))
|
||||
// ...
|
||||
// CHANGEDET(VARREF(_last), VARREF(var))
|
||||
AstVar* newvarp
|
||||
= new AstVar(varp->fileline(), AstVarType::MODULETEMP, newvarname, varp);
|
||||
AstVar* const 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_newvscp = new AstVarScope{m_vscp->fileline(), m_statep->m_scopetopp, newvarp};
|
||||
m_statep->m_scopetopp->addVarp(m_newvscp);
|
||||
|
||||
m_varEqnp = new AstVarRef(m_vscp->fileline(), m_vscp, VAccess::READ);
|
||||
m_newLvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, VAccess::WRITE);
|
||||
m_newRvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, VAccess::READ);
|
||||
m_varEqnp = new AstVarRef{m_vscp->fileline(), m_vscp, VAccess::READ};
|
||||
m_newLvEqnp = new AstVarRef{m_vscp->fileline(), m_newvscp, VAccess::WRITE};
|
||||
m_newRvEqnp = new AstVarRef{m_vscp->fileline(), m_newvscp, VAccess::READ};
|
||||
}
|
||||
iterate(vscp->dtypep()->skipRefp());
|
||||
m_varEqnp->deleteTree();
|
||||
@@ -218,17 +247,17 @@ private:
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstVarScope::user1() -> bool. True indicates processed
|
||||
AstUser1InUse m_inuser1;
|
||||
const AstUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
ChangedState* m_statep; // Shared state across visitors
|
||||
ChangedState* const m_statep; // Shared state across visitors
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void genChangeDet(AstVarScope* vscp) {
|
||||
vscp->v3warn(IMPERFECTSCH, "Imperfect scheduling of variable: " << vscp->prettyNameQ());
|
||||
ChangedInsertVisitor visitor(vscp, m_statep);
|
||||
{ ChangedInsertVisitor{vscp, m_statep}; }
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
@@ -242,24 +271,11 @@ private:
|
||||
UINFO(4, " TS " << nodep << endl);
|
||||
// Clearing
|
||||
AstNode::user1ClearTree();
|
||||
// Create the change detection function
|
||||
AstScope* scopep = nodep->scopep();
|
||||
// Prep for if make change detection function
|
||||
AstScope* const scopep = nodep->scopep();
|
||||
UASSERT_OBJ(scopep, nodep, "No scope found on top level, perhaps you have no statements?");
|
||||
m_statep->m_scopetopp = scopep;
|
||||
|
||||
// Create a wrapper change detection function that calls each change detection function
|
||||
m_statep->m_tlChgFuncp
|
||||
= new AstCFunc(nodep->fileline(), "_change_request", scopep, "QData");
|
||||
m_statep->m_tlChgFuncp->isStatic(false);
|
||||
m_statep->m_tlChgFuncp->isLoose(true);
|
||||
m_statep->m_tlChgFuncp->declPrivate(true);
|
||||
m_statep->m_scopetopp->addActivep(m_statep->m_tlChgFuncp);
|
||||
// Each change detection function needs at least one AstChangeDet
|
||||
// to ensure that V3EmitC outputs the necessary code.
|
||||
m_statep->maybeCreateChgFuncp();
|
||||
m_statep->m_chgFuncp->addStmtsp(
|
||||
new AstChangeDet(nodep->fileline(), nullptr, nullptr, false));
|
||||
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstVarScope* nodep) override {
|
||||
@@ -288,7 +304,7 @@ void V3Changed::changedAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{
|
||||
ChangedState state;
|
||||
ChangedVisitor visitor(nodep, &state);
|
||||
ChangedVisitor{nodep, &state};
|
||||
} // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("changed", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+16
-12
@@ -32,11 +32,11 @@
|
||||
class ClassVisitor final : public AstNVisitor {
|
||||
private:
|
||||
// MEMBERS
|
||||
AstUser1InUse m_inuser1;
|
||||
const AstUser1InUse m_inuser1;
|
||||
string m_prefix; // String prefix to add to name based on hier
|
||||
AstScope* m_classScopep = nullptr; // Package moving scopes into
|
||||
const AstScope* m_classScopep = nullptr; // Package moving scopes into
|
||||
AstScope* m_packageScopep = nullptr; // Class package scope
|
||||
AstNodeFTask* m_ftaskp = nullptr; // Current task
|
||||
const AstNodeFTask* m_ftaskp = nullptr; // Current task
|
||||
std::vector<std::pair<AstNode*, AstScope*>> m_moves;
|
||||
|
||||
// NODE STATE
|
||||
@@ -53,27 +53,30 @@ private:
|
||||
v3Global.rootp()->addModulep(nodep);
|
||||
// Make containing package
|
||||
// Note origName is the same as the class origName so errors look correct
|
||||
AstClassPackage* packagep = new AstClassPackage(nodep->fileline(), nodep->origName());
|
||||
AstClassPackage* const packagep
|
||||
= new AstClassPackage(nodep->fileline(), nodep->origName());
|
||||
packagep->name(nodep->name() + "__Vclpkg");
|
||||
nodep->classOrPackagep(packagep);
|
||||
packagep->classp(nodep);
|
||||
v3Global.rootp()->addModulep(packagep);
|
||||
// Add package to hierarchy
|
||||
AstCell* cellp = new AstCell(packagep->fileline(), packagep->fileline(), packagep->name(),
|
||||
packagep->name(), nullptr, nullptr, nullptr);
|
||||
AstCell* const cellp
|
||||
= new AstCell(packagep->fileline(), packagep->fileline(), packagep->name(),
|
||||
packagep->name(), nullptr, nullptr, nullptr);
|
||||
cellp->modp(packagep);
|
||||
v3Global.rootp()->topModulep()->addStmtp(cellp);
|
||||
// Find class's scope
|
||||
// Alternative would be to move this and related to V3Scope
|
||||
AstScope* classScopep = nullptr;
|
||||
const AstScope* classScopep = nullptr;
|
||||
for (AstNode* itp = nodep->stmtsp(); itp; itp = itp->nextp()) {
|
||||
if ((classScopep = VN_CAST(itp, Scope))) break;
|
||||
}
|
||||
UASSERT_OBJ(classScopep, nodep, "No scope under class");
|
||||
|
||||
// Add scope
|
||||
AstScope* scopep = new AstScope(nodep->fileline(), packagep, classScopep->name(),
|
||||
classScopep->aboveScopep(), classScopep->aboveCellp());
|
||||
AstScope* const scopep
|
||||
= new AstScope(nodep->fileline(), packagep, classScopep->name(),
|
||||
classScopep->aboveScopep(), classScopep->aboveCellp());
|
||||
packagep->addStmtp(scopep);
|
||||
// Iterate
|
||||
VL_RESTORER(m_prefix);
|
||||
@@ -141,8 +144,9 @@ public:
|
||||
if (VN_IS(moved.first, NodeFTask)) {
|
||||
moved.second->addActivep(moved.first->unlinkFrBack());
|
||||
} else if (VN_IS(moved.first, Var)) {
|
||||
AstVarScope* scopep = VN_CAST(moved.first->user1p(), VarScope);
|
||||
moved.second->addVarp(scopep->unlinkFrBack());
|
||||
AstVarScope* const scopep = VN_AS(moved.first->user1p(), VarScope);
|
||||
scopep->unlinkFrBack();
|
||||
moved.second->addVarp(scopep);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,6 +157,6 @@ public:
|
||||
|
||||
void V3Class::classAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ ClassVisitor visitor(nodep); } // Destruct before checking
|
||||
{ ClassVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("class", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+10
-10
@@ -42,15 +42,15 @@ private:
|
||||
// AstNode::user() -> CleanState. For this node, 0==UNKNOWN
|
||||
// AstNode::user2() -> bool. True indicates widthMin has been propagated
|
||||
// AstNodeDType::user3() -> AstNodeDType*. Alternative node with C size
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
AstUser3InUse m_inuser3;
|
||||
const AstUser1InUse m_inuser1;
|
||||
const AstUser2InUse m_inuser2;
|
||||
const AstUser3InUse m_inuser3;
|
||||
|
||||
// TYPES
|
||||
enum CleanState : uint8_t { CS_UNKNOWN, CS_CLEAN, CS_DIRTY };
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp = nullptr;
|
||||
const AstNodeModule* m_modp = nullptr;
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
@@ -67,16 +67,16 @@ private:
|
||||
}
|
||||
void setCppWidth(AstNode* nodep) {
|
||||
nodep->user2(true); // Don't resize it again
|
||||
AstNodeDType* old_dtypep = nodep->dtypep();
|
||||
AstNodeDType* const old_dtypep = nodep->dtypep();
|
||||
const 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 conversion for each, and reuse it
|
||||
if (AstNodeDType* new_dtypep = VN_CAST(old_dtypep->user3p(), NodeDType)) {
|
||||
if (AstNodeDType* const new_dtypep = VN_CAST(old_dtypep->user3p(), NodeDType)) {
|
||||
nodep->dtypep(new_dtypep);
|
||||
} else {
|
||||
nodep->dtypeChgWidth(width, nodep->widthMin());
|
||||
AstNodeDType* new_dtypep2 = nodep->dtypep();
|
||||
AstNodeDType* const new_dtypep2 = nodep->dtypep();
|
||||
UASSERT_OBJ(new_dtypep2 != old_dtypep, nodep,
|
||||
"Dtype didn't change when width changed");
|
||||
old_dtypep->user3p(new_dtypep2); // Remember for next time
|
||||
@@ -126,7 +126,7 @@ private:
|
||||
computeCppWidth(nodep);
|
||||
V3Number mask(nodep, cppWidth(nodep));
|
||||
mask.setMask(nodep->widthMin());
|
||||
AstNode* cleanp
|
||||
AstNode* const cleanp
|
||||
= new AstAnd(nodep->fileline(), new AstConst(nodep->fileline(), mask), nodep);
|
||||
cleanp->dtypeFrom(nodep); // Otherwise the AND normally picks LHS
|
||||
relinkHandle.relink(cleanp);
|
||||
@@ -138,7 +138,7 @@ private:
|
||||
void ensureCleanAndNext(AstNode* nodep) {
|
||||
// Editing list, careful looping!
|
||||
for (AstNode* exprp = nodep; exprp;) {
|
||||
AstNode* nextp = exprp->nextp();
|
||||
AstNode* const nextp = exprp->nextp();
|
||||
ensureClean(exprp);
|
||||
exprp = nextp;
|
||||
}
|
||||
@@ -314,6 +314,6 @@ public:
|
||||
|
||||
void V3Clean::cleanAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CleanVisitor visitor(nodep); } // Destruct before checking
|
||||
{ CleanVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("clean", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+45
-36
@@ -73,11 +73,11 @@ private:
|
||||
// NODE STATE
|
||||
// Cleared each Module:
|
||||
// AstVarScope::user1p() -> AstVarScope*. Temporary signal that was created.
|
||||
AstUser1InUse m_inuser1;
|
||||
const AstUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
AstTopScope* m_topScopep = nullptr; // Current top scope
|
||||
const AstTopScope* m_topScopep = nullptr; // Current top scope
|
||||
AstScope* m_scopep = nullptr; // Current scope
|
||||
AstCFunc* m_evalFuncp = nullptr; // Top eval function we are creating
|
||||
AstCFunc* m_initFuncp = nullptr; // Top initial function we are creating
|
||||
@@ -92,30 +92,30 @@ private:
|
||||
|
||||
AstVarScope* getCreateLastClk(AstVarScope* vscp) {
|
||||
if (vscp->user1p()) return static_cast<AstVarScope*>(vscp->user1p());
|
||||
AstVar* varp = vscp->varp();
|
||||
const AstVar* const varp = vscp->varp();
|
||||
if (!varp->width1()) {
|
||||
varp->v3warn(E_UNSUPPORTED, "Unsupported: Clock edge on non-single bit signal: "
|
||||
<< varp->prettyNameQ());
|
||||
}
|
||||
string newvarname
|
||||
const string newvarname
|
||||
= (string("__Vclklast__") + vscp->scopep()->nameDotless() + "__" + varp->name());
|
||||
AstVar* newvarp = new AstVar(vscp->fileline(), AstVarType::MODULETEMP, newvarname,
|
||||
VFlagLogicPacked(), 1);
|
||||
AstVar* const 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);
|
||||
AstVarScope* const newvscp = new AstVarScope(vscp->fileline(), m_scopep, newvarp);
|
||||
vscp->user1p(newvscp);
|
||||
m_scopep->addVarp(newvscp);
|
||||
// Add init
|
||||
AstNode* fromp = new AstVarRef(newvarp->fileline(), vscp, VAccess::READ);
|
||||
if (v3Global.opt.xInitialEdge()) fromp = new AstNot(fromp->fileline(), fromp);
|
||||
AstNode* newinitp = new AstAssign(
|
||||
AstNode* const newinitp = new AstAssign(
|
||||
vscp->fileline(), new AstVarRef(newvarp->fileline(), newvscp, VAccess::WRITE), fromp);
|
||||
addToInitial(newinitp);
|
||||
// At bottom, assign them
|
||||
AstAssign* finalp = new AstAssign(vscp->fileline(),
|
||||
new AstVarRef(vscp->fileline(), newvscp, VAccess::WRITE),
|
||||
new AstVarRef(vscp->fileline(), vscp, VAccess::READ));
|
||||
AstAssign* const finalp = new AstAssign(
|
||||
vscp->fileline(), new AstVarRef(vscp->fileline(), newvscp, VAccess::WRITE),
|
||||
new AstVarRef(vscp->fileline(), vscp, VAccess::READ));
|
||||
m_evalFuncp->addFinalsp(finalp);
|
||||
//
|
||||
UINFO(4, "New Last: " << newvscp << endl);
|
||||
@@ -136,16 +136,17 @@ private:
|
||||
"Unsupported: Complicated event expression in sensitive activity list");
|
||||
return nullptr;
|
||||
}
|
||||
AstVarScope* clkvscp = nodep->varrefp()->varScopep();
|
||||
UASSERT_OBJ(nodep->varrefp(), nodep, "No clock found on sense item");
|
||||
AstVarScope* const clkvscp = nodep->varrefp()->varScopep();
|
||||
if (nodep->edgeType() == VEdgeType::ET_POSEDGE) {
|
||||
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
|
||||
AstVarScope* const lastVscp = getCreateLastClk(clkvscp);
|
||||
newp = new AstAnd(
|
||||
nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), nodep->varrefp()->varScopep(), VAccess::READ),
|
||||
new AstNot(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), lastVscp, VAccess::READ)));
|
||||
} else if (nodep->edgeType() == VEdgeType::ET_NEGEDGE) {
|
||||
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
|
||||
AstVarScope* const lastVscp = getCreateLastClk(clkvscp);
|
||||
newp = new AstAnd(
|
||||
nodep->fileline(),
|
||||
new AstNot(nodep->fileline(),
|
||||
@@ -153,7 +154,7 @@ private:
|
||||
VAccess::READ)),
|
||||
new AstVarRef(nodep->fileline(), lastVscp, VAccess::READ));
|
||||
} else if (nodep->edgeType() == VEdgeType::ET_BOTHEDGE) {
|
||||
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
|
||||
AstVarScope* const lastVscp = getCreateLastClk(clkvscp);
|
||||
newp = new AstXor(
|
||||
nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), nodep->varrefp()->varScopep(), VAccess::READ),
|
||||
@@ -171,7 +172,7 @@ private:
|
||||
AstNode* createSenseEquation(AstSenItem* nodesp) {
|
||||
// Nodep may be a list of elements; we need to walk it
|
||||
AstNode* senEqnp = nullptr;
|
||||
for (AstSenItem* senp = nodesp; senp; senp = VN_CAST(senp->nextp(), SenItem)) {
|
||||
for (AstSenItem* senp = nodesp; senp; senp = VN_AS(senp->nextp(), SenItem)) {
|
||||
AstNode* const senOnep = createSenItemEquation(senp);
|
||||
if (senEqnp) {
|
||||
// Add new OR to the sensitivity list equation
|
||||
@@ -183,9 +184,9 @@ private:
|
||||
return senEqnp;
|
||||
}
|
||||
AstIf* makeActiveIf(AstSenTree* sensesp) {
|
||||
AstNode* senEqnp = createSenseEquation(sensesp->sensesp());
|
||||
AstNode* const senEqnp = createSenseEquation(sensesp->sensesp());
|
||||
UASSERT_OBJ(senEqnp, sensesp, "No sense equation, shouldn't be in sequent activation.");
|
||||
AstIf* newifp = new AstIf(sensesp->fileline(), senEqnp, nullptr, nullptr);
|
||||
AstIf* const newifp = new AstIf(sensesp->fileline(), senEqnp, nullptr, nullptr);
|
||||
return newifp;
|
||||
}
|
||||
void clearLastSen() {
|
||||
@@ -214,11 +215,11 @@ private:
|
||||
AstCFunc* funcp = nullptr;
|
||||
|
||||
// Unlink all statements, then add item by item to new sub-functions
|
||||
AstBegin* tempp = new AstBegin{ofuncp->fileline(), "[EditWrapper]",
|
||||
ofuncp->stmtsp()->unlinkFrBackWithNext()};
|
||||
AstBegin* const tempp = new AstBegin{ofuncp->fileline(), "[EditWrapper]",
|
||||
ofuncp->stmtsp()->unlinkFrBackWithNext()};
|
||||
if (ofuncp->finalsp()) tempp->addStmtsp(ofuncp->finalsp()->unlinkFrBackWithNext());
|
||||
while (tempp->stmtsp()) {
|
||||
AstNode* itemp = tempp->stmtsp()->unlinkFrBack();
|
||||
AstNode* const itemp = tempp->stmtsp()->unlinkFrBack();
|
||||
const int stmts = EmitCBaseCounterVisitor(itemp).count();
|
||||
if (!funcp || (func_stmts + stmts) > v3Global.opt.outputSplitCFuncs()) {
|
||||
// Make a new function
|
||||
@@ -230,7 +231,7 @@ private:
|
||||
funcp->slow(ofuncp->slow());
|
||||
m_topScopep->scopep()->addActivep(funcp);
|
||||
//
|
||||
AstCCall* callp = new AstCCall{funcp->fileline(), funcp};
|
||||
AstCCall* const callp = new AstCCall{funcp->fileline(), funcp};
|
||||
ofuncp->addStmtsp(callp);
|
||||
func_stmts = 0;
|
||||
}
|
||||
@@ -257,6 +258,14 @@ private:
|
||||
// Process the activates
|
||||
iterateChildren(nodep);
|
||||
UINFO(4, " TOPSCOPE iter done " << nodep << endl);
|
||||
// Clear the DPI export trigger flag at the end of eval
|
||||
if (AstVarScope* const dpiExportTriggerp = v3Global.rootp()->dpiExportTriggerp()) {
|
||||
FileLine* const fl = dpiExportTriggerp->fileline();
|
||||
AstAssign* const assignp
|
||||
= new AstAssign{fl, new AstVarRef{fl, dpiExportTriggerp, VAccess::WRITE},
|
||||
new AstConst{fl, AstConst::BitFalse{}}};
|
||||
m_evalFuncp->addFinalsp(assignp);
|
||||
}
|
||||
// Split large functions
|
||||
splitCheck(m_evalFuncp);
|
||||
splitCheck(m_initFuncp);
|
||||
@@ -280,7 +289,7 @@ private:
|
||||
// UINFO(4, " SCOPE " << nodep << endl);
|
||||
m_scopep = nodep;
|
||||
iterateChildren(nodep);
|
||||
if (AstNode* movep = nodep->finalClksp()) {
|
||||
if (AstNode* const movep = nodep->finalClksp()) {
|
||||
UASSERT_OBJ(m_topScopep, nodep, "Final clocks under non-top scope");
|
||||
movep->unlinkFrBackWithNext();
|
||||
m_evalFuncp->addFinalsp(movep);
|
||||
@@ -288,7 +297,7 @@ private:
|
||||
m_scopep = nullptr;
|
||||
}
|
||||
virtual void visit(AstNodeProcedure* nodep) override {
|
||||
if (AstNode* stmtsp = nodep->bodysp()) {
|
||||
if (AstNode* const stmtsp = nodep->bodysp()) {
|
||||
stmtsp->unlinkFrBackWithNext();
|
||||
nodep->addNextHere(stmtsp);
|
||||
}
|
||||
@@ -298,12 +307,12 @@ private:
|
||||
// nodep->dumpTree(cout, "ct:");
|
||||
// COVERTOGGLE(INC, ORIG, CHANGE) ->
|
||||
// IF(ORIG ^ CHANGE) { INC; CHANGE = ORIG; }
|
||||
AstNode* incp = nodep->incp()->unlinkFrBack();
|
||||
AstNode* origp = nodep->origp()->unlinkFrBack();
|
||||
AstNode* changeWrp = nodep->changep()->unlinkFrBack();
|
||||
AstNode* changeRdp = ConvertWriteRefsToRead::main(changeWrp->cloneTree(false));
|
||||
AstIf* newp = new AstIf(nodep->fileline(), new AstXor(nodep->fileline(), origp, changeRdp),
|
||||
incp, nullptr);
|
||||
AstNode* const incp = nodep->incp()->unlinkFrBack();
|
||||
AstNode* const origp = nodep->origp()->unlinkFrBack();
|
||||
AstNode* const changeWrp = nodep->changep()->unlinkFrBack();
|
||||
AstNode* const changeRdp = ConvertWriteRefsToRead::main(changeWrp->cloneTree(false));
|
||||
AstIf* const newp = new AstIf(
|
||||
nodep->fileline(), new AstXor(nodep->fileline(), origp, changeRdp), incp, nullptr);
|
||||
// We could add another IF to detect posedges, and only increment if so.
|
||||
// It's another whole branch though versus a potential memory miss.
|
||||
// We'll go with the miss.
|
||||
@@ -316,7 +325,7 @@ private:
|
||||
// Link to global function
|
||||
if (nodep->formCallTree()) {
|
||||
UINFO(4, " formCallTree " << nodep << endl);
|
||||
AstCCall* callp = new AstCCall(nodep->fileline(), nodep);
|
||||
AstCCall* const callp = new AstCCall(nodep->fileline(), nodep);
|
||||
m_finalFuncp->addStmtsp(callp);
|
||||
}
|
||||
}
|
||||
@@ -344,7 +353,7 @@ private:
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
} else if (m_mtaskBodyp) {
|
||||
UINFO(4, " TR ACTIVE " << nodep << endl);
|
||||
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
|
||||
AstNode* const stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
|
||||
if (nodep->hasClocked()) {
|
||||
UASSERT_OBJ(!nodep->hasInitial(), nodep,
|
||||
"Initial block should not have clock sensitivity");
|
||||
@@ -369,7 +378,7 @@ private:
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
} else {
|
||||
UINFO(4, " ACTIVE " << nodep << endl);
|
||||
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
|
||||
AstNode* const stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
|
||||
if (nodep->hasClocked()) {
|
||||
// Remember the latest sensitivity so we can compare it next time
|
||||
UASSERT_OBJ(!nodep->hasInitial(), nodep,
|
||||
@@ -403,8 +412,8 @@ private:
|
||||
}
|
||||
}
|
||||
virtual void visit(AstExecGraph* nodep) override {
|
||||
for (m_mtaskBodyp = VN_CAST(nodep->op1p(), MTaskBody); m_mtaskBodyp;
|
||||
m_mtaskBodyp = VN_CAST(m_mtaskBodyp->nextp(), MTaskBody)) {
|
||||
for (m_mtaskBodyp = VN_AS(nodep->op1p(), MTaskBody); m_mtaskBodyp;
|
||||
m_mtaskBodyp = VN_AS(m_mtaskBodyp->nextp(), MTaskBody)) {
|
||||
clearLastSen();
|
||||
iterate(m_mtaskBodyp);
|
||||
}
|
||||
@@ -435,6 +444,6 @@ public:
|
||||
|
||||
void V3Clock::clockAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ ClockVisitor visitor(nodep); } // Destruct before checking
|
||||
{ ClockVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("clock", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+5
-5
@@ -121,7 +121,7 @@ class CombineVisitor final : CombBaseVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
AstUser3InUse m_user3InUse; // Marks replaced AstCFuncs
|
||||
const AstUser3InUse m_user3InUse; // Marks replaced AstCFuncs
|
||||
// AstUser4InUse part of V3Hasher in V3DupFinder
|
||||
|
||||
// STATE
|
||||
@@ -132,7 +132,7 @@ private:
|
||||
// METHODS
|
||||
void walkEmptyFuncs() {
|
||||
for (const auto& itr : m_dupFinder) {
|
||||
AstCFunc* const oldfuncp = VN_CAST(itr.second, CFunc);
|
||||
AstCFunc* const oldfuncp = VN_AS(itr.second, CFunc);
|
||||
UASSERT_OBJ(oldfuncp, itr.second, "Not a CFunc in hash");
|
||||
if (!oldfuncp->emptyBody()) continue;
|
||||
UASSERT_OBJ(!oldfuncp->dontCombine(), oldfuncp,
|
||||
@@ -152,14 +152,14 @@ private:
|
||||
// Do non-slow first as then favors naming functions based on fast name
|
||||
for (const bool slow : {false, true}) {
|
||||
for (auto newIt = m_dupFinder.begin(); newIt != m_dupFinder.end(); ++newIt) {
|
||||
AstCFunc* const newfuncp = VN_CAST(newIt->second, CFunc);
|
||||
AstCFunc* const newfuncp = VN_AS(newIt->second, CFunc);
|
||||
UASSERT_OBJ(newfuncp, newIt->second, "Not a CFunc in hash");
|
||||
if (newfuncp->user3()) continue; // Already replaced
|
||||
if (newfuncp->slow() != slow) continue;
|
||||
auto oldIt = newIt;
|
||||
++oldIt; // Skip over current position
|
||||
for (; oldIt != m_dupFinder.end(); ++oldIt) {
|
||||
AstCFunc* const oldfuncp = VN_CAST(oldIt->second, CFunc);
|
||||
AstCFunc* const oldfuncp = VN_AS(oldIt->second, CFunc);
|
||||
UASSERT_OBJ(oldfuncp, oldIt->second, "Not a CFunc in hash");
|
||||
UASSERT_OBJ(newfuncp != oldfuncp, newfuncp,
|
||||
"Same function hashed multiple times");
|
||||
@@ -223,6 +223,6 @@ public:
|
||||
|
||||
void V3Combine::combineAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CombineVisitor visitor(nodep); } // Destruct before checking
|
||||
{ CombineVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("combine", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Add common contents to modules
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you
|
||||
// can redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
// V3Common's Transformations:
|
||||
//
|
||||
// Each class:
|
||||
// Create string access functions
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Common.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3EmitCBase.h"
|
||||
|
||||
//######################################################################
|
||||
// Common component builders
|
||||
|
||||
static void makeVlToString(AstClass* nodep) {
|
||||
AstCFunc* const funcp
|
||||
= new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"};
|
||||
funcp->argTypes("const VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(nodep) + ">& obj");
|
||||
funcp->isMethod(false);
|
||||
funcp->isConst(false);
|
||||
funcp->isStatic(false);
|
||||
funcp->protect(false);
|
||||
AstNode* const exprp = new AstCMath{nodep->fileline(), "obj ? obj->to_string() : \"null\"", 0};
|
||||
exprp->dtypeSetString();
|
||||
funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp});
|
||||
nodep->addStmtp(funcp);
|
||||
}
|
||||
static void makeToString(AstClass* nodep) {
|
||||
AstCFunc* const funcp = new AstCFunc{nodep->fileline(), "to_string", nullptr, "std::string"};
|
||||
funcp->isConst(true);
|
||||
funcp->isStatic(false);
|
||||
funcp->protect(false);
|
||||
AstNode* const exprp
|
||||
= new AstCMath{nodep->fileline(), R"(std::string("'{") + to_string_middle() + "}")", 0};
|
||||
exprp->dtypeSetString();
|
||||
funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp});
|
||||
nodep->addStmtp(funcp);
|
||||
}
|
||||
static void makeToStringMiddle(AstClass* nodep) {
|
||||
AstCFunc* const funcp
|
||||
= new AstCFunc{nodep->fileline(), "to_string_middle", nullptr, "std::string"};
|
||||
funcp->isConst(true);
|
||||
funcp->isStatic(false);
|
||||
funcp->protect(false);
|
||||
funcp->addStmtsp(new AstCStmt{nodep->fileline(), "std::string out;\n"});
|
||||
std::string comma;
|
||||
for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) {
|
||||
if (const auto* const varp = VN_CAST(itemp, Var)) {
|
||||
if (!varp->isParam()) {
|
||||
string stmt = "out += \"";
|
||||
stmt += comma;
|
||||
comma = ", ";
|
||||
stmt += itemp->origNameProtect();
|
||||
stmt += ":\" + ";
|
||||
if (itemp->isWide()) {
|
||||
stmt += "VL_TO_STRING_W(";
|
||||
stmt += cvtToStr(itemp->widthWords());
|
||||
stmt += ", ";
|
||||
} else {
|
||||
stmt += "VL_TO_STRING(";
|
||||
}
|
||||
stmt += itemp->nameProtect();
|
||||
stmt += ");\n";
|
||||
nodep->user1(true); // So what we extend dumps this
|
||||
funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nodep->extendsp() && nodep->extendsp()->classp()->user1()) {
|
||||
string stmt = "out += \"";
|
||||
if (!comma.empty()) stmt += "\", \"+ ";
|
||||
// comma = ", "; // Nothing further so not needed
|
||||
stmt += nodep->extendsp()->dtypep()->nameProtect();
|
||||
stmt += "::to_string_middle();\n";
|
||||
nodep->user1(true); // So what we extend dumps this
|
||||
funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt});
|
||||
}
|
||||
funcp->addStmtsp(new AstCStmt{nodep->fileline(), "return out;\n"});
|
||||
nodep->addStmtp(funcp);
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// V3Common class functions
|
||||
|
||||
void V3Common::commonAll() {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
// Create common contents for each module
|
||||
for (AstNode* nodep = v3Global.rootp()->modulesp(); nodep; nodep = nodep->nextp()) {
|
||||
if (AstClass* const classp = VN_CAST(nodep, Class)) {
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstClass::user1() -> bool. True if class needs to_string dumper
|
||||
const AstUser1InUse m_inuser1;
|
||||
// Create ToString methods
|
||||
makeVlToString(classp);
|
||||
makeToString(classp);
|
||||
makeToStringMiddle(classp);
|
||||
}
|
||||
}
|
||||
V3Global::dumpCheckGlobalTree("common", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Add common contents to modules
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you
|
||||
// can redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_V3COMMON_H_
|
||||
#define VERILATOR_V3COMMON_H_
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3Common final {
|
||||
public:
|
||||
static void commonAll();
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user