mirror of
https://github.com/verilator/verilator.git
synced 2026-10-04 17:13:40 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
746c7ea8f7 | ||
|
|
5ed882faf2 | ||
|
|
fa4b10b4d9 | ||
|
|
f25a9a4c80 | ||
|
|
a999c73ce0 | ||
|
|
75a70bee6d | ||
|
|
1b1907af49 | ||
|
|
1b18eee5dc | ||
|
|
4931e48016 | ||
|
|
f96454adcc | ||
|
|
9a20a258f5 | ||
|
|
10796457d2 | ||
|
|
78e659a142 | ||
|
|
2f50642ecb | ||
|
|
050060b139 | ||
|
|
63c694f65f | ||
|
|
12093e6939 | ||
|
|
9949a6cd17 | ||
|
|
ce03293128 | ||
|
|
0a8cfb8d2c | ||
|
|
0b07679ff2 | ||
|
|
5df14627fd | ||
|
|
fc3ce29845 | ||
|
|
bba800f2d6 | ||
|
|
fe2a1e1749 | ||
|
|
fc4ffd454e | ||
|
|
8dae4ad93a | ||
|
|
8da261f302 | ||
|
|
7d88e63bab | ||
|
|
79be097e34 | ||
|
|
11b0d36ba2 | ||
|
|
80b73859a2 | ||
|
|
a548a77f26 | ||
|
|
d6bfb37e6a | ||
|
|
38a8d7fb2e | ||
|
|
0c70a0dcbf | ||
|
|
d16619fe86 | ||
|
|
2dc85a5acd | ||
|
|
dbe1348b4c | ||
|
|
d74536a4dc | ||
|
|
da20da264b | ||
|
|
22b9dfb9c9 | ||
|
|
e43c089ab8 | ||
|
|
d85b909054 | ||
|
|
75fd71d7e5 | ||
|
|
9efd64ab98 | ||
|
|
7aa01625d8 | ||
|
|
a3c58d7b70 | ||
|
|
ae466b1703 | ||
|
|
2564484429 | ||
|
|
93a044f587 | ||
|
|
81fe35ee2e | ||
|
|
08b6bdddf9 | ||
|
|
4d49db48a3 | ||
|
|
9b2266f68c | ||
|
|
752f425025 | ||
|
|
47262cd4ec | ||
|
|
47e64535d6 | ||
|
|
45d622e4bc | ||
|
|
249feaae7c | ||
|
|
9a27004ae5 | ||
|
|
42283150d7 | ||
|
|
5a1bcf9794 | ||
|
|
361cef4633 | ||
|
|
e94cdcf29c | ||
|
|
1af046986d | ||
|
|
1c9263a25b | ||
|
|
6b6790fc50 | ||
|
|
fb931087ab | ||
|
|
937e893b6d | ||
|
|
d42a2d6494 | ||
|
|
8e8f4b1e5c | ||
|
|
2ba39b25f1 | ||
|
|
5c828b7e60 | ||
|
|
8d0c06e570 | ||
|
|
5b2fbf4f37 | ||
|
|
592dab2bdb | ||
|
|
51daa64e9a | ||
|
|
2136afde6b | ||
|
|
ea55db7286 | ||
|
|
6a5f77b278 | ||
|
|
8658a0d7dc | ||
|
|
c335aad25f | ||
|
|
9d9d647c1f | ||
|
|
d47a37fb76 | ||
|
|
0a3a15a66e | ||
|
|
2358ced061 | ||
|
|
5869fdf7f6 | ||
|
|
1a1d2ecfd9 | ||
|
|
9ac64d0b92 | ||
|
|
c6607724cb | ||
|
|
90dc04cf93 | ||
|
|
4d81eb021d | ||
|
|
83475008d9 | ||
|
|
03ac7ad730 | ||
|
|
cd50949a7e | ||
|
|
f0040c7b9a | ||
|
|
f8a0389e73 | ||
|
|
b436794773 | ||
|
|
90d22cbec6 | ||
|
|
33e2acfe61 | ||
|
|
db5fdfb0ee | ||
|
|
951cd73fe0 | ||
|
|
0eeb40b975 | ||
|
|
93272c13fd | ||
|
|
43abaeb055 | ||
|
|
18b9e661c9 | ||
|
|
f435d96241 | ||
|
|
d32e3f042f | ||
|
|
df5f95a5bd | ||
|
|
b0c475205b | ||
|
|
1e2219347e | ||
|
|
cbe1b8e266 | ||
|
|
2b12fe5773 | ||
|
|
d20f22beb1 | ||
|
|
f4fe10844b | ||
|
|
122e89ffde | ||
|
|
96a4b3e5a5 | ||
|
|
fac8e76923 | ||
|
|
b864f5f5ba | ||
|
|
f9f66d787e | ||
|
|
bd211c87aa | ||
|
|
6c33e6e889 | ||
|
|
cfb6fd8b34 | ||
|
|
cb60663d49 | ||
|
|
0d2bf23d82 | ||
|
|
682a60e325 | ||
|
|
2ab6272cc7 | ||
|
|
152a6cd886 | ||
|
|
f91793e931 | ||
|
|
a2d26b45bb | ||
|
|
dce8f3d25d | ||
|
|
38e5b6c1ad | ||
|
|
b9d7819faa | ||
|
|
1f9323d086 | ||
|
|
574dbfded1 | ||
|
|
2a87387eb3 | ||
|
|
a5ddd10e31 | ||
|
|
e871cd8a44 | ||
|
|
7b431b37c7 | ||
|
|
eeef5ab4de | ||
|
|
ac4ec87942 | ||
|
|
290c2e0388 | ||
|
|
89924bda51 | ||
|
|
60eab3eb8c | ||
|
|
e0b61ceabd | ||
|
|
30e3edb81d | ||
|
|
542e324869 | ||
|
|
1c5e5704f5 | ||
|
|
1d400dd98c | ||
|
|
a4ed3c2086 | ||
|
|
efb5caad22 | ||
|
|
3a002b6cf2 | ||
|
|
f8b7981be4 | ||
|
|
b55ee79d86 | ||
|
|
af70db88db | ||
|
|
7ef033f876 | ||
|
|
db59c07f27 | ||
|
|
9085e34d70 | ||
|
|
0dfa7d3af5 | ||
|
|
b0e796ca83 | ||
|
|
3bd830eacf | ||
|
|
f4efcbde5c | ||
|
|
658819bb71 | ||
|
|
3fc8249429 | ||
|
|
e0a38ce2c2 | ||
|
|
178e1789b5 | ||
|
|
108c900387 | ||
|
|
63507e8e29 | ||
|
|
87f1e06c41 | ||
|
|
7e8bafd217 | ||
|
|
457ad07ade | ||
|
|
79c901c220 | ||
|
|
b61d819fcb | ||
|
|
f4038e3674 | ||
|
|
8377514127 | ||
|
|
d8ea989eda | ||
|
|
5f3316d3dc | ||
|
|
a4fddb3fbe | ||
|
|
3d71716a8a | ||
|
|
9f37cef1bb | ||
|
|
0de1bbc85b | ||
|
|
3aa8624658 | ||
|
|
42b711b862 | ||
|
|
2873dbe154 | ||
|
|
31a83cb0d8 | ||
|
|
383e384739 | ||
|
|
1bb6433649 | ||
|
|
ced39d0982 | ||
|
|
fc4d6a62af | ||
|
|
fa99cbbc73 | ||
|
|
49455721a3 | ||
|
|
11032b1936 | ||
|
|
f2fba51fe2 |
+21
-10
@@ -17,7 +17,7 @@ AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: false
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
@@ -59,15 +59,27 @@ ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IncludeBlocks: Preserve
|
||||
|
||||
# Include grouping/sorting
|
||||
SortIncludes: true
|
||||
IncludeBlocks: Regroup
|
||||
IncludeCategories:
|
||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||
Priority: 2
|
||||
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||
Priority: 3
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
- Regex: '"(config_build|verilated_config|verilatedos)\.h"'
|
||||
Priority: -1 # Sepecials before main header
|
||||
- Regex: '(<|")verilated.*'
|
||||
Priority: 1 # Runtime headers
|
||||
- Regex: '"V3.*__gen.*\.h"'
|
||||
Priority: 3 # Generated internal headers separately
|
||||
- Regex: '"V3.*"'
|
||||
Priority: 2 # Internal header
|
||||
- Regex: '".*"'
|
||||
Priority: 4 # Other non-system headers
|
||||
- Regex: '<[[:alnum:]_.]+>'
|
||||
Priority: 5 # Simple system headers next
|
||||
- Regex: '<.*>'
|
||||
Priority: 6 # Other system headers next
|
||||
IncludeIsMainRegex: '$'
|
||||
|
||||
IndentCaseLabels: false
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 4
|
||||
@@ -91,7 +103,6 @@ PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PointerAlignment: Left
|
||||
ReflowComments: true
|
||||
SortIncludes: false
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
|
||||
@@ -11,7 +11,7 @@ on:
|
||||
jobs:
|
||||
Test:
|
||||
name: "'docs/CONTRIBUTORS' was signed"
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: test_regress/t/t_dist_contributors.pl
|
||||
|
||||
@@ -10,11 +10,11 @@ on:
|
||||
|
||||
jobs:
|
||||
format:
|
||||
runs-on: ubuntu-20.04
|
||||
name: Ubuntu 20.04 | format
|
||||
runs-on: ubuntu-22.04
|
||||
name: Ubuntu 22.04 | format
|
||||
env:
|
||||
CI_OS_NAME: linux
|
||||
CI_RUNS_ON: ubuntu-20.04
|
||||
CI_RUNS_ON: ubuntu-22.04
|
||||
CI_COMMIT: ${{ github.sha }}
|
||||
CI_M32: 0
|
||||
steps:
|
||||
@@ -27,14 +27,14 @@ jobs:
|
||||
CI_BUILD_STAGE_NAME: build
|
||||
run: |
|
||||
bash ci/ci-install.bash &&
|
||||
sudo apt-get install clang-format-11 yapf3 &&
|
||||
sudo apt-get install clang-format-14 yapf3 &&
|
||||
git config --global user.email "[email protected]" &&
|
||||
git config --global user.name "github action"
|
||||
- name: Format code
|
||||
run: |
|
||||
autoconf &&
|
||||
./configure &&
|
||||
make -j 2 format CLANGFORMAT=clang-format-11 &&
|
||||
make -j 2 format CLANGFORMAT=clang-format-14 &&
|
||||
git status
|
||||
- name: Push
|
||||
run: |
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
\#*
|
||||
.#*
|
||||
.gdb_history
|
||||
.nfs*
|
||||
*~
|
||||
*.tidy
|
||||
|
||||
@@ -8,6 +8,65 @@ The changes in each Verilator version are described below. The
|
||||
contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
|
||||
Verilator 4.228 2022-10-01
|
||||
==========================
|
||||
|
||||
**Announcement:**
|
||||
|
||||
* The next release is anticipated primere Verilator Version 5. Please
|
||||
consider beta-testing the github 'develop-v5' branch, which will soon
|
||||
merge into the github 'master' branch (#3383).
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Support some IEEE signal strengths (#3601) (#3629). [Ryszard Rozak/Antmicro]
|
||||
* Add --main to generate main() C++ (previously was experimental only).
|
||||
* Add --build-jobs, and rework arguments for -j (#3623). [Kamil Rakoczy]
|
||||
* Rename --bin to --build-dep-bin.
|
||||
* Rename debug flags --dumpi-tree, --dumpi-graph, etc. [Geza Lore]
|
||||
* Fix thread saftey in SystemC VL_ASSIGN_SBW/WSB (#3494) (#3513). [Mladen Slijepcevic]
|
||||
* Fix crash in gate optimization of circular logic (#3543). [Bill Flynn]
|
||||
* Fix arguments in non-static method call (#3547) (#3582). [Gustav Svensk]
|
||||
* Fix default --mod-prefix when --prefix is repeated (#3603). [Geza Lore]
|
||||
* Fix calling trace() after open() segfault (#3610) (#3627). [Yu-Sheng Lin]
|
||||
* Fix typedef'ed class conversion to boolean (#3616). [Aleksander Kiryk]
|
||||
* Fix Verilation speed when disabled warnings (#3632). [Kamil Rakoczy/Antmicro]
|
||||
|
||||
|
||||
Verilator 4.226 2022-08-31
|
||||
==========================
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Add --future0 and --future1 options.
|
||||
* Support class parameters (#2231) (#3541). [Arkadiusz Kozdra/Antmicro]
|
||||
* Support wildcard index associative arrays (#3501). [Arkadiusz Kozdra/Antmicro]
|
||||
* Support negated properties (#3572). [Aleksander Kiryk]
|
||||
* Support $test$plusargs(expr) (#3489).
|
||||
* Rename trace rolloverSize() (#3570).
|
||||
* Improve Verilation speed with --threads on large designs. [Geza Lore]
|
||||
* Improve Verilation memory by reducing V3Number (#3521). [Mariusz Glebocki/Antmicro]
|
||||
* Fix struct pattern assignment (#2328) (#3517). [Mostafa Gamal]
|
||||
* Fix public combo propagation issues (#2905). [Todd Strader]
|
||||
* Fix incorrect tristate logic (#3399) [shareefj, Vighnesh Iyer]
|
||||
* Fix incorrect bit op tree optimization (#3470). [algrobman]
|
||||
* Fix bisonpre for MSYS2 (#3471).
|
||||
* Fix max memory usage (#3483). [Kamil Rakoczy/Antmicro]
|
||||
* Fix empty string arguments to display (#3484). [Grulfen]
|
||||
* Fix table misoptimizing away display (#3488). [Stefan Post]
|
||||
* Fix unique_ptr memory header for MinGW64 (#3493).
|
||||
* Fix $dump systemtask with --output-split-cfuncs (#3495) (#3497). [Varun Koyyalagunta]
|
||||
* Fix wrong bit op tree optimization (#3509). [Nathan Graybeal]
|
||||
* Fix nested default assignment for struct pattern (#3511) (#3524). [Mostafa Gamal]
|
||||
* Fix sformat string incorrectly cleared (#3515) (#3519). [Gustav Svensk]
|
||||
* Fix segfault exporting non-existant package (#3535).
|
||||
* Fix void-cast queue pop_front or pop_back (#3542) (#3364). [Drew Ranck]
|
||||
* Fix case statement comparing string literal (#3544). [Gustav Svensk]
|
||||
* Fix === with some tristate constants (#3551). [Ryszard Rozak/Antmicro]
|
||||
* Fix converting subclasses to string (#3552). [Arkadiusz Kozdra/Antmicro]
|
||||
* Fix --hierarchical with order-based pin connections (#3583) (#3585). [Kelin9298]
|
||||
|
||||
|
||||
Verilator 4.224 2022-06-19
|
||||
==========================
|
||||
|
||||
@@ -26,7 +85,7 @@ Verilator 4.224 2022-06-19
|
||||
* Improve conditional merging optimization (#3125). [Geza Lore, Shunyao CAD]
|
||||
* Define VM_TRACE_VCD when tracing in VCD format. [Geza Lore, Shunyao CAD]
|
||||
* Add assert when VerilatedContext is mis-deleted (#3121). [Rupert Swarbrick]
|
||||
* Internal prep work towards timing control. [Krzysztof Bieganski]
|
||||
* Internal prep work towards timing control. [Krzysztof Bieganski/Antmicro]
|
||||
* Fix hang with large case statement optimization (#3405). [Mike Urbach]
|
||||
* Fix UNOPTFLAT warning from initial static var (#3406). [Kamil Rakoczy]
|
||||
* Fix compile error when enable VL_LEAK_CHECKS (#3411). [HungMingWu]
|
||||
@@ -193,7 +252,7 @@ Verilator 4.212 2021-09-01
|
||||
* 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]
|
||||
* Support assignment patterns as children of pins (#3041). [Krzysztof Bieganski/Antmicro]
|
||||
* 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
|
||||
@@ -258,7 +317,7 @@ Verilator 4.204 2021-06-12
|
||||
* Prep work towards better ccache hashing/performance. [Geza Lore]
|
||||
* Fix assertion failure in bitOpTree optimization (#2891) (#2899). [Raynard Qiao]
|
||||
* Fix DPI functions not seen as vpiModule (#2893). [Todd Strader]
|
||||
* Fix bounds check in VL_SEL_IWII (#2910). [Krzysztof Bieganski]
|
||||
* Fix bounds check in VL_SEL_IWII (#2910). [Krzysztof Bieganski/Antmicro]
|
||||
* Fix slowdown in elaboration (#2911). [Nathan Graybeal]
|
||||
* Fix initialization of assoc in assoc array (#2914). [myftptoyman]
|
||||
* Fix make support for gmake 3.x (#2920) (#2921). [Philipp Wagner]
|
||||
@@ -373,7 +432,7 @@ Verilator 4.108 2021-01-10
|
||||
**Major:**
|
||||
|
||||
* Many VPI changes for IEEE compatibility, which may alter behavior from previous releases.
|
||||
* Support randomize() class method and rand (#2607). [Krzysztof Bieganski]
|
||||
* Support randomize() class method and rand (#2607). [Krzysztof Bieganski/Antmicro]
|
||||
|
||||
**Minor:**
|
||||
|
||||
@@ -433,7 +492,7 @@ Verilator 4.104 2020-11-14
|
||||
* Support queue and associative array 'with' statements (#2616).
|
||||
* Support queue slicing (#2326).
|
||||
* Support associative array pattern assignments and defaults.
|
||||
* Support static methods and typedefs in classes (#2615). [Krzysztof Bieganski]
|
||||
* Support static methods and typedefs in classes (#2615). [Krzysztof Bieganski/Antmicro]
|
||||
* Add error on typedef referencing self (#2539). [Cody Piersall]
|
||||
* With --debug, turn off address space layout randomization.
|
||||
* Fix iteration over mutating list bug in VPI (#2588). [Kaleb Barrett]
|
||||
|
||||
+10
-7
@@ -322,7 +322,10 @@ cppcheck: $(CPPCHECK_DEP)
|
||||
$(CPPCHECK) $(CPPCHECK_FLAGS) -DVL_DEBUG=1 -DVL_CPPCHECK=1 -DVL_THREADED=1 $(CPPCHECK_INC) $<
|
||||
|
||||
CLANGTIDY = clang-tidy
|
||||
CLANGTIDY_FLAGS = -config='' -checks='-fuchsia-*,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-init-variables'
|
||||
CLANGTIDY_FLAGS = -config='' \
|
||||
-header-filter='.*' \
|
||||
-checks='-fuchsia-*,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-init-variables,-cppcoreguidelines-avoid-goto,-modernize-avoid-c-arrays,-readability-magic-numbers,-readability-simplify-boolean-expr,-cppcoreguidelines-macro-usage' \
|
||||
|
||||
CLANGTIDY_DEP = $(subst .h,.h.tidy,$(CPPCHECK_H)) \
|
||||
$(subst .cpp,.cpp.tidy,$(CPPCHECK_CPP))
|
||||
CLANGTIDY_DEFS = -DVL_DEBUG=1 -DVL_THREADED=1 -DVL_CPPCHECK=1
|
||||
@@ -331,7 +334,7 @@ clang-tidy: $(CLANGTIDY_DEP)
|
||||
%.cpp.tidy: %.cpp
|
||||
$(CLANGTIDY) $(CLANGTIDY_FLAGS) $< -- $(CLANGTIDY_DEFS) $(CPPCHECK_INC) | 2>&1 tee $@
|
||||
%.h.tidy: %.h
|
||||
$(CLANGTIDY) $(CLANGTIDY_FLAGS) $< -- $(CLANGTIDY_DEFS) $(CPPCHECK_INC) | 2>&1 tee $@
|
||||
$(CLANGTIDY) $(CLANGTIDY_FLAGS) $< -- $(CLANGTIDY_DEFS) $(CPPCHECK_INC) -x c++-header | 2>&1 tee $@
|
||||
|
||||
analyzer-src:
|
||||
-rm -rf src/obj_dbg
|
||||
@@ -343,13 +346,13 @@ analyzer-include:
|
||||
|
||||
format: clang-format yapf format-pl-exec
|
||||
|
||||
CLANGFORMAT = clang-format-11
|
||||
CLANGFORMAT = clang-format-14
|
||||
CLANGFORMAT_FLAGS = -i
|
||||
CLANGFORMAT_FILES = $(CPPCHECK_CPP) $(CPPCHECK_H) $(CPPCHECK_YL) test_regress/t/*.c* test_regress/t/*.h
|
||||
|
||||
clang-format:
|
||||
@$(CLANGFORMAT) --version | egrep 11.0 > /dev/null \
|
||||
|| echo "*** You are not using clang-format 11.0, indents may differ from master's ***"
|
||||
@$(CLANGFORMAT) --version | egrep 14.0 > /dev/null \
|
||||
|| echo "*** You are not using clang-format-14, indents may differ from master's ***"
|
||||
$(CLANGFORMAT) $(CLANGFORMAT_FLAGS) $(CLANGFORMAT_FILES)
|
||||
|
||||
PY_PROGRAMS = \
|
||||
@@ -388,10 +391,10 @@ yapf:
|
||||
FLAKE8 = flake8
|
||||
FLAKE8_FLAGS = \
|
||||
--extend-exclude=fastcov.py \
|
||||
--ignore=E123,E129,E251,E501,W503,W504,E701
|
||||
--ignore=E123,E129,E251,E402,E501,W503,W504,E701
|
||||
|
||||
PYLINT = pylint
|
||||
PYLINT_FLAGS = --disable=R0801
|
||||
PYLINT_FLAGS = --score=n --disable=R0801
|
||||
|
||||
lint-py:
|
||||
-$(FLAKE8) $(FLAKE8_FLAGS) $(PY_PROGRAMS)
|
||||
|
||||
+17
-16
@@ -28,7 +28,7 @@ Welcome to Verilator
|
||||
- |Logo|
|
||||
* - |verilator multithreaded performance|
|
||||
- **Fast**
|
||||
* Outperforms many commercial simulators
|
||||
* Outperforms many closed-source commercial simulators
|
||||
* Single- and multi-threaded output models
|
||||
* - **Widely Used**
|
||||
* Wide industry and academic deployment
|
||||
@@ -55,20 +55,19 @@ performing lint checks, and optionally inserting assertion checks and
|
||||
coverage-analysis points. It outputs single- or multi-threaded .cpp and .h
|
||||
files, the "Verilated" code.
|
||||
|
||||
The user writes a little C++/SystemC wrapper file, which instantiates the
|
||||
"Verilated" model of the user's top level module. These C++/SystemC files
|
||||
are then compiled by a C++ compiler (gcc/clang/MSVC++). Executing the
|
||||
resulting executable performs the design simulation. Verilator also
|
||||
supports linking Verilated generated libraries, optionally encrypted, into
|
||||
other simulators.
|
||||
These Verilated C++/SystemC files are then compiled by a C++ compiler
|
||||
(gcc/clang/MSVC++), optionally along with a user's own C++/SystemC wrapper
|
||||
file to instantiate the Verilated model. Executing the resulting executable
|
||||
performs the design simulation. Verilator also supports linking Verilated
|
||||
generated libraries, optionally encrypted, into other simulators.
|
||||
|
||||
Verilator may not be the best choice if you are expecting a full featured
|
||||
replacement for Incisive, ModelSim/Questa, VCS or another commercial
|
||||
Verilog simulator, or if you are looking for a behavioral Verilog simulator
|
||||
e.g. for a quick class project (we recommend `Icarus Verilog`_ for this.)
|
||||
However, if you are looking for a path to migrate SystemVerilog to C++ or
|
||||
SystemC, or your team is comfortable writing just a touch of C++ code,
|
||||
Verilator is the tool for you.
|
||||
replacement for a closed-source Verilog simulator, need SDF annotation,
|
||||
mixed-signal simulation, or are doing a quick class project (we recommend
|
||||
`Icarus Verilog`_ for classwork.) However, if you are looking for a path
|
||||
to migrate SystemVerilog to C++/SystemC, or want high speed simulation of
|
||||
synthesizable designs containing limited verification constructs, Verilator
|
||||
is the tool for you.
|
||||
|
||||
|
||||
Performance
|
||||
@@ -85,9 +84,11 @@ multithreading (yielding 200-1000x total over interpreted simulators).
|
||||
|
||||
Verilator has typically similar or better performance versus the
|
||||
closed-source Verilog simulators (Carbon Design Systems Carbonator,
|
||||
Modelsim, Cadence Incisive/NC-Verilog, Synopsys VCS, VTOC, and Pragmatic
|
||||
CVer/CVC). But, Verilator is open-sourced, so you can spend on computes
|
||||
rather than licenses. Thus Verilator gives you the best cycles/dollar.
|
||||
Modelsim/Questa, Cadence Incisive/NC-Verilog, Synopsys VCS, VTOC, and
|
||||
Pragmatic CVer/CVC). But, Verilator is open-sourced, so you can spend on
|
||||
computes rather than licenses. Thus Verilator gives you the best
|
||||
cycles/dollar.
|
||||
|
||||
|
||||
Installation & Documentation
|
||||
============================
|
||||
|
||||
+29
-20
@@ -282,35 +282,40 @@ detailed descriptions of these arguments.
|
||||
--autoflush Flush streams after all $displays
|
||||
--bbox-sys Blackbox unknown $system calls
|
||||
--bbox-unsup Blackbox unsupported language features
|
||||
--bin <filename> Override Verilator binary
|
||||
--build Build model executable/library after Verilation
|
||||
-CFLAGS <flags> C++ compiler arguments for makefile
|
||||
--build-dep-bin <filename> Override build dependency Verilator binary
|
||||
--build-jobs <jobs> Parallelism for --build
|
||||
--cc Create C++ output
|
||||
--cdc Clock domain crossing analysis
|
||||
-CFLAGS <flags> C++ compiler arguments for makefile
|
||||
--clk <signal-name> Mark specified signal as clock
|
||||
--make <build-tool> Generate scripts for specified build tool
|
||||
--no-clk <signal-name> Prevent marking specified signal as clock
|
||||
--compiler <compiler-name> Tune for specified C++ compiler
|
||||
--converge-limit <loops> Tune convergence settle time
|
||||
--coverage Enable all coverage
|
||||
--coverage-line Enable line coverage
|
||||
--coverage-max-width <width> Maximum array depth for coverage
|
||||
--coverage-toggle Enable toggle coverage
|
||||
--coverage-user Enable SVL user coverage
|
||||
--coverage-underscore Enable coverage of _signals
|
||||
--coverage-user Enable SVL user coverage
|
||||
-D<var>[=<value>] Set preprocessor define
|
||||
--debug Enable debugging
|
||||
--debug-check Enable debugging assertions
|
||||
--no-debug-leak Disable leaking memory in --debug mode
|
||||
--debugi <level> Enable debugging at a specified level
|
||||
--debugi-<srcfile> <level> Enable debugging a source file at a level
|
||||
--no-decoration Disable comments and symbol decorations
|
||||
--default-language <lang> Default language to parse
|
||||
+define+<var>=<value> Set preprocessor define
|
||||
--dpi-hdr-only Only produce the DPI header file
|
||||
--dump-defines Show preprocessor defines with -E
|
||||
--dump-tree Enable dumping .tree files
|
||||
--dump-treei <level> Enable dumping .tree files at a level
|
||||
--dump-treei-<srcfile> <level> Enable dumping .tree file at a source file at a level
|
||||
--dump-graph Enable dumping V3Graphs to .dot
|
||||
--dump-tree Enable dumping Ast .tree files
|
||||
--dump-tree-addrids Use short identifiers instead of addresses
|
||||
--dump-<srcfile> Enable dumping everything in source file
|
||||
--dumpi-graph <level> Enable dumping V3Graphs to .dot files at level
|
||||
--dumpi-tree <level> Enable dumping Ast .tree files at level
|
||||
--dumpi-<srcfile> <level> Enable dumping everything in source file at level
|
||||
-E Preprocess, but do not compile
|
||||
--error-limit <value> Abort after this number of errors
|
||||
--exe Link to create executable
|
||||
@@ -321,6 +326,7 @@ detailed descriptions of these arguments.
|
||||
--flatten Force inlining of all modules, tasks and functions
|
||||
-fno-<optimization> Disable internal optimization stage
|
||||
-G<name>=<value> Overwrite top-level parameter
|
||||
--gate-stmts <value> Tune gate optimizer depth
|
||||
--gdb Run Verilator under GDB interactively
|
||||
--gdbbt Run Verilator under GDB for backtrace
|
||||
--generate-key Create random key for --protect-key
|
||||
@@ -328,54 +334,52 @@ detailed descriptions of these arguments.
|
||||
--help Display this help
|
||||
--hierarchical Enable hierarchical Verilation
|
||||
-I<dir> Directory to search for includes
|
||||
-j <jobs> Parallelism for --build
|
||||
--gate-stmts <value> Tune gate optimizer depth
|
||||
--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
|
||||
-j <jobs> Parallelism for --build (alias to --build-jobs)
|
||||
--l2-name <value> Verilog scope name of the top module
|
||||
--language <lang> Default language standard to parse
|
||||
-LDFLAGS <flags> Linker pre-object arguments for makefile
|
||||
--lib-create <name> Create a DPI library
|
||||
+libext+<ext>+[ext]... Extensions for finding modules
|
||||
--lint-only Lint, but do not make output
|
||||
--make <build-tool> Generate scripts for specified build tool
|
||||
-MAKEFLAGS <flags> Arguments to pass to make during --build
|
||||
--main Generate C++ main() file
|
||||
--max-num-width <value> Maximum number width (default: 64K)
|
||||
--MMD Create .d dependency files
|
||||
--MP Create phony dependency targets
|
||||
--Mdir <directory> Name of output object directory
|
||||
--MMD Create .d dependency files
|
||||
--mod-prefix <topname> Name to prepend to lower classes
|
||||
--no-clk <signal-name> Prevent marking specified signal as clock
|
||||
--no-decoration Disable comments and symbol decorations
|
||||
--no-pins64 Don't use uint64_t's for 33-64 bit sigs
|
||||
--no-skip-identical Disable skipping identical output
|
||||
--MP Create phony dependency targets
|
||||
+notimingchecks Ignored
|
||||
-O0 Disable optimizations
|
||||
-O3 High performance optimizations
|
||||
-O<optimization-letter> Selectable optimizations
|
||||
-o <executable> Name of final executable
|
||||
--no-order-clock-delay Disable ordering clock enable assignments
|
||||
--no-verilate Skip verilation and just compile previously Verilated code.
|
||||
--no-verilate Skip Verilation, only compile previously Verilated code
|
||||
--output-split <statements> Split .cpp files into pieces
|
||||
--output-split-cfuncs <statements> Split model functions
|
||||
--output-split-ctrace <statements> Split tracing functions
|
||||
-P Disable line numbers and blanks with -E
|
||||
--pins-bv <bits> Specify types for top level ports
|
||||
--pins-sc-uint Specify types for top level ports
|
||||
--pins-sc-biguint Specify types for top level ports
|
||||
--pins-sc-uint Specify types for top level ports
|
||||
--pins-uint8 Specify types for top level ports
|
||||
--no-pins64 Don't use uint64_t's for 33-64 bit sigs
|
||||
--pipe-filter <command> Filter all input through a script
|
||||
--pp-comments Show preprocessor comments with -E
|
||||
--prefix <topname> Name of top level class
|
||||
--private Debugging; see docs
|
||||
--prof-c Compile C++ code with profiling
|
||||
--prof-cfuncs Name functions for profiling
|
||||
--prof-exec Enable generating execution profile for gantt chart
|
||||
--prof-pgo Enable generating profiling data for PGO
|
||||
--protect-key <key> Key for symbol protection
|
||||
--protect-ids Hash identifier names for obscurity
|
||||
--protect-key <key> Key for symbol protection
|
||||
--protect-lib <name> Create a DPI protected library
|
||||
--private Debugging; see docs
|
||||
--public Debugging; see docs
|
||||
--public-flat-rw Mark all variables, etc as public_flat_rw
|
||||
-pvalue+<name>=<value> Overwrite toplevel parameter
|
||||
@@ -386,6 +390,7 @@ detailed descriptions of these arguments.
|
||||
--rr Run Verilator and record with rr
|
||||
--savable Enable model save-restore
|
||||
--sc Create SystemC output
|
||||
--no-skip-identical Disable skipping identical output
|
||||
--stats Create statistics file
|
||||
--stats-vars Provide statistics on variables
|
||||
-sv Enable SystemVerilog parsing
|
||||
@@ -413,6 +418,7 @@ detailed descriptions of these arguments.
|
||||
--unused-regexp <regexp> Tune UNUSED lint signals
|
||||
-V Verbose version and config
|
||||
-v <filename> Verilog library
|
||||
--no-verilate Skip verilation and just compile previously Verilated code.
|
||||
+verilog1995ext+<ext> Synonym for +1364-1995ext+<ext>
|
||||
+verilog2001ext+<ext> Synonym for +1364-2001ext+<ext>
|
||||
--version Displays program version and exits
|
||||
@@ -427,6 +433,9 @@ detailed descriptions of these arguments.
|
||||
-Wno-lint Disable all lint warnings
|
||||
-Wno-style Disable all style warnings
|
||||
-Wpedantic Warn on compliance-test issues
|
||||
-Wwarn-<message> Enable specified warning message
|
||||
-Wwarn-lint Enable lint warning message
|
||||
-Wwarn-style Enable style warning message
|
||||
--x-assign <mode> Assign non-initial Xs to this value
|
||||
--x-initial <mode> Assign initial Xs to this value
|
||||
--x-initial-edge Enable initial X->0 and X->1 edge triggers
|
||||
|
||||
@@ -38,7 +38,7 @@ def diff_dir(a, b):
|
||||
|
||||
anyfile = False
|
||||
for base in sorted(files.keys()):
|
||||
if (not 'a' in files[base]) or (not 'b' in files[base]):
|
||||
if ('a' not in files[base]) or ('b' not in files[base]):
|
||||
continue
|
||||
a = files[base]['a']
|
||||
b = files[base]['b']
|
||||
|
||||
+2
-1
@@ -149,7 +149,8 @@ def report():
|
||||
for thread in Threads:
|
||||
# Make potentially multiple characters per column
|
||||
for start in Threads[thread]:
|
||||
if not Threads[thread][start]: continue
|
||||
if not Threads[thread][start]:
|
||||
continue
|
||||
cpu = Threads[thread][start]['cpu']
|
||||
elapsed = Threads[thread][start]['end'] - start
|
||||
if cpu not in Global['cpus']:
|
||||
|
||||
@@ -19,6 +19,10 @@ set -x
|
||||
|
||||
cd $(dirname "$0")/..
|
||||
|
||||
# Avoid occasional cpan failures "Issued certificate has expired."
|
||||
export PERL_LWP_SSL_VERIFY_HOSTNAME=0
|
||||
echo "check_certificate = off" >> ~/.wgetrc
|
||||
|
||||
fatal() {
|
||||
echo "ERROR: $(basename "$0"): $1" >&2; exit 1;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
FROM ubuntu:20.04
|
||||
FROM ubuntu:22.04
|
||||
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive \
|
||||
|
||||
@@ -8,7 +8,7 @@ Verilator build. It uses the following parameters:
|
||||
|
||||
- Source revision (default: master)
|
||||
|
||||
- Compiler (GCC 9.3.0, clang 10.0.0, default: 9.3.0)
|
||||
- Compiler (GCC 10.3.0, clang 10.0.0, default: 10.3.0)
|
||||
|
||||
The container is published as ``verilator/verilator-buildenv`` on `docker
|
||||
hub
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
FROM ubuntu:20.04
|
||||
FROM ubuntu:22.04
|
||||
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive \
|
||||
|
||||
+6
-6
@@ -5,14 +5,14 @@
|
||||
# General Public License Version 3 or the Perl Artistic License Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[4.224 2022-06-19],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
# When releasing, also update header of Changes file
|
||||
# and commit using "devel release" or "Version bump" message
|
||||
# Then 'make maintainer-dist'
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[4.228 2022-10-01],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
|
||||
AC_CONFIG_HEADERS(src/config_build.h)
|
||||
AC_CONFIG_FILES(Makefile src/Makefile src/Makefile_obj include/verilated.mk include/verilated_config.h verilator.pc verilator-config.cmake verilator-config-version.cmake)
|
||||
@@ -355,7 +355,7 @@ AC_SUBST(CFG_CXXFLAGS_PROFILE)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++20)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++20)
|
||||
case "$(which lsb_release 2>&1 > /dev/null && lsb_release -d)" in
|
||||
*Ubuntu*22.04*)
|
||||
*Arch*Linux* | *Ubuntu*22.04*)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++17)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++17)
|
||||
;;
|
||||
|
||||
@@ -5,11 +5,13 @@ Please see the Verilator manual for 200+ additional contributors. Thanks to all.
|
||||
|
||||
Adrien Le Masle
|
||||
Ahmed El-Mahmoudy
|
||||
Aleksander Kiryk
|
||||
Alex Chadwick
|
||||
Àlex Torregrosa
|
||||
Aliaksei Chapyzhenka
|
||||
Ameya Vikram Singh
|
||||
Andreas Kuster
|
||||
Arkadiusz Kozdra
|
||||
Chris Randall
|
||||
Chuxuan Wang
|
||||
Conor McCullough
|
||||
@@ -32,6 +34,7 @@ Gianfranco Costamagna
|
||||
Glen Gibb
|
||||
Graham Rushton
|
||||
Guokai Chen
|
||||
Gustav Svensk
|
||||
Harald Heckmann
|
||||
Howard Su
|
||||
Huang Rui
|
||||
@@ -41,6 +44,7 @@ HyungKi Jeong
|
||||
Iru Cai
|
||||
Ivan Vnučec
|
||||
Iztok Jeras
|
||||
Jake Merdich
|
||||
James Hanlon
|
||||
James Hutchinson
|
||||
James Pallister
|
||||
@@ -68,6 +72,7 @@ Lukasz Dalek
|
||||
Maarten De Braekeleer
|
||||
Maciej Sobkowski
|
||||
Marco Widmer
|
||||
Mariusz Glebocki
|
||||
Markus Krause
|
||||
Marlon James
|
||||
Marshal Qiao
|
||||
@@ -78,12 +83,15 @@ Michael Killough
|
||||
Michaël Lefebvre
|
||||
Mike Popoloski
|
||||
Miodrag Milanović
|
||||
Mladen Slijepcevic
|
||||
Morten Borup Petersen
|
||||
Mostafa Gamal
|
||||
Nandu Raj
|
||||
Nathan Kohagen
|
||||
Nathan Myers
|
||||
Patrick Stewart
|
||||
Paul Wright
|
||||
Pawel Sagan
|
||||
Peter Horvath
|
||||
Peter Monsson
|
||||
Philipp Wagner
|
||||
@@ -95,6 +103,7 @@ Rafal Kapuscik
|
||||
Raynard Qiao
|
||||
Richard Myers
|
||||
Rupert Swarbrick
|
||||
Ryszard Rozak
|
||||
Samuel Riedel
|
||||
Sean Cross
|
||||
Sebastien Van Cauwenberghe
|
||||
@@ -111,14 +120,17 @@ Tomasz Gorochowik
|
||||
Tymoteusz Blazejczyk
|
||||
Udi Finkelstein
|
||||
Unai Martinez-Corral
|
||||
Varun Koyyalagunta
|
||||
Vassilis Papaefstathiou
|
||||
Veripool API Bot
|
||||
Victor Besyakov
|
||||
William D. Jones
|
||||
Wilson Snyder
|
||||
Xi Zhang
|
||||
Yoda Lee
|
||||
Yossi Nivin
|
||||
Yuri Victorovich
|
||||
Yutetsu TAKATSUKASA
|
||||
Yu-Sheng Lin
|
||||
Yves Mathieu
|
||||
Zhanglei Wang
|
||||
|
||||
@@ -13,6 +13,7 @@ from datetime import datetime
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.abspath('./_ext'))
|
||||
|
||||
import sphinx_rtd_theme # pylint: disable=wrong-import-position,
|
||||
|
||||
+13
-56
@@ -86,61 +86,17 @@ Connecting to C++
|
||||
In C++ output mode (:vlopt:`--cc`), the Verilator generated model class is a
|
||||
simple C++ class. The user must write a C++ wrapper and main loop for the
|
||||
simulation, which instantiates the model class, and link with the Verilated
|
||||
model. Here is a simple example:
|
||||
model.
|
||||
|
||||
.. code-block:: C++
|
||||
Refer to ``examples/make_tracing_c`` in the distribution for a detailed
|
||||
commented example.
|
||||
|
||||
#include <verilated.h> // Defines common routines
|
||||
#include <iostream> // Need std::cout
|
||||
#include "Vtop.h" // From Verilating "top.v"
|
||||
Top level IO signals are read and written as members of the model. You
|
||||
call the model's :code:`eval()` method to evaluate the model. When the
|
||||
simulation is complete call the model's :code:`final()` method to execute
|
||||
any SystemVerilog final blocks, and complete any assertions. See
|
||||
:ref:`Evaluation Loop`.
|
||||
|
||||
Vtop *top; // Instantiation of model
|
||||
|
||||
uint64_t main_time = 0; // Current simulation time
|
||||
// This is a 64-bit integer to reduce wrap over issues and
|
||||
// allow modulus. This is in units of the timeprecision
|
||||
// used in Verilog (or from --timescale-override)
|
||||
|
||||
double sc_time_stamp() { // Called by $time in Verilog
|
||||
return main_time; // converts to double, to match
|
||||
// what SystemC does
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
Verilated::commandArgs(argc, argv); // Remember args
|
||||
|
||||
top = new Vtop; // Create model
|
||||
// Do not instead make Vtop as a file-scope static
|
||||
// variable, as the "C++ static initialization order fiasco"
|
||||
// may cause a crash
|
||||
|
||||
top->reset_l = 0; // Set some inputs
|
||||
|
||||
while (!Verilated::gotFinish()) {
|
||||
if (main_time > 10) {
|
||||
top->reset_l = 1; // Deassert reset
|
||||
}
|
||||
if ((main_time % 10) == 1) {
|
||||
top->clk = 1; // Toggle clock
|
||||
}
|
||||
if ((main_time % 10) == 6) {
|
||||
top->clk = 0;
|
||||
}
|
||||
top->eval(); // Evaluate model
|
||||
cout << top->out << endl; // Read a output
|
||||
main_time++; // Time passes...
|
||||
}
|
||||
|
||||
top->final(); // Done simulating
|
||||
// // (Though this example doesn't get here)
|
||||
delete top;
|
||||
}
|
||||
|
||||
|
||||
Note top level IO signals are read and written as members of the model. You
|
||||
call the :code:`eval()` method to evaluate the model. When the simulation is
|
||||
complete call the :code:`final()` method to execute any SystemVerilog final
|
||||
blocks, and complete any assertions. See :ref:`Evaluation Loop`.
|
||||
|
||||
|
||||
Connecting to SystemC
|
||||
@@ -449,14 +405,15 @@ accesses the above signal "readme" would be:
|
||||
|
||||
int main(int argc, char** argv, char** env) {
|
||||
Verilated::commandArgs(argc, argv);
|
||||
Vour* top = new Vour;
|
||||
Verilated::internalsDump(); // See scopes to help debug
|
||||
while (!Verilated::gotFinish()) {
|
||||
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
|
||||
const std::unique_ptr<Vour> top{new Vour{contextp.get()}};
|
||||
|
||||
contextp->internalsDump(); // See scopes to help debug
|
||||
while (!contextp->gotFinish()) {
|
||||
top->eval();
|
||||
VerilatedVpi::callValueCbs(); // For signal callbacks
|
||||
read_and_check();
|
||||
}
|
||||
delete top;
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
|
||||
@@ -8,7 +8,7 @@ Simulation Runtime Arguments
|
||||
|
||||
The following are the arguments that may be passed to a Verilated
|
||||
executable, provided that executable calls
|
||||
:code:`Verilated::commandArgs()`.
|
||||
:code:`VerilatedContext*->commandArgs(argc, argv)`.
|
||||
|
||||
All simulation runtime arguments begin with "+verilator", so that the
|
||||
user's executable may skip over all "+verilator" arguments when parsing its
|
||||
@@ -96,7 +96,7 @@ Summary:
|
||||
.. option:: +verilator+noassert
|
||||
|
||||
Disable assert checking per runtime argument. This is the same as
|
||||
calling :code:`Verilated::assertOn(false)` in the model.
|
||||
calling :code:`VerilatedContext*->assertOn(false)` in the model.
|
||||
|
||||
.. option:: +verilator+V
|
||||
|
||||
|
||||
+176
-112
@@ -115,13 +115,6 @@ Summary:
|
||||
|
||||
Using this argument will likely cause incorrect simulation.
|
||||
|
||||
.. option:: --bin <filename>
|
||||
|
||||
Rarely needed. Override the default filename for Verilator itself.
|
||||
When a dependency (.d) file is created, this filename will become a
|
||||
source dependency, such that a change in this binary will have make
|
||||
rebuild the output files.
|
||||
|
||||
.. option:: --build
|
||||
|
||||
After generating the SystemC/C++ code, Verilator will invoke the
|
||||
@@ -129,15 +122,23 @@ Summary:
|
||||
is also used). Verilator manages the build itself, and for this --build
|
||||
requires GNU Make to be available on the platform.
|
||||
|
||||
.. option:: -CFLAGS <flags>
|
||||
.. option:: --build-dep-bin <filename>
|
||||
|
||||
Add specified C compiler argument to the generated makefiles. For
|
||||
multiple flags either pass them as a single argument with space
|
||||
separators quoted in the shell (:command:`-CFLAGS "-a -b"`), or use
|
||||
multiple -CFLAGS options (:command:`-CFLAGS -a -CFLAGS -b`).
|
||||
Rarely needed. When a dependency (.d) file is created, this filename
|
||||
will become a source dependency, such that a change in this binary will
|
||||
have make rebuild the output files. Defaults to the full path to the
|
||||
Verilator binary.
|
||||
|
||||
When make is run on the generated makefile these will be passed to the
|
||||
C++ compiler (g++/clang++/msvc++).
|
||||
This option was named `--bin` prior to version 4.228.
|
||||
|
||||
.. option:: --build-jobs [<value>]
|
||||
|
||||
Specify the level of parallelism for :vlopt:`--build`. If zero, uses the
|
||||
number of threads in the current hardware. Otherwise, the <value> must
|
||||
be a positive integer specifying the maximum number of parallel build
|
||||
jobs.
|
||||
|
||||
See also :vlopt:`-j`.
|
||||
|
||||
.. option:: --cc
|
||||
|
||||
@@ -156,6 +157,16 @@ Summary:
|
||||
have interest in adding more traditional CDC checks, please contact the
|
||||
authors.
|
||||
|
||||
.. option:: -CFLAGS <flags>
|
||||
|
||||
Add specified C compiler argument to the generated makefiles. For
|
||||
multiple flags either pass them as a single argument with space
|
||||
separators quoted in the shell (:command:`-CFLAGS "-a -b"`), or use
|
||||
multiple -CFLAGS options (:command:`-CFLAGS -a -CFLAGS -b`).
|
||||
|
||||
When make is run on the generated makefile these will be passed to the
|
||||
C++ compiler (g++/clang++/msvc++).
|
||||
|
||||
.. option:: --clk <signal-name>
|
||||
|
||||
With :vlopt:`--clk`, the specified signal-name is taken as a root clock
|
||||
@@ -179,6 +190,11 @@ Summary:
|
||||
remove it from the combinatorial logic reevaluation checking code. This
|
||||
may greatly improve performance.
|
||||
|
||||
.. option:: --no-clk <signal-name>
|
||||
|
||||
Prevent the specified signal from being marked as clock. See
|
||||
:vlopt:`--clk`.
|
||||
|
||||
.. option:: --compiler <compiler-name>
|
||||
|
||||
Enables workarounds for the specified C++ compiler (list below).
|
||||
@@ -250,8 +266,8 @@ Summary:
|
||||
generally is a less-optimized binary with symbols present (so GDB can be used on it).
|
||||
* Enable debugging messages (equivalent to :vlopt:`--debugi 3 <--debugi>`).
|
||||
* Enable internal assertions (equivalent to :vlopt:`--debug-check`).
|
||||
* Enable intermediate form dump files (equivalent to :vlopt:`--dump-treei 3
|
||||
<--dump-treei>`).
|
||||
* Enable intermediate form dump files (equivalent to :vlopt:`--dumpi-tree 3
|
||||
<--dumpi-tree>`).
|
||||
* Leak to make node numbers unique (equivalent to :vlopt:`--debug-leak
|
||||
<--no-debug-leak>`.
|
||||
* Call abort() instead of exit() if there are any errors (so GDB can see
|
||||
@@ -288,6 +304,13 @@ Summary:
|
||||
<--debugi>`). Higher levels produce more detailed messages. See
|
||||
:vlopt:`--debug` for other implications of enabling debug.
|
||||
|
||||
.. option:: --no-decoration
|
||||
|
||||
When creating output Verilated code, minimize comments, white space,
|
||||
symbol names and other decorative items, at the cost of greatly reduced
|
||||
readability. This may assist C++ compile times. This will not typically
|
||||
change the ultimate model's performance, but may in some cases.
|
||||
|
||||
.. option:: --default-language <value>
|
||||
|
||||
Select the language to be used by default when first processing each
|
||||
@@ -335,26 +358,21 @@ Summary:
|
||||
|
||||
touch foo.v ; verilator -E --dump-defines foo.v
|
||||
|
||||
.. option:: --dump-graph
|
||||
|
||||
Rarely needed. Enable dumping V3Graph .dot debug files with dumping
|
||||
level 3. Before Verilator 4.228, :vlopt:`--dump-tree` used
|
||||
to include this option.
|
||||
|
||||
.. option:: --dump-tree
|
||||
|
||||
Rarely needed. Enable writing .tree debug files with dumping level 3,
|
||||
Rarely needed. Enable dumping Ast .tree debug files with dumping level 3,
|
||||
which dumps the standard critical stages. For details on the format see
|
||||
the Verilator Internals manual. :vlopt:`--dump-tree` is enabled
|
||||
automatically with :vlopt:`--debug`, so :vlopt:`--debug --no-dump-tree
|
||||
<--dump-tree>` may be useful if the dump files are large and not
|
||||
desired.
|
||||
|
||||
.. option:: --dump-treei <level>
|
||||
|
||||
.. option:: --dump-treei-<srcfile> <level>
|
||||
|
||||
Rarely needed - for developer use. Set internal tree dumping level
|
||||
globally to a specific dumping level or set the specified Verilator
|
||||
source file to the specified tree dumping level (e.g.
|
||||
:vlopt:`--dump-treei-V3Order 9 <--dump-treei>`). Level 0 disables dumps
|
||||
and is equivalent to :vlopt:`--no-dump-tree <--dump-tree>`. Level 9
|
||||
enables dumping of every stage.
|
||||
|
||||
.. option:: --dump-tree-addrids
|
||||
|
||||
Rarely needed - for developer use. Replace AST node addresses with
|
||||
@@ -364,6 +382,28 @@ Summary:
|
||||
by a newly allocated node after a node with the same address has been
|
||||
dumped then freed.
|
||||
|
||||
.. option:: --dump-<srcfile>
|
||||
|
||||
Rarely needed - for developer use. Enable all dumping in the given
|
||||
source file at level 3.
|
||||
|
||||
.. option:: --dumpi-graph <level>
|
||||
|
||||
Rarely needed - for developer use. Set internal V3Graph dumping level
|
||||
globally to the specified value.
|
||||
|
||||
.. option:: --dumpi-tree <level>
|
||||
|
||||
Rarely needed - for developer use. Set internal Ast dumping level
|
||||
globally to the specified value.
|
||||
|
||||
.. option:: --dumpi-<srcfile> <level>
|
||||
|
||||
Rarely needed - for developer use. Set the dumping level in the
|
||||
specified Verilator source file to the specified value (e.g.
|
||||
:vlopt:`--dumpi-V3Order 9`). Level 0 disables dumps and is equivalent
|
||||
to :vlopt:`--no-dump-<srcfile>`. Level 9 enables dumping of everything.
|
||||
|
||||
.. option:: -E
|
||||
|
||||
Preprocess the source code, but do not compile, similar to C++
|
||||
@@ -479,6 +519,30 @@ Summary:
|
||||
are typically used only when recommended by a maintainer to help debug
|
||||
or work around an issue.
|
||||
|
||||
.. option:: -future0 <option>
|
||||
|
||||
Rarely needed. Suppress an unknown Verilator option for an option that
|
||||
takes no additional arguments. This is used to allow scripts written
|
||||
with pragmas for a later version of Verilator to run under a older
|
||||
version. e.g. :code:`-future0 option --option` would on older versions
|
||||
that do not understand :code:`--option` or :code:`+option` suppress what
|
||||
would otherwise be an invalid option error, and on newer versions that
|
||||
implement :code:`--option`, :code:`-future0 option --option` would have
|
||||
the :code:`-future0 option` ignored and the :code:`--option` would
|
||||
function appropriately.
|
||||
|
||||
.. option:: -future1 <option>
|
||||
|
||||
Rarely needed. Suppress an unknown Verilator option for an option that
|
||||
takes an additional argument. This is used to allow scripts written
|
||||
with pragmas for a later version of Verilator to run under a older
|
||||
version. e.g. :code:`-future1 option --option arg` would on older
|
||||
versions that do not understand :code:`--option arg` or :code:`+option
|
||||
arg` suppress what would otherwise be an invalid option error, and on
|
||||
newer versions that implement :code:`--option arg`, :code:`-future1
|
||||
option --option arg` would have the :code:`-future1 option` ignored and
|
||||
the :code:`--option arg` would function appropriately.
|
||||
|
||||
.. option:: -G<name>=<value>
|
||||
|
||||
Overwrites the given parameter of the toplevel module. The value is
|
||||
@@ -579,26 +643,10 @@ Summary:
|
||||
|
||||
.. option:: -j [<value>]
|
||||
|
||||
Specify the level of parallelism for :vlopt:`--build`. The <value> must
|
||||
be a positive integer specifying the maximum number of parallel build
|
||||
jobs, or can be omitted. When <value> is omitted, the build will not try
|
||||
to limit the number of parallel build jobs but attempt to execute all
|
||||
independent build steps in parallel.
|
||||
|
||||
.. option:: -LDFLAGS <flags>
|
||||
|
||||
Add specified C linker arguments to the generated makefiles. For multiple
|
||||
flags either pass them as a single argument with space separators quoted
|
||||
in the shell (``-LDFLAGS "-a -b"``), or use multiple -LDFLAGS arguments
|
||||
(``-LDFLAGS -a -LDFLAGS -b``).
|
||||
|
||||
When make is run on the generated makefile these will be passed to the
|
||||
C++ linker (ld) **after** the primary file being linked. This flag is
|
||||
called :vlopt:`-LDFLAGS` as that's the traditional name in simulators;
|
||||
it's would have been better called LDLIBS as that's the Makefile
|
||||
variable it controls. (In Make, LDFLAGS is before the first object,
|
||||
LDLIBS after. -L libraries need to be in the Make variable LDLIBS, not
|
||||
LDFLAGS.)
|
||||
Specify the level of parallelism for :vlopt:`--build` if
|
||||
:vlopt:`--build-jobs` isn't provided. If zero, uses the number of threads
|
||||
in the current hardware. Otherwise, the <value> must be a positive
|
||||
integer specifying the maximum number of parallel build jobs.
|
||||
|
||||
.. option:: --l2-name <value>
|
||||
|
||||
@@ -616,12 +664,20 @@ Summary:
|
||||
A synonym for :vlopt:`--default-language`, for compatibility with other
|
||||
tools and earlier versions of Verilator.
|
||||
|
||||
.. option:: +libext+<ext>[+<ext>][...]
|
||||
.. option:: -LDFLAGS <flags>
|
||||
|
||||
Specify the extensions that should be used for finding modules. If for
|
||||
example module "my" is referenced, look in :file:`my.<ext>`. Note
|
||||
"+libext+" is fairly standard across Verilog tools. Defaults to
|
||||
".v+.sv".
|
||||
Add specified C linker arguments to the generated makefiles. For multiple
|
||||
flags either pass them as a single argument with space separators quoted
|
||||
in the shell (``-LDFLAGS "-a -b"``), or use multiple -LDFLAGS arguments
|
||||
(``-LDFLAGS -a -LDFLAGS -b``).
|
||||
|
||||
When make is run on the generated makefile these will be passed to the
|
||||
C++ linker (ld) **after** the primary file being linked. This flag is
|
||||
called :vlopt:`-LDFLAGS` as that's the traditional name in simulators;
|
||||
it's would have been better called LDLIBS as that's the Makefile
|
||||
variable it controls. (In Make, LDFLAGS is before the first object,
|
||||
LDLIBS after. -L libraries need to be in the Make variable LDLIBS, not
|
||||
LDFLAGS.)
|
||||
|
||||
.. option:: --lib-create <name>
|
||||
|
||||
@@ -640,6 +696,13 @@ Summary:
|
||||
|
||||
See also :vlopt:`--protect-lib`.
|
||||
|
||||
.. option:: +libext+<ext>[+<ext>][...]
|
||||
|
||||
Specify the extensions that should be used for finding modules. If for
|
||||
example module "my" is referenced, look in :file:`my.<ext>`. Note
|
||||
"+libext+" is fairly standard across Verilog tools. Defaults to
|
||||
".v+.sv".
|
||||
|
||||
.. option:: --lint-only
|
||||
|
||||
Check the files for lint violations only, do not create any other
|
||||
@@ -673,22 +736,20 @@ Summary:
|
||||
(e.g. ``-MAKEFLAGS -l -MAKEFLAGS -k``). Use of this option should not be
|
||||
required for simple builds using the host toolchain.
|
||||
|
||||
.. option:: --main
|
||||
|
||||
Generates a top-level C++ main() file that supports parsing arguments,
|
||||
but does not drive any inputs. This is sufficient to use for top-level
|
||||
SystemVerilog designs that has no inputs, and does not need the C++ to
|
||||
do any time advancement.
|
||||
|
||||
Implies :vlopt:`--cc` if no other output mode was provided.
|
||||
|
||||
.. option:: --max-num-width <value>
|
||||
|
||||
Set the maximum number literal width (e.g. in 1024'd22 this it the
|
||||
1024). Defaults to 64K.
|
||||
|
||||
.. option:: --MMD =item --no-MMD
|
||||
|
||||
Enable/disable creation of .d dependency files, used for make dependency
|
||||
detection, similar to gcc -MMD option. By default this option is
|
||||
enabled for :vlopt:`--cc` or :vlopt:`--sc` modes.
|
||||
|
||||
.. option:: --MP
|
||||
|
||||
When creating .d dependency files with :vlopt:`--MMD` option, make phony
|
||||
targets. Similar to :command:`gcc -MP` option.
|
||||
|
||||
.. option:: --Mdir <directory>
|
||||
|
||||
Specifies the name of the Make object directory. All generated files
|
||||
@@ -696,33 +757,23 @@ Summary:
|
||||
The directory is created if it does not exist and the parent directories
|
||||
exist; otherwise manually create the Mdir before calling Verilator.
|
||||
|
||||
.. option:: --MMD
|
||||
|
||||
.. option:: --no-MMD
|
||||
|
||||
Enable/disable creation of .d dependency files, used for make dependency
|
||||
detection, similar to gcc -MMD option. By default this option is
|
||||
enabled for :vlopt:`--cc` or :vlopt:`--sc` modes.
|
||||
|
||||
.. option:: --mod-prefix <topname>
|
||||
|
||||
Specifies the name to prepend to all lower level classes. Defaults to
|
||||
the same as :vlopt:`--prefix`.
|
||||
|
||||
.. option:: --no-clk <signal-name>
|
||||
.. option:: --MP
|
||||
|
||||
Prevent the specified signal from being marked as clock. See
|
||||
:vlopt:`--clk`.
|
||||
|
||||
.. option:: --no-decoration
|
||||
|
||||
When creating output Verilated code, minimize comments, white space,
|
||||
symbol names and other decorative items, at the cost of greatly reduced
|
||||
readability. This may assist C++ compile times. This will not typically
|
||||
change the ultimate model's performance, but may in some cases.
|
||||
|
||||
.. option:: --no-pins64
|
||||
|
||||
Backward compatible alias for :vlopt:`--pins-bv 33 <--pins-bv>`.
|
||||
|
||||
.. option:: --no-skip-identical =item --skip-identical
|
||||
|
||||
Rarely needed. Disables or enables skipping execution of Verilator if
|
||||
all source files are identical, and all output files exist with newer
|
||||
dates. By default this option is enabled for :vlopt:`--cc` or
|
||||
:vlopt:`--sc` modes only.
|
||||
When creating .d dependency files with :vlopt:`--MMD` option, make phony
|
||||
targets. Similar to :command:`gcc -MP` option.
|
||||
|
||||
.. option:: +notimingchecks
|
||||
|
||||
@@ -801,11 +852,6 @@ Summary:
|
||||
With :vlopt:`-E`, disable generation of :code:`&96;line` markers and
|
||||
blank lines, similar to :command:`gcc -P`.
|
||||
|
||||
.. option:: --pins64
|
||||
|
||||
Backward compatible alias for :vlopt:`--pins-bv 65 <--pins-bv>`. Note
|
||||
that's a 65, not a 64.
|
||||
|
||||
.. option:: --pins-bv <width>
|
||||
|
||||
Specifies SystemC inputs/outputs of greater than or equal to <width>
|
||||
@@ -838,6 +884,15 @@ Summary:
|
||||
of uint32_t. Likewise pins of width 9-16 will use uint16_t instead of
|
||||
uint32_t.
|
||||
|
||||
.. option:: --pins64
|
||||
|
||||
Backward compatible alias for :vlopt:`--pins-bv 65 <--pins-bv>`. Note
|
||||
that's a 65, not a 64.
|
||||
|
||||
.. option:: --no-pins64
|
||||
|
||||
Backward compatible alias for :vlopt:`--pins-bv 33 <--pins-bv>`.
|
||||
|
||||
.. option:: --pipe-filter <command>
|
||||
|
||||
Rarely needed. Verilator will spawn the specified command as a
|
||||
@@ -867,6 +922,11 @@ Summary:
|
||||
prepended to the name of the :vlopt:`--top` option, or V prepended to
|
||||
the first Verilog filename passed on the command line.
|
||||
|
||||
.. option:: --private
|
||||
|
||||
Opposite of :vlopt:`--public`. Is the default; this option exists for
|
||||
backwards compatibility.
|
||||
|
||||
.. option:: --prof-c
|
||||
|
||||
When compiling the C++ code, enable the compiler's profiling flag
|
||||
@@ -900,23 +960,6 @@ Summary:
|
||||
|
||||
Deprecated. Same as --prof-exec and --prof-pgo together.
|
||||
|
||||
.. option:: --protect-key <key>
|
||||
|
||||
Specifies the private key for :vlopt:`--protect-ids`. For best security
|
||||
this key should be 16 or more random bytes, a reasonable secure choice
|
||||
is the output of :command:`verilator --generate-key` . Typically, a key
|
||||
would be created by the user once for a given protected design library,
|
||||
then every Verilator run for subsequent versions of that library would
|
||||
be passed the same :vlopt:`--protect-key`. Thus, if the input Verilog is
|
||||
similar between library versions (Verilator runs), the Verilated code
|
||||
will likewise be mostly similar.
|
||||
|
||||
If :vlopt:`--protect-key` is not specified and a key is needed,
|
||||
Verilator will generate a new key for every Verilator run. As the key is
|
||||
not saved, this is best for security, but means every Verilator run will
|
||||
give vastly different output even for identical input, perhaps harming
|
||||
compile times (and certainly thrashing any "ccache").
|
||||
|
||||
.. option:: --protect-ids
|
||||
|
||||
Hash any private identifiers (variable, module, and assertion block
|
||||
@@ -937,6 +980,23 @@ Summary:
|
||||
prototypes. Use of the VPI is not recommended as many design details
|
||||
may be exposed, and an INSECURE warning will be issued.
|
||||
|
||||
.. option:: --protect-key <key>
|
||||
|
||||
Specifies the private key for :vlopt:`--protect-ids`. For best security
|
||||
this key should be 16 or more random bytes, a reasonable secure choice
|
||||
is the output of :command:`verilator --generate-key` . Typically, a key
|
||||
would be created by the user once for a given protected design library,
|
||||
then every Verilator run for subsequent versions of that library would
|
||||
be passed the same :vlopt:`--protect-key`. Thus, if the input Verilog is
|
||||
similar between library versions (Verilator runs), the Verilated code
|
||||
will likewise be mostly similar.
|
||||
|
||||
If :vlopt:`--protect-key` is not specified and a key is needed,
|
||||
Verilator will generate a new key for every Verilator run. As the key is
|
||||
not saved, this is best for security, but means every Verilator run will
|
||||
give vastly different output even for identical input, perhaps harming
|
||||
compile times (and certainly thrashing any "ccache").
|
||||
|
||||
.. option:: --protect-lib <name>
|
||||
|
||||
Produces a DPI library similar to :vlopt:`--lib-create`, but hides
|
||||
@@ -948,11 +1008,6 @@ Summary:
|
||||
in the distribution for a demonstration of how to build and use the DPI
|
||||
library.
|
||||
|
||||
.. option:: --private
|
||||
|
||||
Opposite of :vlopt:`--public`. Is the default; this option exists for
|
||||
backwards compatibility.
|
||||
|
||||
.. option:: --public
|
||||
|
||||
This is only for historical debug use. Using it may result in
|
||||
@@ -1045,6 +1100,15 @@ Summary:
|
||||
|
||||
Specifies SystemC output mode; see also :vlopt:`--cc` option.
|
||||
|
||||
.. option:: --skip-identical
|
||||
|
||||
.. option:: --no-skip-identical
|
||||
|
||||
Rarely needed. Disables or enables skipping execution of Verilator if
|
||||
all source files are identical, and all output files exist with newer
|
||||
dates. By default this option is enabled for :vlopt:`--cc` or
|
||||
:vlopt:`--sc` modes only.
|
||||
|
||||
.. option:: --stats
|
||||
|
||||
Creates a dump file with statistics on the design in
|
||||
|
||||
+12
-6
@@ -50,7 +50,7 @@ For --cc/--sc, it creates:
|
||||
- Make include file with class names (from --make gmake)
|
||||
* - *{prefix}*\ _hier.mk
|
||||
- Make file for hierarchy blocks (from --make gmake)
|
||||
* - *{prefix|*\ _hierMkArgs.f
|
||||
* - *{prefix}*\ _hierMkArgs.f
|
||||
- Arguments for hierarchical Verilation (from --make gmake)
|
||||
* - *{prefix}*\ _hierCMakeArgs.f
|
||||
- Arguments for hierarchical Verilation (from --make cmake)
|
||||
@@ -62,13 +62,17 @@ For --cc/--sc, it creates:
|
||||
- Top level (SystemVerilog $root) internal header file
|
||||
* - *{prefix}*\ ___024root.cpp
|
||||
- Top level (SystemVerilog $root) internal C++ file
|
||||
* - *{prefix}*___024root*{__n}*\ .cpp
|
||||
- Additional top level internal C++ files (from --output-split)
|
||||
* - *{prefix}*\ ___024root\ *{__n}*\ .cpp
|
||||
- Additional top level internal C++ files
|
||||
* - *{prefix}*\ ___024root\ *{__DepSet_hash__n}*\ .cpp
|
||||
- Additional top level internal C++ files (hashed to reduce build times)
|
||||
* - *{prefix}*\ ___024root__Slow\ *{__n}*\ .cpp
|
||||
- Infrequent cold routines
|
||||
* - *{prefix}*\ ___024root__Trace{__n}*\ .cpp
|
||||
* - *{prefix}*\ ___024root\ *{__DepSet_hash__n}*\ .cpp
|
||||
- Infrequent cold routines (hashed to reduce build times)
|
||||
* - *{prefix}*\ ___024root__Trace\ *{__n}*\ .cpp
|
||||
- Wave file generation code (from --trace)
|
||||
* - *{prefix}*\ ___024root__Trace__Slow{__n}*\ .cpp
|
||||
* - *{prefix}*\ ___024root__Trace__Slow\ *{__n}*\ .cpp
|
||||
- Wave file generation code (from --trace)
|
||||
* - *{prefix}*\ __Dpi.h
|
||||
- DPI import and export declarations (from --dpi)
|
||||
@@ -87,7 +91,9 @@ For --cc/--sc, it creates:
|
||||
* - *{prefix}{each_verilog_module}*\ .cpp
|
||||
- Lower level internal C++ files
|
||||
* - *{prefix}{each_verilog_module}{__n}*\ .cpp
|
||||
- Additional lower C++ files (from --output-split)
|
||||
- Additional lower C++ files
|
||||
* - *{prefix}{each_verilog_module}{__DepSet_hash__n}*\ .cpp
|
||||
- Additional lower C++ files (hased to reduce build times)
|
||||
|
||||
For --hierarchy mode, it creates:
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ Those developing Verilator itself may also want these (see internals.rst):
|
||||
|
||||
::
|
||||
|
||||
sudo apt-get install gdb graphviz cmake clang clang-format-11 gprof lcov
|
||||
sudo apt-get install gdb graphviz cmake clang clang-format-14 gprof lcov
|
||||
sudo apt-get install yapf3
|
||||
sudo pip3 install sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe
|
||||
cpan install Pod::Perldoc
|
||||
|
||||
@@ -489,7 +489,7 @@ $test$plusargs, $value$plusargs
|
||||
|
||||
.. code-block:: C++
|
||||
|
||||
Verilated::commandArgs(argc, argv);
|
||||
{VerilatedContext*} ->commandArgs(argc, argv);
|
||||
|
||||
to register the command line before calling $test$plusargs or
|
||||
$value$plusargs.
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
Simulating (Verilated-Model Runtime)
|
||||
************************************
|
||||
|
||||
This section describes items related to simulating, that using a Verilated
|
||||
model's executable. For the runtime arguments to a simulated model, see
|
||||
:ref:`Simulation Runtime Arguments`.
|
||||
This section describes items related to simulating, that is, the use of a
|
||||
Verilated model's executable. For the runtime arguments to a simulated
|
||||
model, see :ref:`Simulation Runtime Arguments`.
|
||||
|
||||
|
||||
.. _Benchmarking & Optimization:
|
||||
|
||||
+92
-13
@@ -96,10 +96,11 @@ this.
|
||||
Each ``AstNode`` has pointers to up to four children, accessed by the
|
||||
``op1p`` through ``op4p`` methods. These methods are then abstracted in a
|
||||
specific Ast\* node class to a more specific name. For example with the
|
||||
``AstIf`` node (for ``if`` statements), ``ifsp`` calls ``op2p`` to give the
|
||||
``AstIf`` node (for ``if`` statements), ``thensp`` calls ``op2p`` to give the
|
||||
pointer to the AST for the "then" block, while ``elsesp`` calls ``op3p`` to
|
||||
give the pointer to the AST for the "else" block, or NULL if there is not
|
||||
one.
|
||||
one. These accessors are automatically generated by ``astgen`` after
|
||||
parsing the ``@astgen`` directives in the specific ``AstNode`` subclasses.
|
||||
|
||||
``AstNode`` has the concept of a next and previous AST - for example the
|
||||
next and previous statements in a block. Pointers to the AST for these
|
||||
@@ -501,22 +502,99 @@ code:
|
||||
The ``astgen`` Script
|
||||
---------------------
|
||||
|
||||
Some of the code implementing passes is extremely repetitive, and must be
|
||||
implemented for each sub-class of ``AstNode``. However, while repetitive,
|
||||
there is more variability than can be handled in C++ macros.
|
||||
The ``astgen`` script is used to generate some of the repetitive C++ code
|
||||
related to the ``AstNode`` type hierarchy. An example is the abstract ``visit``
|
||||
methods in ``VNVisitor``. There are other uses, please see the ``*__gen*``
|
||||
files in the bulid directories and the ``astgen`` script itself for details. A
|
||||
description of the more advanced features of ``astgen`` are provided here.
|
||||
|
||||
In Verilator this is implemented by using a script, ``astgen`` to
|
||||
pre-process the C++ code. For example in ``V3Const.cpp`` this is used to
|
||||
implement the ``visit()`` functions for each binary operation using the
|
||||
``TREEOP`` macro.
|
||||
|
||||
The original C source code is transformed into C code in the ``obj_opt``
|
||||
Generating ``AstNode`` members
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Some of the member s of ``AstNode`` sub-classes are generated by ``astgen``.
|
||||
These are emitted as pre-processor macro definitions, which then need to be
|
||||
added to the ``AstNode`` sub-classes they correspond to. Specifically ``class
|
||||
AstFoo`` should contain an instance of ``ASTGEN_MEMBERS_Foo;`` at class scope.
|
||||
The ``astgen`` script checks and errors if this is not present. The method
|
||||
generated depends on whether the class is a concrete final class, or an
|
||||
abstract ``AstNode*`` base-class, and on ``@astgen`` directives present in
|
||||
comment sections in the body of the ``AstNode`` sub-class definitions.
|
||||
|
||||
|
||||
List of ``@astgen`` directives
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
``@astgen`` directives in comments contained in the body of ``AstNode``
|
||||
sub-class definitions are parsed and contribute to the code generated by
|
||||
``astgen``. The general syntax is ``@astgen <keywords> := <description>``,
|
||||
where ``<keywords>`` determines what is being defined, and ``<description>`` is
|
||||
a ``<keywords>`` dependent description of the definition. The list of
|
||||
``@astgen`` directives is as follows:
|
||||
|
||||
|
||||
``op<N>`` operand directives
|
||||
"""""""""""""""""""""""""""""
|
||||
|
||||
The ``op1``, ``op2``, ``op3`` and ``op4`` directives are used to describe the
|
||||
name and type of the up to 4 child operands of a node. The syntax of the
|
||||
``<description>`` field is ``<identifier> : <type>``, where ``<identifier>``
|
||||
will be used as the base name of the generated operand accessors, and
|
||||
``<type>`` is one of:
|
||||
|
||||
1. An ``AstNode`` sub-class, defining the operand to be of that type, always
|
||||
no-null, and with an always null ``nextp()``. That is, the child node is
|
||||
always present, and is a single ``AstNode`` (as opposed to a list).
|
||||
|
||||
2. ``Optional[<AstNode sub-class>]``. This is just like in point 1 above, but
|
||||
defines the child node to be optional, meaning it may be null.
|
||||
|
||||
3. ``List[AstNode sub-class]`` describes a list operand, which means the child
|
||||
node may have a non-null ``nextp()`` and in addition the child itself may be
|
||||
null, representing an empty list.
|
||||
|
||||
|
||||
An example of the full syntax of the directive is
|
||||
``@astgen op1 := lhsp : AstNodeMath``.
|
||||
|
||||
``astnode`` generates accessors for the child nodes based on these directives.
|
||||
For non-list children, the names of the getter and setter both are that of the
|
||||
given ``<identifier>``. For list type children, the getter is ``<identifier>``,
|
||||
and instead of the setter, there an ``add<Identifier>`` method is generated
|
||||
that appends new nodes (or lists of nodes) to the child list.
|
||||
|
||||
``alias op<N>`` operand alias directives
|
||||
""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
If a super-class already defined a name and type for a child node using the
|
||||
``op<N>`` directive, but a more appropriate name exists in the context of a
|
||||
sub-class, then the alias directive can be used to introduce an additional name
|
||||
for the child node. The is ``alias op<N> := <identifier>`` where
|
||||
``<identifier>`` is the new name. ``op<N>`` must have been defined in some
|
||||
super-class of the current node.
|
||||
|
||||
Example: ``@astgen alias op1 := condp``
|
||||
|
||||
|
||||
Additional features of ``astgen``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In addition to generating ``AstNode`` members as described above,
|
||||
``astgen`` is also use to handle some of the repetitive implementation code
|
||||
that is still variable enough not to be handled in C++ macros.
|
||||
|
||||
In particular, ``astgen`` is used to pre-process some of the C++ source
|
||||
files. For example in ``V3Const.cpp``, it is used to implement the
|
||||
``visit()`` functions for each binary operation using the ``TREEOP`` macro.
|
||||
|
||||
The original C++ source code is transformed into C++ code in the ``obj_opt``
|
||||
and ``obj_dbg`` sub-directories (the former for the optimized version of
|
||||
Verilator, the latter for the debug version). So for example
|
||||
``V3Const.cpp`` into ``V3Const__gen.cpp``.
|
||||
|
||||
|
||||
Visitor Functions -----------------
|
||||
Visitor Functions
|
||||
-----------------
|
||||
|
||||
Verilator uses the "Visitor" design pattern to implement its refinement and
|
||||
optimization passes. This allows separation of the pass algorithm from the
|
||||
@@ -1153,8 +1231,9 @@ Generally what would you do to add a new feature?
|
||||
language and has a lot of back-and-forth with Verilator's grammar. Copy
|
||||
the appropriate rules to src/verilog.y and modify the productions.
|
||||
|
||||
4. If a new Ast type is needed, add it to V3AstNodes.h. Follow the
|
||||
convention described above about the AstNode type hierarchy.
|
||||
4. If a new Ast type is needed, add it to the appropriate V3AstNode*.h.
|
||||
Follow the convention described above about the AstNode type hierarchy.
|
||||
Ordering of definitions is enforced by ``astgen``.
|
||||
|
||||
5. Now you can run "test_regress/t/t_<newtestcase>.pl --debug" and it'll
|
||||
probably fail but you'll see a
|
||||
|
||||
@@ -21,11 +21,14 @@ int main(int argc, char** argv, char** env) {
|
||||
// Prevent unused variable warnings
|
||||
if (false && argc && argv && env) {}
|
||||
|
||||
// Construct a VerilatedContext to hold simulation time, etc.
|
||||
VerilatedContext* contextp = new VerilatedContext;
|
||||
|
||||
// Construct the Verilated model, from Vtop.h generated from Verilating "top.v"
|
||||
Vtop* top = new Vtop;
|
||||
Vtop* top = new Vtop{contextp};
|
||||
|
||||
// Simulate until $finish
|
||||
while (!Verilated::gotFinish()) {
|
||||
while (!contextp->gotFinish()) {
|
||||
|
||||
// Evaluate model
|
||||
top->eval();
|
||||
|
||||
@@ -32,7 +32,7 @@ int main(int argc, char** argv, char** env) {
|
||||
// When tracing, the contents of the secret module will not be seen
|
||||
VerilatedVcdC* tfp = nullptr;
|
||||
const char* flag = contextp->commandArgsPlusMatch("trace");
|
||||
if (flag && 0 == strcmp(flag, "+trace")) {
|
||||
if (flag && 0 == std::strcmp(flag, "+trace")) {
|
||||
contextp->traceEverOn(true);
|
||||
VL_PRINTF("Enabling waves into logs/vlt_dump.vcd...\n");
|
||||
tfp = new VerilatedVcdC;
|
||||
|
||||
@@ -82,14 +82,14 @@ int sc_main(int argc, char* argv[]) {
|
||||
|
||||
// You must do one evaluation before enabling waves, in order to allow
|
||||
// SystemC to interconnect everything for testing.
|
||||
sc_start(1, SC_NS);
|
||||
sc_start(SC_ZERO_TIME);
|
||||
|
||||
#if VM_TRACE
|
||||
// If verilator was invoked with --trace argument,
|
||||
// and if at run time passed the +trace argument, turn on tracing
|
||||
VerilatedVcdSc* tfp = nullptr;
|
||||
const char* flag = Verilated::commandArgsPlusMatch("trace");
|
||||
if (flag && 0 == strcmp(flag, "+trace")) {
|
||||
if (flag && 0 == std::strcmp(flag, "+trace")) {
|
||||
cout << "Enabling waves into logs/vlt_dump.vcd...\n";
|
||||
tfp = new VerilatedVcdSc;
|
||||
top->trace(tfp, 99); // Trace 99 levels of hierarchy
|
||||
|
||||
@@ -16,12 +16,13 @@ from shutil import copy2
|
||||
|
||||
|
||||
class VlFileCopy:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
verilator_args, # presently all verilator options are passed-thru
|
||||
# ideally this script would check against options mentioned in help
|
||||
self,
|
||||
verilator_args, # presently all verilator options are passed-thru
|
||||
# ideally this script would check against options mentioned in help
|
||||
debug=0,
|
||||
output_dir='copied'): # directory name we output file uses
|
||||
output_dir='copied'): # directory name we output file uses
|
||||
|
||||
self.debug = debug
|
||||
|
||||
|
||||
@@ -14,12 +14,13 @@ import xml.etree.ElementTree as ET
|
||||
|
||||
|
||||
class VlHierGraph:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
verilator_args, # presently all verilator options are passed-thru
|
||||
# ideally this script would check against options mentioned in help
|
||||
self,
|
||||
verilator_args, # presently all verilator options are passed-thru
|
||||
# ideally this script would check against options mentioned in help
|
||||
debug=0,
|
||||
output_filename='graph.dot'): # output filename
|
||||
output_filename='graph.dot'): # output filename
|
||||
self.debug = debug
|
||||
self.next_vertex_number = 0
|
||||
self.name_to_number = {}
|
||||
|
||||
+106
-59
@@ -47,27 +47,34 @@
|
||||
|
||||
#define VERILATOR_VERILATED_CPP_
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated_imp.h"
|
||||
|
||||
#include "verilated_config.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated_imp.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
#include <sys/stat.h> // mkdir
|
||||
#include <list>
|
||||
#include <limits>
|
||||
#include <list>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
#include <sys/stat.h> // mkdir
|
||||
|
||||
// clang-format off
|
||||
#if defined(_WIN32) || defined(__MINGW32__)
|
||||
# include <direct.h> // mkdir
|
||||
#endif
|
||||
|
||||
#ifdef VL_THREADED
|
||||
# include "verilated_threads.h"
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
#include "verilated_trace.h"
|
||||
|
||||
// Max characters in static char string for VL_VALUE_STRING
|
||||
constexpr unsigned VL_VALUE_STRING_MAX_WIDTH = 8192;
|
||||
|
||||
@@ -673,7 +680,10 @@ std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t w
|
||||
if (std::numeric_limits<T>::is_integer) {
|
||||
constexpr int b = 128;
|
||||
constexpr int w = VL_WORDS_I(b);
|
||||
VlWide<w> tmp0, tmp1, tmp2, tmp3;
|
||||
VlWide<w> tmp0;
|
||||
VlWide<w> tmp1;
|
||||
VlWide<w> tmp2;
|
||||
VlWide<w> tmp3;
|
||||
|
||||
WDataInP shifted = VL_EXTEND_WQ(b, 0, tmp0, static_cast<QData>(ld));
|
||||
if (shift < 0) {
|
||||
@@ -691,7 +701,8 @@ std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t w
|
||||
= VL_EXTEND_WQ(b, 0, tmp2, std::numeric_limits<uint64_t>::max()); // breaks shifted
|
||||
if (VL_GT_W(w, integer, max64Bit)) {
|
||||
WDataOutP v = VL_ASSIGN_W(b, tmp3, integer); // breaks fracDigitsPow10
|
||||
VlWide<w> zero, ten;
|
||||
VlWide<w> zero;
|
||||
VlWide<w> ten;
|
||||
VL_ZERO_W(b, zero);
|
||||
VL_EXTEND_WI(b, 0, ten, 10);
|
||||
char buf[128]; // 128B is obviously long enough to represent 128bit integer in decimal
|
||||
@@ -992,22 +1003,22 @@ 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 {
|
||||
static bool _vl_vsss_eof(FILE* fp, int floc) VL_MT_SAFE {
|
||||
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 {
|
||||
static void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE {
|
||||
if (VL_LIKELY(fp)) {
|
||||
std::fgetc(fp);
|
||||
} else {
|
||||
floc -= 8;
|
||||
}
|
||||
}
|
||||
static inline int _vl_vsss_peek(FILE* fp, int& floc, const WDataInP fromp,
|
||||
const std::string& fstr) VL_MT_SAFE {
|
||||
static int _vl_vsss_peek(FILE* fp, int& floc, const WDataInP fromp,
|
||||
const std::string& fstr) VL_MT_SAFE {
|
||||
// Get a character without advancing
|
||||
if (VL_LIKELY(fp)) {
|
||||
const int data = std::fgetc(fp);
|
||||
@@ -1024,17 +1035,16 @@ static inline int _vl_vsss_peek(FILE* fp, int& floc, const WDataInP fromp,
|
||||
}
|
||||
}
|
||||
}
|
||||
static inline void _vl_vsss_skipspace(FILE* fp, int& floc, const WDataInP fromp,
|
||||
const std::string& fstr) VL_MT_SAFE {
|
||||
static void _vl_vsss_skipspace(FILE* fp, int& floc, const WDataInP fromp,
|
||||
const std::string& fstr) VL_MT_SAFE {
|
||||
while (true) {
|
||||
const int c = _vl_vsss_peek(fp, floc, fromp, fstr);
|
||||
if (c == EOF || !std::isspace(c)) return;
|
||||
_vl_vsss_advance(fp, floc);
|
||||
}
|
||||
}
|
||||
static inline void _vl_vsss_read_str(FILE* fp, int& floc, const WDataInP fromp,
|
||||
const std::string& fstr, char* tmpp,
|
||||
const char* acceptp) VL_MT_SAFE {
|
||||
static void _vl_vsss_read_str(FILE* fp, int& floc, const WDataInP fromp, const std::string& fstr,
|
||||
char* tmpp, const char* acceptp) VL_MT_SAFE {
|
||||
// Read into tmp, consisting of characters from acceptp list
|
||||
char* cp = tmpp;
|
||||
while (true) {
|
||||
@@ -1048,9 +1058,8 @@ static inline void _vl_vsss_read_str(FILE* fp, int& floc, const WDataInP fromp,
|
||||
*cp++ = '\0';
|
||||
// VL_DBG_MSGF(" _read got='"<<tmpp<<"'\n");
|
||||
}
|
||||
static inline char* _vl_vsss_read_bin(FILE* fp, int& floc, const WDataInP fromp,
|
||||
const std::string& fstr, char* beginp, std::size_t n,
|
||||
const bool inhibit = false) {
|
||||
static char* _vl_vsss_read_bin(FILE* fp, int& floc, const WDataInP fromp, const std::string& fstr,
|
||||
char* beginp, std::size_t n, 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.
|
||||
@@ -1062,12 +1071,11 @@ static inline char* _vl_vsss_read_bin(FILE* fp, int& floc, const WDataInP fromp,
|
||||
}
|
||||
return beginp;
|
||||
}
|
||||
static inline void _vl_vsss_setbit(WDataOutP owp, int obits, int lsb, int nbits,
|
||||
IData ld) VL_MT_SAFE {
|
||||
static 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(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 {
|
||||
static void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp,
|
||||
size_t posstart, size_t posend) VL_MT_SAFE {
|
||||
// Read in base "2^^baseLog2" digits from strp[posstart..posend-1] into owp of size obits.
|
||||
int lsb = 0;
|
||||
for (int i = 0, pos = static_cast<int>(posend) - 1;
|
||||
@@ -1453,11 +1461,12 @@ void VL_SFORMAT_X(int obits, void* destp, const char* formatp, ...) VL_MT_SAFE {
|
||||
|
||||
void VL_SFORMAT_X(int obits_ignored, std::string& output, const char* formatp, ...) VL_MT_SAFE {
|
||||
if (obits_ignored) {}
|
||||
output = "";
|
||||
std::string temp_output;
|
||||
va_list ap;
|
||||
va_start(ap, formatp);
|
||||
_vl_vsformat(output, formatp, ap);
|
||||
_vl_vsformat(temp_output, formatp, ap);
|
||||
va_end(ap);
|
||||
output = temp_output;
|
||||
}
|
||||
|
||||
std::string VL_SFORMATF_NX(const char* formatp, ...) VL_MT_SAFE {
|
||||
@@ -1606,8 +1615,8 @@ IData VL_SYSTEM_IW(int lhswords, const WDataInP lhsp) VL_MT_SAFE {
|
||||
return code >> 8; // Want exit status
|
||||
}
|
||||
|
||||
IData VL_TESTPLUSARGS_I(const char* formatp) VL_MT_SAFE {
|
||||
const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(formatp);
|
||||
IData VL_TESTPLUSARGS_I(const std::string& format) VL_MT_SAFE {
|
||||
const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(format.c_str());
|
||||
return match.empty() ? 0 : 1;
|
||||
}
|
||||
|
||||
@@ -1840,8 +1849,7 @@ VlReadMem::VlReadMem(bool hex, int bits, const std::string& filename, QData star
|
||||
, m_bits{bits}
|
||||
, m_filename(filename) // Need () or GCC 4.8 false warning
|
||||
, m_end{end}
|
||||
, m_addr{start}
|
||||
, m_linenum{0} {
|
||||
, m_addr{start} {
|
||||
m_fp = std::fopen(filename.c_str(), "r");
|
||||
if (VL_UNLIKELY(!m_fp)) {
|
||||
// We don't report the Verilog source filename as it slow to have to pass it down
|
||||
@@ -1975,8 +1983,7 @@ void VlReadMem::setData(void* valuep, const std::string& rhs) {
|
||||
|
||||
VlWriteMem::VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end)
|
||||
: m_hex{hex}
|
||||
, m_bits{bits}
|
||||
, m_addr{0} {
|
||||
, m_bits{bits} {
|
||||
if (VL_UNLIKELY(start > end)) {
|
||||
VL_FATAL_MT(filename.c_str(), 0, "", "$writemem invalid address range");
|
||||
return;
|
||||
@@ -2158,29 +2165,6 @@ void VL_WRITEMEM_N(bool hex, // Hex format, else binary
|
||||
//===========================================================================
|
||||
// Timescale conversion
|
||||
|
||||
// Helper function for conversion of timescale strings
|
||||
// Converts (1|10|100)(s|ms|us|ns|ps|fs) to power of then
|
||||
int VL_TIME_STR_CONVERT(const char* strp) VL_PURE {
|
||||
int scale = 0;
|
||||
if (!strp) return 0;
|
||||
if (*strp++ != '1') return 0;
|
||||
while (*strp == '0') {
|
||||
++scale;
|
||||
++strp;
|
||||
}
|
||||
switch (*strp++) {
|
||||
case 's': break;
|
||||
case 'm': scale -= 3; break;
|
||||
case 'u': scale -= 6; break;
|
||||
case 'n': scale -= 9; break;
|
||||
case 'p': scale -= 12; break;
|
||||
case 'f': scale -= 15; break;
|
||||
default: return 0;
|
||||
}
|
||||
if ((scale < 0) && (*strp++ != 's')) return 0;
|
||||
if (*strp) return 0;
|
||||
return scale;
|
||||
}
|
||||
static const char* vl_time_str(int scale) VL_PURE {
|
||||
static const char* const names[]
|
||||
= {"100s", "10s", "1s", "100ms", "10ms", "1ms", "100us", "10us", "1us",
|
||||
@@ -2302,8 +2286,9 @@ void VerilatedContext::checkMagic(const VerilatedContext* contextp) {
|
||||
}
|
||||
|
||||
VerilatedContext::Serialized::Serialized() {
|
||||
m_timeunit = VL_TIME_UNIT; // Initial value until overriden by _Vconfigure
|
||||
m_timeprecision = VL_TIME_PRECISION; // Initial value until overriden by _Vconfigure
|
||||
constexpr int8_t picosecond = -12;
|
||||
m_timeunit = picosecond; // Initial value until overriden by _Vconfigure
|
||||
m_timeprecision = picosecond; // Initial value until overriden by _Vconfigure
|
||||
}
|
||||
|
||||
void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
|
||||
@@ -2323,7 +2308,7 @@ std::string VerilatedContext::dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMut
|
||||
return m_dumpfile;
|
||||
}
|
||||
std::string VerilatedContext::dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
|
||||
const std::string out = dumpfile();
|
||||
std::string out = dumpfile();
|
||||
if (VL_UNLIKELY(out.empty())) {
|
||||
VL_PRINTF_MT("%%Warning: $dumpvar ignored as not proceeded by $dumpfile\n");
|
||||
return "";
|
||||
@@ -2428,6 +2413,33 @@ const char* VerilatedContext::timeprecisionString() const VL_MT_SAFE {
|
||||
return vl_time_str(timeprecision());
|
||||
}
|
||||
|
||||
void VerilatedContext::threads(unsigned n) {
|
||||
if (n == 0) VL_FATAL_MT(__FILE__, __LINE__, "", "%Error: Simulation threads must be >= 1");
|
||||
|
||||
if (m_threadPool) {
|
||||
VL_FATAL_MT(
|
||||
__FILE__, __LINE__, "",
|
||||
"%Error: Cannot set simulation threads after the thread pool has been created.");
|
||||
}
|
||||
|
||||
#if VL_THREADED
|
||||
if (m_threads == n) return; // To avoid unnecessary warnings
|
||||
m_threads = n;
|
||||
const unsigned hardwareThreadsAvailable = std::thread::hardware_concurrency();
|
||||
if (m_threads > hardwareThreadsAvailable) {
|
||||
VL_PRINTF_MT("%%Warning: System has %u hardware threads but simulation thread count set "
|
||||
"to %u. This will likely cause significant slowdown.\n",
|
||||
hardwareThreadsAvailable, m_threads);
|
||||
}
|
||||
#else
|
||||
if (n > 1) {
|
||||
VL_PRINTF_MT("%%Warning: Verilator run-time library built without VL_THREADS. Ignoring "
|
||||
"call to 'VerilatedContext::threads' with argument %u.\n",
|
||||
n);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void VerilatedContext::commandArgs(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex) {
|
||||
const VerilatedLockGuard lock{m_argMutex};
|
||||
m_args.m_argVec.clear(); // Empty first, then add
|
||||
@@ -2458,6 +2470,33 @@ void VerilatedContext::internalsDump() const VL_MT_SAFE {
|
||||
VerilatedImp::userDump();
|
||||
}
|
||||
|
||||
void VerilatedContext::addModel(VerilatedModel* modelp) {
|
||||
threadPoolp(); // Ensure thread pool is created, so m_threads cannot change any more
|
||||
|
||||
if (modelp->threads() > m_threads) {
|
||||
std::ostringstream msg;
|
||||
msg << "VerilatedContext has " << m_threads << " threads but model '"
|
||||
<< modelp->modelName() << "' (instantiated as '" << modelp->hierName()
|
||||
<< "') was Verilated with --threads " << modelp->threads() << ".\n";
|
||||
const std::string str = msg.str();
|
||||
VL_FATAL_MT(__FILE__, __LINE__, modelp->hierName(), str.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
VerilatedVirtualBase* VerilatedContext::threadPoolp() {
|
||||
if (m_threads == 1) return nullptr;
|
||||
#if VL_THREADED
|
||||
if (!m_threadPool) m_threadPool.reset(new VlThreadPool{this, m_threads - 1});
|
||||
#endif
|
||||
return m_threadPool.get();
|
||||
}
|
||||
|
||||
VerilatedVirtualBase*
|
||||
VerilatedContext::enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&)) {
|
||||
if (!m_executionProfiler) m_executionProfiler.reset(construct(*this));
|
||||
return m_executionProfiler.get();
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// VerilatedContextImp:: Methods - command line
|
||||
|
||||
@@ -2767,7 +2806,7 @@ void Verilated::runFlushCallbacks() VL_MT_SAFE {
|
||||
// When running internal code coverage (gcc --coverage, as opposed to
|
||||
// verilator --coverage), dump coverage data to properly cover failing
|
||||
// tests.
|
||||
VL_GCOV_FLUSH();
|
||||
VL_GCOV_DUMP();
|
||||
}
|
||||
|
||||
void Verilated::addExitCb(VoidPCb cb, void* datap) VL_MT_SAFE {
|
||||
@@ -2850,6 +2889,14 @@ void VerilatedImp::versionDump() VL_MT_SAFE {
|
||||
VL_PRINTF_MT(" Version: %s %s\n", Verilated::productName(), Verilated::productVersion());
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// VerilatedModel:: Methods
|
||||
|
||||
VerilatedModel::VerilatedModel(VerilatedContext& context)
|
||||
: m_context{context} {}
|
||||
|
||||
std::unique_ptr<VerilatedTraceConfig> VerilatedModel::traceConfig() const { return nullptr; }
|
||||
|
||||
//===========================================================================
|
||||
// VerilatedModule:: Methods
|
||||
|
||||
|
||||
+70
-14
@@ -81,6 +81,7 @@
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
class VerilatedContext;
|
||||
class VerilatedContextImp;
|
||||
class VerilatedContextImpData;
|
||||
class VerilatedCovContext;
|
||||
@@ -90,6 +91,9 @@ class VerilatedFstC;
|
||||
class VerilatedFstSc;
|
||||
class VerilatedScope;
|
||||
class VerilatedScopeNameMap;
|
||||
template <class, class>
|
||||
class VerilatedTrace;
|
||||
class VerilatedTraceConfig;
|
||||
class VerilatedVar;
|
||||
class VerilatedVarNameMap;
|
||||
class VerilatedVcd;
|
||||
@@ -252,6 +256,40 @@ public:
|
||||
#endif
|
||||
};
|
||||
|
||||
//=========================================================================
|
||||
/// Base class of a Verilator generated (Verilated) model.
|
||||
///
|
||||
/// VerilatedModel is a base class of the user facing primary class generated
|
||||
/// by Verilator.
|
||||
|
||||
class VerilatedModel VL_NOT_FINAL {
|
||||
VL_UNCOPYABLE(VerilatedModel);
|
||||
|
||||
VerilatedContext& m_context; // The VerilatedContext this model is instantiated under
|
||||
|
||||
protected:
|
||||
explicit VerilatedModel(VerilatedContext& context);
|
||||
virtual ~VerilatedModel() = default;
|
||||
|
||||
public:
|
||||
/// Returns the VerilatedContext this model is instantiated under
|
||||
/// Used to get to e.g. simulation time via contextp()->time()
|
||||
VerilatedContext* contextp() const { return &m_context; }
|
||||
/// Returns the hierarchical name of this module instance.
|
||||
virtual const char* hierName() const = 0;
|
||||
/// Returns the name of this model (the name of the generated model class).
|
||||
virtual const char* modelName() const = 0;
|
||||
/// Returns the thread level parallelism, this model was Verilated with. Always 1 or higher.
|
||||
virtual unsigned threads() const = 0;
|
||||
|
||||
private:
|
||||
// The following are for use by Verilator internals only
|
||||
template <class, class>
|
||||
friend class VerilatedTrace;
|
||||
// Run-time trace configuration requested by this model
|
||||
virtual std::unique_ptr<VerilatedTraceConfig> traceConfig() const;
|
||||
};
|
||||
|
||||
//=========================================================================
|
||||
/// Base class for all Verilated module classes.
|
||||
|
||||
@@ -266,10 +304,6 @@ public:
|
||||
const char* name() const { return m_namep; } ///< Return name of module
|
||||
};
|
||||
|
||||
/// Declare a module, ala SC_MODULE
|
||||
#define VL_MODULE(modname) class modname VL_NOT_FINAL : public VerilatedModule
|
||||
// Not class final in VL_MODULE, as users might be abstracting our models (--hierarchical)
|
||||
|
||||
//=========================================================================
|
||||
// Functions overridable by user defines
|
||||
// (Internals however must use VL_PRINTF_MT, which calls these.)
|
||||
@@ -362,6 +396,16 @@ protected:
|
||||
|
||||
// Implementation details
|
||||
const std::unique_ptr<VerilatedContextImpData> m_impdatap;
|
||||
// Number of threads to use for simulation (size of m_threadPool + 1 for main thread)
|
||||
#ifdef VL_THREADED
|
||||
unsigned m_threads = std::thread::hardware_concurrency();
|
||||
#else
|
||||
const unsigned m_threads = 1;
|
||||
#endif
|
||||
// The thread pool shared by all models added to this context
|
||||
std::unique_ptr<VerilatedVirtualBase> m_threadPool;
|
||||
// The execution profiler shared by all models added to this context
|
||||
std::unique_ptr<VerilatedVirtualBase> m_executionProfiler;
|
||||
// Coverage access
|
||||
std::unique_ptr<VerilatedVirtualBase> m_coveragep; // Pointer for coveragep()
|
||||
|
||||
@@ -413,9 +457,9 @@ public:
|
||||
VerilatedCovContext* coveragep() VL_MT_SAFE;
|
||||
/// Set debug level
|
||||
/// Debug is currently global, but for forward compatibility have a per-context method
|
||||
static void debug(int val) VL_MT_SAFE;
|
||||
static inline void debug(int val) VL_MT_SAFE;
|
||||
/// Return debug level
|
||||
static int debug() VL_MT_SAFE;
|
||||
static inline int debug() VL_MT_SAFE;
|
||||
/// Set current number of errors/assertions
|
||||
void errorCount(int val) VL_MT_SAFE;
|
||||
/// Increment current number of errors/assertions
|
||||
@@ -477,7 +521,7 @@ public:
|
||||
///
|
||||
/// * Else, time comes from the legacy 'double sc_time_stamp()' which
|
||||
/// must be a function defined by the user's wrapper.
|
||||
uint64_t time() const VL_MT_SAFE;
|
||||
inline uint64_t time() const VL_MT_SAFE;
|
||||
/// Set current simulation time. See time() for side effect details
|
||||
void time(uint64_t value) VL_MT_SAFE { m_s.m_time = value; }
|
||||
/// Advance current simulation time. See time() for side effect details
|
||||
@@ -495,6 +539,12 @@ public:
|
||||
/// Get time precision as IEEE-standard text
|
||||
const char* timeprecisionString() const VL_MT_SAFE;
|
||||
|
||||
/// Get number of threads used for simulation (including the main thread)
|
||||
unsigned threads() const { return m_threads; }
|
||||
/// Set number of threads used for simulation (including the main thread)
|
||||
/// Can only be called before the thread pool is created (before first model is added).
|
||||
void threads(unsigned n);
|
||||
|
||||
/// Allow traces to at some point be enabled (disables some optimizations)
|
||||
void traceEverOn(bool flag) VL_MT_SAFE {
|
||||
if (flag) calcUnusedSigs(true);
|
||||
@@ -510,13 +560,20 @@ public:
|
||||
/// releases - contact the authors before production use.
|
||||
void scopesDump() const VL_MT_SAFE;
|
||||
|
||||
public: // But for internal use only
|
||||
// METHODS - public but for internal use only
|
||||
|
||||
// Internal: access to implementation class
|
||||
VerilatedContextImp* impp() { return reinterpret_cast<VerilatedContextImp*>(this); }
|
||||
const VerilatedContextImp* impp() const {
|
||||
return reinterpret_cast<const VerilatedContextImp*>(this);
|
||||
}
|
||||
|
||||
void addModel(VerilatedModel*);
|
||||
|
||||
VerilatedVirtualBase* threadPoolp();
|
||||
VerilatedVirtualBase*
|
||||
enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&));
|
||||
|
||||
// Internal: $dumpfile
|
||||
void dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
|
||||
std::string dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
|
||||
@@ -595,13 +652,13 @@ public: // But internals only - called from VerilatedModule's
|
||||
const char* name() const { return m_namep; }
|
||||
const char* identifier() const { return m_identifierp; }
|
||||
int8_t timeunit() const { return m_timeunit; }
|
||||
inline VerilatedSyms* symsp() const { return m_symsp; }
|
||||
VerilatedSyms* symsp() const { return m_symsp; }
|
||||
VerilatedVar* varFind(const char* namep) const VL_MT_SAFE_POSTINIT;
|
||||
VerilatedVarNameMap* varsp() const VL_MT_SAFE_POSTINIT { return m_varsp; }
|
||||
void scopeDump() const;
|
||||
void* exportFindError(int funcnum) const;
|
||||
static void* exportFindNullError(int funcnum) VL_MT_SAFE;
|
||||
static inline void* exportFind(const VerilatedScope* scopep, int funcnum) VL_MT_SAFE {
|
||||
static void* exportFind(const VerilatedScope* scopep, int funcnum) VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(!scopep)) return exportFindNullError(funcnum);
|
||||
if (VL_LIKELY(funcnum < scopep->m_funcnumMax)) {
|
||||
// m_callbacksp must be declared, as Max'es are > 0
|
||||
@@ -670,7 +727,7 @@ public:
|
||||
/// Return debug level
|
||||
/// When multithreaded this may not immediately react to another thread
|
||||
/// changing the level (no mutex)
|
||||
static inline int debug() VL_MT_SAFE { return s_debug; }
|
||||
static int debug() VL_MT_SAFE { return s_debug; }
|
||||
#else
|
||||
/// Return constant 0 debug level, so C++'s optimizer rips up
|
||||
static constexpr int debug() VL_PURE { return 0; }
|
||||
@@ -830,7 +887,6 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
public:
|
||||
// METHODS - INTERNAL USE ONLY (but public due to what uses it)
|
||||
// Internal: Create a new module name by concatenating two strings
|
||||
// Returns pointer to thread-local static data (overwritten on next call)
|
||||
@@ -877,8 +933,8 @@ private:
|
||||
#endif
|
||||
};
|
||||
|
||||
inline void VerilatedContext::debug(int val) VL_MT_SAFE { Verilated::debug(val); }
|
||||
inline int VerilatedContext::debug() VL_MT_SAFE { return Verilated::debug(); }
|
||||
void VerilatedContext::debug(int val) VL_MT_SAFE { Verilated::debug(val); }
|
||||
int VerilatedContext::debug() VL_MT_SAFE { return Verilated::debug(); }
|
||||
|
||||
//=========================================================================
|
||||
// Data Types
|
||||
|
||||
+1
-21
@@ -142,26 +142,6 @@ ifneq ($(VM_THREADS),0)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(VM_TRACE_THREADS),0)
|
||||
ifneq ($(VM_TRACE_THREADS),)
|
||||
ifeq ($(findstring -DVL_THREADED,$(CPPFLAGS)),)
|
||||
$(error VM_TRACE_THREADS requires VM_THREADS)
|
||||
endif
|
||||
CPPFLAGS += -DVL_TRACE_THREADED
|
||||
VK_C11=1
|
||||
VK_LIBS_THREADED=1
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
ifneq ($(VM_TRACE_FST_WRITER_THREAD),0)
|
||||
ifneq ($(VM_TRACE_FST_WRITER_THREAD),)
|
||||
CPPFLAGS += -DVL_TRACE_FST_WRITER_THREAD
|
||||
VK_C11=1
|
||||
VK_LIBS_THREADED=1
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(VK_C11),0)
|
||||
ifneq ($(VK_C11),)
|
||||
# Need C++11 at least, so always default to newest
|
||||
@@ -187,7 +167,7 @@ VM_SLOW += $(VM_CLASSES_SLOW) $(VM_SUPPORT_SLOW)
|
||||
VK_FAST_OBJS = $(addsuffix .o, $(VM_FAST))
|
||||
VK_SLOW_OBJS = $(addsuffix .o, $(VM_SLOW))
|
||||
|
||||
VK_USER_OBJS = $(addsuffix .o, $(VM_USER_CLASSES))
|
||||
VK_USER_OBJS = $(addsuffix .o, $(VM_USER_CLASSES))
|
||||
|
||||
# Note VM_GLOBAL_FAST and VM_GLOBAL_SLOW holds the files required from the
|
||||
# run-time library. In practice everything is actually in VM_GLOBAL_FAST,
|
||||
|
||||
@@ -22,8 +22,10 @@
|
||||
//=============================================================================
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h"
|
||||
|
||||
#include "verilated_cov.h"
|
||||
|
||||
#include "verilated.h"
|
||||
#include "verilated_cov_key.h"
|
||||
|
||||
#include <deque>
|
||||
@@ -69,22 +71,23 @@ public: // But only local to this file
|
||||
// This isn't in the header file for auto-magic conversion because it
|
||||
// inlines to too much code and makes compilation too slow.
|
||||
|
||||
template <class T> class VerilatedCoverItemSpec final : public VerilatedCovImpItem {
|
||||
template <class T>
|
||||
class VerilatedCoverItemSpec final : public VerilatedCovImpItem {
|
||||
private:
|
||||
// MEMBERS
|
||||
T* m_countp; // Count value
|
||||
public:
|
||||
// METHODS
|
||||
// cppcheck-suppress truncLongCastReturn
|
||||
virtual uint64_t count() const override { return *m_countp; }
|
||||
virtual void zero() const override { *m_countp = 0; }
|
||||
uint64_t count() const override { return *m_countp; }
|
||||
void zero() const override { *m_countp = 0; }
|
||||
// CONSTRUCTORS
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
explicit VerilatedCoverItemSpec(T* countp)
|
||||
: m_countp{countp} {
|
||||
*m_countp = 0;
|
||||
}
|
||||
virtual ~VerilatedCoverItemSpec() override = default;
|
||||
~VerilatedCoverItemSpec() override = default;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
@@ -122,7 +125,7 @@ public:
|
||||
|
||||
protected:
|
||||
friend class VerilatedCovContext;
|
||||
virtual ~VerilatedCovImp() override { clearGuts(); }
|
||||
~VerilatedCovImp() override { clearGuts(); }
|
||||
|
||||
private:
|
||||
// PRIVATE METHODS
|
||||
@@ -205,7 +208,7 @@ private:
|
||||
// Forward to . so we have a whole word
|
||||
const std::string suffix = *bpost ? std::string{bpost + 1} : "";
|
||||
|
||||
const std::string out = prefix + "*" + suffix;
|
||||
std::string out = prefix + "*" + suffix;
|
||||
|
||||
// cout << "\nch pre="<<prefix<<" s="<<suffix<<"\nch a="<<old<<"\nch b="<<add
|
||||
// <<"\ncho="<<out<<endl;
|
||||
@@ -511,8 +514,10 @@ VerilatedCovContext* VerilatedCov::threadCovp() VL_MT_SAFE {
|
||||
|
||||
VerilatedCovContext* VerilatedContext::coveragep() VL_MT_SAFE {
|
||||
static VerilatedMutex s_mutex;
|
||||
// cppcheck-suppress identicalInnerCondition
|
||||
if (VL_UNLIKELY(!m_coveragep)) {
|
||||
const VerilatedLockGuard lock{s_mutex};
|
||||
// cppcheck-suppress identicalInnerCondition
|
||||
if (VL_LIKELY(!m_coveragep)) { // Not redundant, prevents race
|
||||
m_coveragep.reset(new VerilatedCovImp);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#define VERILATOR_VERILATED_COV_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated.h"
|
||||
|
||||
#include <iostream>
|
||||
@@ -88,7 +89,8 @@ class VerilatedCovImp;
|
||||
//=============================================================================
|
||||
// Convert VL_COVER_INSERT value arguments to strings, is \internal
|
||||
|
||||
template <class T> std::string vlCovCvtToStr(const T& t) VL_PURE {
|
||||
template <class T>
|
||||
std::string vlCovCvtToStr(const T& t) VL_PURE {
|
||||
std::ostringstream os;
|
||||
os << t;
|
||||
return os.str();
|
||||
@@ -120,7 +122,8 @@ public:
|
||||
/// Zero coverage points
|
||||
void zero() VL_MT_SAFE;
|
||||
|
||||
public: // But Internal use only
|
||||
// METHODS - public but Internal use only
|
||||
|
||||
// Insert a coverage item
|
||||
// We accept from 1-30 key/value pairs, all as strings.
|
||||
// Call _insert1, followed by _insert2 and _insert3
|
||||
@@ -158,7 +161,7 @@ protected:
|
||||
// CONSTRUCTORS
|
||||
// Internal: Only made as part of VerilatedCovImp
|
||||
VerilatedCovContext() = default;
|
||||
virtual ~VerilatedCovContext() = default;
|
||||
~VerilatedCovContext() override = default;
|
||||
|
||||
// METHODS
|
||||
// Internal: access to implementation class
|
||||
|
||||
@@ -28,7 +28,9 @@
|
||||
#define VERILATOR_VERILATED_DPI_CPP_
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated_dpi.h"
|
||||
|
||||
#include "verilated_imp.h"
|
||||
|
||||
// On MSVC++ we need svdpi.h to declare exports, not imports
|
||||
@@ -179,7 +181,7 @@ void svPutPartselLogic(svLogicVecVal* dp, const svLogicVecVal s, int lbit, int w
|
||||
//======================================================================
|
||||
// Open array internals
|
||||
|
||||
static inline const VerilatedDpiOpenVar* _vl_openhandle_varp(const svOpenArrayHandle h) {
|
||||
static const VerilatedDpiOpenVar* _vl_openhandle_varp(const svOpenArrayHandle h) {
|
||||
if (VL_UNLIKELY(!h)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"%%Error: DPI svOpenArrayHandle function called with nullptr handle");
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#define VERILATOR_VERILATED_DPI_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated.h" // Also presumably included by caller
|
||||
#include "verilated_sym_props.h"
|
||||
|
||||
|
||||
+25
-15
@@ -28,7 +28,7 @@
|
||||
#include "verilated_fst_c.h"
|
||||
|
||||
// GTKWave configuration
|
||||
#ifdef VL_TRACE_FST_WRITER_THREAD
|
||||
#ifdef VL_THREADED
|
||||
# define HAVE_LIBPTHREAD
|
||||
# define FST_WRITER_PARALLEL
|
||||
#endif
|
||||
@@ -92,8 +92,7 @@ static_assert(static_cast<int>(FST_ST_VCD_PROGRAM) == static_cast<int>(VLT_TRACE
|
||||
//=============================================================================
|
||||
// VerilatedFst
|
||||
|
||||
VerilatedFst::VerilatedFst(void* fst)
|
||||
: m_fst{fst} {}
|
||||
VerilatedFst::VerilatedFst(void* /*fst*/) {}
|
||||
|
||||
VerilatedFst::~VerilatedFst() {
|
||||
if (m_fst) fstWriterClose(m_fst);
|
||||
@@ -106,9 +105,7 @@ void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
m_fst = fstWriterCreate(filename, 1);
|
||||
fstWriterSetPackType(m_fst, FST_WR_PT_LZ4);
|
||||
fstWriterSetTimescaleFromString(m_fst, timeResStr().c_str()); // lintok-begin-on-ref
|
||||
#ifdef VL_TRACE_FST_WRITER_THREAD
|
||||
fstWriterSetParallelMode(m_fst, 1);
|
||||
#endif
|
||||
if (m_useFstWriterThread) fstWriterSetParallelMode(m_fst, 1);
|
||||
fullDump(true); // First dump must be full for fst
|
||||
|
||||
m_curScope.clear();
|
||||
@@ -250,23 +247,36 @@ void VerilatedFst::declDouble(uint32_t code, const char* name, int dtypenum, fst
|
||||
//=============================================================================
|
||||
// Get/commit trace buffer
|
||||
|
||||
VerilatedFstBuffer* VerilatedFst::getTraceBuffer() { return new VerilatedFstBuffer{*this}; }
|
||||
VerilatedFst::Buffer* VerilatedFst::getTraceBuffer() {
|
||||
#ifdef VL_THREADED
|
||||
if (offload()) return new OffloadBuffer{*this};
|
||||
#endif
|
||||
return new Buffer{*this};
|
||||
}
|
||||
|
||||
void VerilatedFst::commitTraceBuffer(VerilatedFstBuffer* bufp) {
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
if (bufp->m_offloadBufferWritep) {
|
||||
m_offloadBufferWritep = bufp->m_offloadBufferWritep;
|
||||
return; // Buffer will be deleted by the offload thread
|
||||
void VerilatedFst::commitTraceBuffer(VerilatedFst::Buffer* bufp) {
|
||||
#ifdef VL_THREADED
|
||||
if (offload()) {
|
||||
OffloadBuffer* const offloadBufferp = static_cast<OffloadBuffer*>(bufp);
|
||||
if (offloadBufferp->m_offloadBufferWritep) {
|
||||
m_offloadBufferWritep = offloadBufferp->m_offloadBufferWritep;
|
||||
return; // Buffer will be deleted by the offload thread
|
||||
}
|
||||
}
|
||||
#endif
|
||||
delete bufp;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFstBuffer implementation
|
||||
// Configure
|
||||
|
||||
VerilatedFstBuffer::VerilatedFstBuffer(VerilatedFst& owner)
|
||||
: VerilatedTraceBuffer<VerilatedFst, VerilatedFstBuffer>{owner} {}
|
||||
void VerilatedFst::configure(const VerilatedTraceConfig& config) {
|
||||
// If at least one model requests the FST writer thread, then use it
|
||||
m_useFstWriterThread |= config.m_useFstWriterThread;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFstBuffer implementation
|
||||
|
||||
//=============================================================================
|
||||
// Trace rendering primitives
|
||||
|
||||
+40
-25
@@ -43,18 +43,20 @@ public:
|
||||
using Super = VerilatedTrace<VerilatedFst, VerilatedFstBuffer>;
|
||||
|
||||
private:
|
||||
friend Buffer; // Give the buffer access to the private bits
|
||||
friend VerilatedFstBuffer; // Give the buffer access to the private bits
|
||||
|
||||
//=========================================================================
|
||||
// FST specific internals
|
||||
|
||||
void* m_fst;
|
||||
void* m_fst = nullptr;
|
||||
std::map<uint32_t, fstHandle> m_code2symbol;
|
||||
std::map<int, fstEnumHandle> m_local2fstdtype;
|
||||
std::list<std::string> m_curScope;
|
||||
fstHandle* m_symbolp = nullptr; // same as m_code2symbol, but as an array
|
||||
char* m_strbuf = nullptr; // String buffer long enough to hold maxBits() chars
|
||||
|
||||
bool m_useFstWriterThread = false; // Whether to use the separate FST writer thread
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedFst);
|
||||
void declare(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
@@ -65,15 +67,18 @@ protected:
|
||||
// Implementation of VerilatedTrace interface
|
||||
|
||||
// Called when the trace moves forward to a new time point
|
||||
virtual void emitTimeChange(uint64_t timeui) override;
|
||||
void emitTimeChange(uint64_t timeui) override;
|
||||
|
||||
// Hooks called from VerilatedTrace
|
||||
virtual bool preFullDump() override { return isOpen(); }
|
||||
virtual bool preChangeDump() override { return isOpen(); }
|
||||
bool preFullDump() override { return isOpen(); }
|
||||
bool preChangeDump() override { return isOpen(); }
|
||||
|
||||
// Trace buffer management
|
||||
virtual VerilatedFstBuffer* getTraceBuffer() override;
|
||||
virtual void commitTraceBuffer(VerilatedFstBuffer*) override;
|
||||
Buffer* getTraceBuffer() override;
|
||||
void commitTraceBuffer(Buffer*) override;
|
||||
|
||||
// Configure sub-class
|
||||
void configure(const VerilatedTraceConfig&) override;
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
@@ -113,21 +118,31 @@ public:
|
||||
|
||||
#ifndef DOXYGEN
|
||||
// Declare specialization here as it's used in VerilatedFstC just below
|
||||
template <> void VerilatedFst::Super::dump(uint64_t time);
|
||||
template <> void VerilatedFst::Super::set_time_unit(const char* unitp);
|
||||
template <> void VerilatedFst::Super::set_time_unit(const std::string& unit);
|
||||
template <> void VerilatedFst::Super::set_time_resolution(const char* unitp);
|
||||
template <> void VerilatedFst::Super::set_time_resolution(const std::string& unit);
|
||||
template <> void VerilatedFst::Super::dumpvars(int level, const std::string& hier);
|
||||
template <>
|
||||
void VerilatedFst::Super::dump(uint64_t time);
|
||||
template <>
|
||||
void VerilatedFst::Super::set_time_unit(const char* unitp);
|
||||
template <>
|
||||
void VerilatedFst::Super::set_time_unit(const std::string& unit);
|
||||
template <>
|
||||
void VerilatedFst::Super::set_time_resolution(const char* unitp);
|
||||
template <>
|
||||
void VerilatedFst::Super::set_time_resolution(const std::string& unit);
|
||||
template <>
|
||||
void VerilatedFst::Super::dumpvars(int level, const std::string& hier);
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFstBuffer
|
||||
|
||||
class VerilatedFstBuffer final : public VerilatedTraceBuffer<VerilatedFst, VerilatedFstBuffer> {
|
||||
class VerilatedFstBuffer VL_NOT_FINAL {
|
||||
// Give the trace file access to the private bits
|
||||
friend VerilatedFst;
|
||||
friend VerilatedFst::Super;
|
||||
friend VerilatedFst::Buffer;
|
||||
friend VerilatedFst::OffloadBuffer;
|
||||
|
||||
VerilatedFst& m_owner; // Trace file owning this buffer. Required by subclasses.
|
||||
|
||||
// The FST file handle
|
||||
void* const m_fst = m_owner.m_fst;
|
||||
@@ -136,23 +151,23 @@ class VerilatedFstBuffer final : public VerilatedTraceBuffer<VerilatedFst, Veril
|
||||
// String buffer long enough to hold maxBits() chars
|
||||
char* const m_strbuf = m_owner.m_strbuf;
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
explicit VerilatedFstBuffer(VerilatedFst& owner);
|
||||
~VerilatedFstBuffer() = default;
|
||||
explicit VerilatedFstBuffer(VerilatedFst& owner)
|
||||
: m_owner{owner} {}
|
||||
virtual ~VerilatedFstBuffer() = default;
|
||||
|
||||
//=========================================================================
|
||||
// Implementation of VerilatedTraceBuffer interface
|
||||
|
||||
// Implementations of duck-typed methods for VerilatedTraceBuffer. These are
|
||||
// called from only one place (the full* methods), so always inline them.
|
||||
VL_ATTR_ALWINLINE inline void emitBit(uint32_t code, CData newval);
|
||||
VL_ATTR_ALWINLINE inline void emitCData(uint32_t code, CData newval, int bits);
|
||||
VL_ATTR_ALWINLINE inline void emitSData(uint32_t code, SData newval, int bits);
|
||||
VL_ATTR_ALWINLINE inline void emitIData(uint32_t code, IData newval, int bits);
|
||||
VL_ATTR_ALWINLINE inline void emitQData(uint32_t code, QData newval, int bits);
|
||||
VL_ATTR_ALWINLINE inline void emitWData(uint32_t code, const WData* newvalp, int bits);
|
||||
VL_ATTR_ALWINLINE inline void emitDouble(uint32_t code, double newval);
|
||||
VL_ATTR_ALWINLINE void emitBit(uint32_t code, CData newval);
|
||||
VL_ATTR_ALWINLINE void emitCData(uint32_t code, CData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitIData(uint32_t code, IData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitQData(uint32_t code, QData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitWData(uint32_t code, const WData* newvalp, int bits);
|
||||
VL_ATTR_ALWINLINE void emitDouble(uint32_t code, double newval);
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
@@ -217,7 +232,7 @@ public:
|
||||
}
|
||||
|
||||
// Internal class access
|
||||
inline VerilatedFst* spTrace() { return &m_sptrace; }
|
||||
VerilatedFst* spTrace() { return &m_sptrace; }
|
||||
};
|
||||
|
||||
#endif // guard
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
//=============================================================================
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated_fst_sc.h"
|
||||
|
||||
//======================================================================
|
||||
@@ -30,7 +31,7 @@
|
||||
void VerilatedFstSc::open(const char* filename) {
|
||||
if (!sc_core::sc_get_curr_simcontext()->elaboration_done()) {
|
||||
vl_fatal(__FILE__, __LINE__, "VerilatedFstSc",
|
||||
("%Error: VerilatedFstSc::open(\"" + std::string(filename)
|
||||
("%Error: VerilatedFstSc::open(\"" + std::string{filename}
|
||||
+ "\") is called before sc_core::sc_start(). "
|
||||
"Run sc_core::sc_start(sc_core::SC_ZERO_TIME) before opening a wave file.")
|
||||
.c_str());
|
||||
@@ -63,9 +64,6 @@ void VerilatedFstSc::trace(const unsigned int&, const std::string&, const char**
|
||||
DECL_TRACE_METHOD_B( unsigned short )
|
||||
DECL_TRACE_METHOD_B( unsigned int )
|
||||
DECL_TRACE_METHOD_B( unsigned long )
|
||||
#ifdef SYSTEMC_64BIT_PATCHES
|
||||
DECL_TRACE_METHOD_B( unsigned long long)
|
||||
#endif
|
||||
DECL_TRACE_METHOD_B( char )
|
||||
DECL_TRACE_METHOD_B( short )
|
||||
DECL_TRACE_METHOD_B( int )
|
||||
|
||||
+12
-13
@@ -23,8 +23,9 @@
|
||||
#define _VERILATED_FST_SC_H_ 1
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated_sc.h"
|
||||
|
||||
#include "verilated_fst_c.h"
|
||||
#include "verilated_sc.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -51,34 +52,35 @@ public:
|
||||
}
|
||||
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
|
||||
}
|
||||
virtual ~VerilatedFstSc() { close(); }
|
||||
~VerilatedFstSc() override { close(); }
|
||||
|
||||
// METHODS
|
||||
/// Called by SystemC simulate()
|
||||
virtual void cycle(bool delta_cycle) {
|
||||
void cycle(bool delta_cycle) override {
|
||||
if (!delta_cycle) { this->dump(sc_time_stamp().to_double()); }
|
||||
}
|
||||
|
||||
// Override VerilatedFstC. Must be called after starting simulation.
|
||||
virtual void open(const char* filename) /*override*/ VL_MT_SAFE;
|
||||
// Note: this is not a virtual function in the base class, so no 'override'
|
||||
virtual void open(const char* filename) VL_MT_SAFE;
|
||||
|
||||
private:
|
||||
/// Fake outs for linker
|
||||
|
||||
#ifdef NC_SYSTEMC
|
||||
// Cadence Incisive has these as abstract functions so we must create them
|
||||
virtual void set_time_unit(int exponent10_seconds) {} // deprecated
|
||||
void set_time_unit(int exponent10_seconds) override {} // deprecated
|
||||
#endif
|
||||
virtual void set_time_unit(double v, sc_time_unit tu) {} // LCOV_EXCL_LINE
|
||||
void set_time_unit(double v, sc_time_unit tu) override {} // LCOV_EXCL_LINE
|
||||
|
||||
//--------------------------------------------------
|
||||
// SystemC 2.1.v1
|
||||
#define DECL_TRACE_METHOD_A(tp) virtual void trace(const tp& object, const std::string& name);
|
||||
#define DECL_TRACE_METHOD_A(tp) void trace(const tp& object, const std::string& name) override;
|
||||
#define DECL_TRACE_METHOD_B(tp) \
|
||||
virtual void trace(const tp& object, const std::string& name, int width);
|
||||
void trace(const tp& object, const std::string& name, int width) override;
|
||||
|
||||
virtual void write_comment(const std::string&);
|
||||
virtual void trace(const unsigned int&, const std::string&, const char**);
|
||||
void write_comment(const std::string&) override;
|
||||
void trace(const unsigned int&, const std::string&, const char**) override;
|
||||
|
||||
// clang-format off
|
||||
// Formatting matches that of sc_trace.h
|
||||
@@ -95,9 +97,6 @@ private:
|
||||
DECL_TRACE_METHOD_B( unsigned short )
|
||||
DECL_TRACE_METHOD_B( unsigned int )
|
||||
DECL_TRACE_METHOD_B( unsigned long )
|
||||
#ifdef SYSTEMC_64BIT_PATCHES
|
||||
DECL_TRACE_METHOD_B( unsigned long long)
|
||||
#endif
|
||||
DECL_TRACE_METHOD_B( char )
|
||||
DECL_TRACE_METHOD_B( short )
|
||||
DECL_TRACE_METHOD_B( int )
|
||||
|
||||
+27
-28
@@ -146,7 +146,7 @@ extern IData VL_SYSTEM_IW(int lhswords, WDataInP const lhsp);
|
||||
extern IData VL_SYSTEM_IQ(QData lhs);
|
||||
inline IData VL_SYSTEM_II(IData lhs) VL_MT_SAFE { return VL_SYSTEM_IQ(lhs); }
|
||||
|
||||
extern IData VL_TESTPLUSARGS_I(const char* formatp);
|
||||
extern IData VL_TESTPLUSARGS_I(const std::string& format);
|
||||
extern const char* vl_mc_scan_plusargs(const char* prefixp); // PLIish
|
||||
|
||||
//=========================================================================
|
||||
@@ -256,25 +256,7 @@ extern void _vl_debug_print_w(int lbits, WDataInP const iwp);
|
||||
//=========================================================================
|
||||
// Pli macros
|
||||
|
||||
extern int VL_TIME_STR_CONVERT(const char* strp) VL_PURE;
|
||||
|
||||
// These are deprecated and used only to establish the default precision/units.
|
||||
// Use Verilator timescale-override for better control.
|
||||
// clang-format off
|
||||
#ifndef VL_TIME_PRECISION
|
||||
# ifdef VL_TIME_PRECISION_STR
|
||||
# define VL_TIME_PRECISION VL_TIME_STR_CONVERT(VL_STRINGIFY(VL_TIME_PRECISION_STR))
|
||||
# else
|
||||
# define VL_TIME_PRECISION (-12) ///< Timescale default units if not in Verilog - picoseconds
|
||||
# endif
|
||||
#endif
|
||||
#ifndef VL_TIME_UNIT
|
||||
# ifdef VL_TIME_UNIT_STR
|
||||
# define VL_TIME_UNIT VL_TIME_STR_CONVERT(VL_STRINGIFY(VL_TIME_PRECISION_STR))
|
||||
# else
|
||||
# define VL_TIME_UNIT (-12) ///< Timescale default units if not in Verilog - picoseconds
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(SYSTEMC_VERSION)
|
||||
/// Return current simulation time
|
||||
@@ -300,7 +282,7 @@ inline uint64_t vl_time_stamp64() {
|
||||
# endif
|
||||
#endif
|
||||
|
||||
inline uint64_t VerilatedContext::time() const VL_MT_SAFE {
|
||||
uint64_t VerilatedContext::time() const VL_MT_SAFE {
|
||||
// When using non-default context, fastest path is return time
|
||||
if (VL_LIKELY(m_s.m_time)) return m_s.m_time;
|
||||
#if defined(SYSTEMC_VERSION) || (!defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY))
|
||||
@@ -466,10 +448,17 @@ static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE {
|
||||
{ \
|
||||
const int words = VL_WORDS_I(obits); \
|
||||
sc_biguint<(obits)> _butemp = (svar).read(); \
|
||||
for (int i = 0; i < words; ++i) { \
|
||||
int msb = ((i + 1) * VL_IDATASIZE) - 1; \
|
||||
msb = (msb >= (obits)) ? ((obits)-1) : msb; \
|
||||
(owp)[i] = _butemp.range(msb, i * VL_IDATASIZE).to_uint(); \
|
||||
uint32_t* chunk = _butemp.get_raw(); \
|
||||
int32_t lsb = 0; \
|
||||
while (lsb < obits - BITS_PER_DIGIT) { \
|
||||
const uint32_t data = *chunk; \
|
||||
++chunk; \
|
||||
_vl_insert_WI(owp.data(), data, lsb + BITS_PER_DIGIT - 1, lsb); \
|
||||
lsb += BITS_PER_DIGIT; \
|
||||
} \
|
||||
if (lsb < obits) { \
|
||||
const uint32_t msb_data = *chunk; \
|
||||
_vl_insert_WI(owp.data(), msb_data, obits - 1, lsb); \
|
||||
} \
|
||||
(owp)[words - 1] &= VL_MASK_E(obits); \
|
||||
}
|
||||
@@ -510,13 +499,23 @@ static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE {
|
||||
{ (svar).write(rd); }
|
||||
#define VL_ASSIGN_SBQ(obits, svar, rd) \
|
||||
{ (svar).write(rd); }
|
||||
#define VL_SC_BITS_PER_DIGIT 30 // This comes from sc_nbdefs.h BITS_PER_DIGIT
|
||||
#define VL_ASSIGN_SBW(obits, svar, rwp) \
|
||||
{ \
|
||||
sc_biguint<(obits)> _butemp; \
|
||||
for (int i = 0; i < VL_WORDS_I(obits); ++i) { \
|
||||
int msb = ((i + 1) * VL_IDATASIZE) - 1; \
|
||||
msb = (msb >= (obits)) ? ((obits)-1) : msb; \
|
||||
_butemp.range(msb, i* VL_IDATASIZE) = (rwp)[i]; \
|
||||
int32_t lsb = 0; \
|
||||
uint32_t* chunk = _butemp.get_raw(); \
|
||||
while (lsb + VL_SC_BITS_PER_DIGIT < (obits)) { \
|
||||
static_assert(std::is_same<IData, EData>::value, "IData and EData missmatch"); \
|
||||
const uint32_t data = VL_SEL_IWII(lsb + VL_SC_BITS_PER_DIGIT + 1, (rwp).data(), lsb, \
|
||||
VL_SC_BITS_PER_DIGIT); \
|
||||
*chunk = data & VL_MASK_E(VL_SC_BITS_PER_DIGIT); \
|
||||
++chunk; \
|
||||
lsb += VL_SC_BITS_PER_DIGIT; \
|
||||
} \
|
||||
if (lsb < (obits)) { \
|
||||
const uint32_t msb_data = VL_SEL_IWII((obits) + 1, (rwp).data(), lsb, (obits)-lsb); \
|
||||
*chunk = msb_data & VL_MASK_E((obits)-lsb); \
|
||||
} \
|
||||
(svar).write(_butemp); \
|
||||
}
|
||||
|
||||
+19
-24
@@ -143,11 +143,10 @@ class VerilatedThreadMsgQueue final {
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VerilatedThreadMsgQueue() {}
|
||||
~VerilatedThreadMsgQueue() {
|
||||
// The only call of this with a non-empty queue is a fatal error.
|
||||
// So this does not flush the queue, as the destination queue is not known to this class.
|
||||
}
|
||||
VerilatedThreadMsgQueue() = default;
|
||||
~VerilatedThreadMsgQueue() = default;
|
||||
// The only call of destructor with a non-empty queue is a fatal error.
|
||||
// So this does not flush the queue, as the destination queue is not known to this class.
|
||||
|
||||
private:
|
||||
VL_UNCOPYABLE(VerilatedThreadMsgQueue);
|
||||
@@ -188,11 +187,11 @@ class VerilatedFpList final {
|
||||
|
||||
public:
|
||||
using const_iterator = FILE* const*;
|
||||
explicit VerilatedFpList() {}
|
||||
explicit VerilatedFpList() = default;
|
||||
const_iterator begin() const { return m_fp; }
|
||||
const_iterator end() const { return m_fp + m_sz; }
|
||||
std::size_t size() const { return m_sz; }
|
||||
std::size_t capacity() const { return 31; }
|
||||
static std::size_t capacity() { return 31; }
|
||||
void push_back(FILE* fd) {
|
||||
if (VL_LIKELY(size() < capacity())) m_fp[m_sz++] = fd;
|
||||
}
|
||||
@@ -236,12 +235,11 @@ class VerilatedContextImp final : VerilatedContext {
|
||||
return s_s;
|
||||
}
|
||||
|
||||
private:
|
||||
public: // But only for verilated*.cpp
|
||||
// CONSTRUCTORS - no data can live here, use only VerilatedContext
|
||||
VerilatedContextImp() = delete;
|
||||
~VerilatedContextImp() = delete;
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - extending into VerilatedContext, call via impp()->
|
||||
|
||||
// Random seed handling
|
||||
@@ -272,13 +270,12 @@ public: // But only for verilated*.cpp
|
||||
std::string argPlusMatch(const char* prefixp) VL_MT_SAFE_EXCLUDES(m_argMutex);
|
||||
std::pair<int, char**> argc_argv() VL_MT_SAFE_EXCLUDES(m_argMutex);
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - scope name
|
||||
// METHODS - scope name - INTERNAL only for verilated*.cpp
|
||||
void scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE;
|
||||
void scopeErase(const VerilatedScope* scopep) VL_MT_SAFE;
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - file IO
|
||||
// METHODS - file IO - INTERNAL only for verilated*.cpp
|
||||
|
||||
IData fdNewMcd(const char* filenamep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
if (m_fdFreeMct.empty()) return 0;
|
||||
@@ -298,7 +295,7 @@ public: // But only for verilated*.cpp
|
||||
const std::size_t start = std::max<std::size_t>(31UL + 1UL + 3UL, m_fdps.size());
|
||||
const std::size_t excess = 10;
|
||||
m_fdps.resize(start + excess);
|
||||
std::fill(m_fdps.begin() + start, m_fdps.end(), (FILE*)0);
|
||||
std::fill(m_fdps.begin() + start, m_fdps.end(), static_cast<FILE*>(nullptr));
|
||||
m_fdFree.resize(excess);
|
||||
for (std::size_t i = 0, id = start; i < m_fdFree.size(); ++i, ++id) {
|
||||
m_fdFree[i] = id;
|
||||
@@ -344,7 +341,7 @@ public: // But only for verilated*.cpp
|
||||
if (VL_UNLIKELY(idx <= 2)) return; // stdout/stdin/stderr
|
||||
if (VL_UNLIKELY(!m_fdps[idx])) return; // Already free
|
||||
std::fclose(m_fdps[idx]);
|
||||
m_fdps[idx] = (FILE*)0;
|
||||
m_fdps[idx] = nullptr;
|
||||
m_fdFree.push_back(idx);
|
||||
} else {
|
||||
// MCD case
|
||||
@@ -359,7 +356,7 @@ public: // But only for verilated*.cpp
|
||||
}
|
||||
}
|
||||
}
|
||||
inline FILE* fdToFp(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
FILE* fdToFp(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
const VerilatedFpList fdlist = fdToFpList(fdi);
|
||||
if (VL_UNLIKELY(fdlist.size() != 1)) return nullptr;
|
||||
@@ -464,13 +461,12 @@ public:
|
||||
// METHODS - debug
|
||||
static void versionDump() VL_MT_SAFE;
|
||||
|
||||
public:
|
||||
// METHODS - user scope tracking
|
||||
// We implement this as a single large map instead of one map per scope.
|
||||
// There's often many more scopes than userdata's and thus having a ~48byte
|
||||
// per map overhead * N scopes would take much more space and cache thrashing.
|
||||
// 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 {
|
||||
static void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{s().m_userMapMutex};
|
||||
const auto it = s().m_userMap.find(std::make_pair(scopep, userKey));
|
||||
if (it != s().m_userMap.end()) {
|
||||
@@ -479,14 +475,15 @@ public:
|
||||
s().m_userMap.emplace(std::make_pair(scopep, userKey), userData);
|
||||
}
|
||||
}
|
||||
static inline void* userFind(const void* scopep, void* userKey) VL_MT_SAFE {
|
||||
static void* userFind(const void* scopep, void* userKey) VL_MT_SAFE {
|
||||
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;
|
||||
}
|
||||
|
||||
public: // But only for verilated.cpp
|
||||
// METHODS - 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 only iterate.
|
||||
@@ -512,8 +509,7 @@ public: // But only for verilated.cpp
|
||||
}
|
||||
}
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - hierarchy
|
||||
// METHODS - hierarchy - only for verilated*.cpp
|
||||
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};
|
||||
@@ -534,8 +530,7 @@ public: // But only for verilated*.cpp
|
||||
return &s().m_hierMap;
|
||||
}
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - export names
|
||||
// METHODS - export names - only for verilated*.cpp
|
||||
|
||||
// Each function prototype is converted to a function number which we
|
||||
// then use to index a 2D table also indexed by scope number, because we
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
//=============================================================================
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated_profiler.h"
|
||||
|
||||
#if VL_THREADED
|
||||
@@ -60,54 +61,87 @@ uint16_t VlExecutionRecord::getcpu() {
|
||||
//=============================================================================
|
||||
// VlExecutionProfiler implementation
|
||||
|
||||
template <size_t N> static size_t roundUptoMultipleOf(size_t value) {
|
||||
template <size_t N>
|
||||
static size_t roundUptoMultipleOf(size_t value) {
|
||||
static_assert((N & (N - 1)) == 0, "'N' must be a power of 2");
|
||||
size_t mask = N - 1;
|
||||
return (value + mask) & ~mask;
|
||||
}
|
||||
|
||||
VlExecutionProfiler::VlExecutionProfiler() {
|
||||
VlExecutionProfiler::VlExecutionProfiler(VerilatedContext& context)
|
||||
: m_context{context} {
|
||||
// Setup profiling on main thread
|
||||
setupThread(0);
|
||||
}
|
||||
|
||||
void VlExecutionProfiler::configure(const VerilatedContext& context) {
|
||||
void VlExecutionProfiler::configure() {
|
||||
|
||||
if (VL_UNLIKELY(m_enabled)) {
|
||||
--m_windowCount;
|
||||
if (VL_UNLIKELY(m_windowCount == context.profExecWindow())) {
|
||||
if (VL_UNLIKELY(m_windowCount == m_context.profExecWindow())) {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("+ profile start collection\n"););
|
||||
clear(); // Clear the profile after the cache warm-up cycles.
|
||||
m_tickBegin = VL_CPU_TICK();
|
||||
} else if (VL_UNLIKELY(m_windowCount == 0)) {
|
||||
const uint64_t tickEnd = VL_CPU_TICK();
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("+ profile end\n"););
|
||||
const std::string& fileName = context.profExecFilename();
|
||||
const std::string& fileName = m_context.profExecFilename();
|
||||
dump(fileName.c_str(), tickEnd);
|
||||
m_enabled = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t startReq = context.profExecStart() + 1; // + 1, so we can start at time 0
|
||||
const uint64_t startReq = m_context.profExecStart() + 1; // + 1, so we can start at time 0
|
||||
|
||||
if (VL_UNLIKELY(m_lastStartReq < startReq && VL_TIME_Q() >= context.profExecStart())) {
|
||||
if (VL_UNLIKELY(m_lastStartReq < startReq && VL_TIME_Q() >= m_context.profExecStart())) {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("+ profile start warmup\n"););
|
||||
VL_DEBUG_IF(assert(m_windowCount == 0););
|
||||
m_enabled = true;
|
||||
m_windowCount = context.profExecWindow() * 2;
|
||||
m_windowCount = m_context.profExecWindow() * 2;
|
||||
m_lastStartReq = startReq;
|
||||
}
|
||||
}
|
||||
|
||||
VerilatedVirtualBase* VlExecutionProfiler::construct(VerilatedContext& context) {
|
||||
VlExecutionProfiler* const selfp = new VlExecutionProfiler{context};
|
||||
#if VL_THREADED
|
||||
if (VlThreadPool* const threadPoolp = static_cast<VlThreadPool*>(context.threadPoolp())) {
|
||||
for (int i = 0; i < threadPoolp->numThreads(); ++i) {
|
||||
// Data to pass to worker thread initialization
|
||||
struct Data {
|
||||
VlExecutionProfiler* const selfp;
|
||||
const uint32_t threadId;
|
||||
} data{selfp, static_cast<uint32_t>(i + 1)};
|
||||
|
||||
// Initialize worker thread
|
||||
threadPoolp->workerp(i)->addTask(
|
||||
[](void* userp, bool) {
|
||||
Data* const datap = static_cast<Data*>(userp);
|
||||
datap->selfp->setupThread(datap->threadId);
|
||||
},
|
||||
&data);
|
||||
|
||||
// Wait until initializationis complete
|
||||
threadPoolp->workerp(i)->wait();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return selfp;
|
||||
}
|
||||
|
||||
void VlExecutionProfiler::setupThread(uint32_t threadId) {
|
||||
// Reserve some space in the thread-local profiling buffer, in order to try to avoid malloc
|
||||
// while profiling.
|
||||
t_trace.reserve(RESERVED_TRACE_CAPACITY);
|
||||
// Register thread-local buffer in list of all buffers
|
||||
bool exists;
|
||||
{
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
bool exists = !m_traceps.emplace(threadId, &t_trace).second;
|
||||
assert(!exists);
|
||||
exists = !m_traceps.emplace(threadId, &t_trace).second;
|
||||
}
|
||||
if (VL_UNLIKELY(exists)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "multiple initialization of profiler on some thread");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,10 +24,6 @@
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#ifndef VL_PROFILER
|
||||
#error "verilated_profiler.h/cpp expects VL_PROFILER (from --prof-{exec, pgo}"
|
||||
#endif
|
||||
|
||||
#include "verilated.h"
|
||||
|
||||
#include <array>
|
||||
@@ -38,18 +34,18 @@
|
||||
#include <vector>
|
||||
|
||||
class VlExecutionProfiler;
|
||||
class VlThreadPool;
|
||||
|
||||
//=============================================================================
|
||||
// Macros to simplify generated code
|
||||
|
||||
#define VL_EXEC_TRACE_ADD_RECORD(vlSymsp) \
|
||||
if (VL_UNLIKELY((vlSymsp)->__Vm_executionProfiler.enabled())) \
|
||||
(vlSymsp)->__Vm_executionProfiler.addRecord()
|
||||
if (VL_UNLIKELY((vlSymsp)->__Vm_executionProfilerp->enabled())) \
|
||||
(vlSymsp)->__Vm_executionProfilerp->addRecord()
|
||||
|
||||
//=============================================================================
|
||||
// Return high-precision counter for profiling, or 0x0 if not available
|
||||
VL_ATTR_ALWINLINE
|
||||
inline QData VL_CPU_TICK() {
|
||||
VL_ATTR_ALWINLINE QData VL_CPU_TICK() {
|
||||
uint64_t val;
|
||||
VL_GET_CPU_TICK(val);
|
||||
return val;
|
||||
@@ -136,7 +132,7 @@ static_assert(std::is_trivially_destructible<VlExecutionRecord>::value,
|
||||
//=============================================================================
|
||||
// VlExecutionProfiler is for collecting profiling data about model execution
|
||||
|
||||
class VlExecutionProfiler final {
|
||||
class VlExecutionProfiler final : public VerilatedVirtualBase {
|
||||
// CONSTANTS
|
||||
|
||||
// In order to try to avoid dynamic memory allocations during the actual profiling phase,
|
||||
@@ -154,6 +150,7 @@ class VlExecutionProfiler final {
|
||||
using ExecutionTrace = std::vector<VlExecutionRecord>;
|
||||
|
||||
// STATE
|
||||
VerilatedContext& m_context; // The context this profiler is under
|
||||
static VL_THREAD_LOCAL ExecutionTrace t_trace; // thread-local trace buffers
|
||||
mutable VerilatedMutex m_mutex;
|
||||
// Map from thread id to &t_trace of given thread
|
||||
@@ -167,31 +164,36 @@ class VlExecutionProfiler final {
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
VlExecutionProfiler();
|
||||
explicit VlExecutionProfiler(VerilatedContext& context);
|
||||
~VlExecutionProfiler() override = default;
|
||||
|
||||
// METHODS
|
||||
|
||||
// Is profiling enabled
|
||||
inline bool enabled() const { return m_enabled; }
|
||||
bool enabled() const { return m_enabled; }
|
||||
// Append a trace record to the trace buffer of the current thread
|
||||
inline VlExecutionRecord& addRecord() {
|
||||
static VlExecutionRecord& addRecord() {
|
||||
t_trace.emplace_back();
|
||||
return t_trace.back();
|
||||
}
|
||||
// Configure profiler (called in beginning of 'eval')
|
||||
void configure(const VerilatedContext&);
|
||||
void configure();
|
||||
// Setup profiling on a particular thread;
|
||||
void setupThread(uint32_t threadId);
|
||||
// Clear all profiling data
|
||||
void clear() VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
// Write profiling data into file
|
||||
void dump(const char* filenamep, uint64_t tickEnd) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
|
||||
// Passed to VerilatedContext to create the VlExecutionProfiler profiler instance
|
||||
static VerilatedVirtualBase* construct(VerilatedContext& context);
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlPgoProfiler is for collecting profiling data for PGO
|
||||
|
||||
template <std::size_t T_Entries> class VlPgoProfiler final {
|
||||
template <std::size_t T_Entries>
|
||||
class VlPgoProfiler final {
|
||||
// TYPES
|
||||
struct Record final {
|
||||
const std::string m_name; // Hashed name of mtask/etc
|
||||
|
||||
@@ -24,8 +24,10 @@
|
||||
#define VERILATOR_VERILATED_SAVE_CPP_
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h"
|
||||
|
||||
#include "verilated_save.h"
|
||||
|
||||
#include "verilated.h"
|
||||
#include "verilated_imp.h"
|
||||
|
||||
#include <cerrno>
|
||||
@@ -38,13 +40,13 @@
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifndef O_LARGEFILE // For example on WIN32
|
||||
#ifndef O_LARGEFILE // WIN32 headers omit this
|
||||
# define O_LARGEFILE 0
|
||||
#endif
|
||||
#ifndef O_NONBLOCK
|
||||
#ifndef O_NONBLOCK // WIN32 headers omit this
|
||||
# define O_NONBLOCK 0
|
||||
#endif
|
||||
#ifndef O_CLOEXEC
|
||||
#ifndef O_CLOEXEC // WIN32 headers omit this
|
||||
# define O_CLOEXEC 0
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#define VERILATOR_VERILATED_SAVE_C_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated.h"
|
||||
|
||||
#include <string>
|
||||
@@ -77,7 +78,7 @@ public:
|
||||
virtual void flush() VL_MT_UNSAFE_ONE {}
|
||||
/// Write data to stream
|
||||
VerilatedSerialize& write(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
|
||||
const uint8_t* __restrict dp = (const uint8_t* __restrict)datap;
|
||||
const uint8_t* __restrict dp = static_cast<const uint8_t* __restrict>(datap);
|
||||
while (size) {
|
||||
bufferCheck();
|
||||
size_t blk = size;
|
||||
@@ -194,16 +195,16 @@ public:
|
||||
/// Construct new object
|
||||
VerilatedSave() = default;
|
||||
/// Flush, close and destruct
|
||||
virtual ~VerilatedSave() override { close(); }
|
||||
~VerilatedSave() override { close(); }
|
||||
// METHODS
|
||||
/// Open the file; call isOpen() to see if errors
|
||||
void open(const char* filenamep) VL_MT_UNSAFE_ONE;
|
||||
/// Open the file; call isOpen() to see if errors
|
||||
void open(const std::string& filename) VL_MT_UNSAFE_ONE { open(filename.c_str()); }
|
||||
/// Flush and close the file
|
||||
virtual void close() override VL_MT_UNSAFE_ONE;
|
||||
void close() override VL_MT_UNSAFE_ONE;
|
||||
/// Flush data to file
|
||||
virtual void flush() override VL_MT_UNSAFE_ONE;
|
||||
void flush() override VL_MT_UNSAFE_ONE;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
@@ -221,7 +222,7 @@ public:
|
||||
/// Construct new object
|
||||
VerilatedRestore() = default;
|
||||
/// Flush, close and destruct
|
||||
virtual ~VerilatedRestore() override { close(); }
|
||||
~VerilatedRestore() override { close(); }
|
||||
|
||||
// METHODS
|
||||
/// Open the file; call isOpen() to see if errors
|
||||
@@ -229,9 +230,9 @@ public:
|
||||
/// Open the file; call isOpen() to see if errors
|
||||
void open(const std::string& filename) VL_MT_UNSAFE_ONE { open(filename.c_str()); }
|
||||
/// Close the file
|
||||
virtual void close() override VL_MT_UNSAFE_ONE;
|
||||
virtual void flush() override VL_MT_UNSAFE_ONE {}
|
||||
virtual void fill() override VL_MT_UNSAFE_ONE;
|
||||
void close() override VL_MT_UNSAFE_ONE;
|
||||
void flush() override VL_MT_UNSAFE_ONE {}
|
||||
void fill() override VL_MT_UNSAFE_ONE;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
@@ -287,7 +288,8 @@ inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, std::string& r
|
||||
uint32_t len = 0;
|
||||
os >> len;
|
||||
rhs.resize(len);
|
||||
return os.read((void*)rhs.data(), len);
|
||||
// C cast is required below
|
||||
return os.read((void*)(rhs.data()), len);
|
||||
}
|
||||
VerilatedSerialize& operator<<(VerilatedSerialize& os, VerilatedContext* rhsp);
|
||||
VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VerilatedContext* rhsp);
|
||||
|
||||
@@ -134,7 +134,6 @@ public:
|
||||
initUnpacked(ulims);
|
||||
}
|
||||
|
||||
public:
|
||||
~VerilatedVarProps() = default;
|
||||
// METHODS
|
||||
bool magicOk() const { return m_magic == MAGIC; }
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#define VERILATOR_VERILATED_SYMS_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated.h"
|
||||
#include "verilated_sym_props.h"
|
||||
|
||||
|
||||
@@ -22,11 +22,8 @@
|
||||
//=============================================================================
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated_threads.h"
|
||||
|
||||
#ifdef VL_PROFILER
|
||||
#include "verilated_profiler.h"
|
||||
#endif
|
||||
#include "verilated_threads.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
@@ -51,68 +48,59 @@ VlMTaskVertex::VlMTaskVertex(uint32_t upstreamDepCount)
|
||||
//=============================================================================
|
||||
// VlWorkerThread
|
||||
|
||||
VlWorkerThread::VlWorkerThread(uint32_t threadId, VerilatedContext* contextp,
|
||||
VlExecutionProfiler* profilerp)
|
||||
VlWorkerThread::VlWorkerThread(VerilatedContext* contextp)
|
||||
: m_ready_size{0}
|
||||
, m_exiting{false}
|
||||
, m_cthread{startWorker, this, threadId, profilerp}
|
||||
, m_contextp{contextp} {}
|
||||
, m_cthread{startWorker, this, contextp} {}
|
||||
|
||||
VlWorkerThread::~VlWorkerThread() {
|
||||
m_exiting.store(true, std::memory_order_release);
|
||||
wakeUp();
|
||||
shutdown();
|
||||
// The thread should exit; join it.
|
||||
m_cthread.join();
|
||||
}
|
||||
|
||||
static void shutdownTask(void*, bool) {
|
||||
// Deliberately empty, we use the address of this function as a magic number
|
||||
}
|
||||
|
||||
void VlWorkerThread::shutdown() { addTask(shutdownTask, nullptr); }
|
||||
|
||||
void VlWorkerThread::wait() {
|
||||
// Enqueue a task that sets this flag. Execution is in-order so this ensures completion.
|
||||
std::atomic<bool> flag{false};
|
||||
addTask([](void* flagp, bool) { static_cast<std::atomic<bool>*>(flagp)->store(true); }, &flag);
|
||||
// Spin wait
|
||||
for (unsigned i = 0; i < VL_LOCK_SPINS; ++i) {
|
||||
if (flag.load()) return;
|
||||
VL_CPU_RELAX();
|
||||
}
|
||||
// Yield wait
|
||||
while (!flag.load()) std::this_thread::yield();
|
||||
}
|
||||
|
||||
void VlWorkerThread::workerLoop() {
|
||||
ExecRec work;
|
||||
work.m_fnp = nullptr;
|
||||
|
||||
// Wait for the first task without spinning, in case the thread is never actually used.
|
||||
dequeWork</* SpinWait: */ false>(&work);
|
||||
|
||||
while (true) {
|
||||
if (VL_LIKELY(!work.m_fnp)) dequeWork(&work);
|
||||
|
||||
// Do this here, not above, to avoid a race with the destructor.
|
||||
if (VL_UNLIKELY(m_exiting.load(std::memory_order_acquire))) break;
|
||||
|
||||
if (VL_LIKELY(work.m_fnp)) {
|
||||
work.m_fnp(work.m_selfp, work.m_evenCycle);
|
||||
work.m_fnp = nullptr;
|
||||
}
|
||||
if (VL_UNLIKELY(work.m_fnp == shutdownTask)) break;
|
||||
work.m_fnp(work.m_selfp, work.m_evenCycle);
|
||||
// Wait for next task with spinning.
|
||||
dequeWork</* SpinWait: */ true>(&work);
|
||||
}
|
||||
}
|
||||
|
||||
void VlWorkerThread::startWorker(VlWorkerThread* workerp, uint32_t threadId,
|
||||
VlExecutionProfiler* profilerp) {
|
||||
Verilated::threadContextp(workerp->m_contextp);
|
||||
#ifdef VL_PROFILER
|
||||
// Note: setupThread is not defined without VL_PROFILER, hence the #ifdef. Still, we might
|
||||
// not be profiling execution (e.g.: PGO only), so profilerp might still be nullptr.
|
||||
if (profilerp) profilerp->setupThread(threadId);
|
||||
#endif
|
||||
void VlWorkerThread::startWorker(VlWorkerThread* workerp, VerilatedContext* contextp) {
|
||||
Verilated::threadContextp(contextp);
|
||||
workerp->workerLoop();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VlThreadPool
|
||||
|
||||
VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads,
|
||||
VlExecutionProfiler* profiler) {
|
||||
// --threads N passes nThreads=N-1, as the "main" threads counts as 1
|
||||
++nThreads;
|
||||
const unsigned cpus = std::thread::hardware_concurrency();
|
||||
if (cpus < nThreads) {
|
||||
static int warnedOnce = 0;
|
||||
if (!warnedOnce++) {
|
||||
VL_PRINTF_MT("%%Warning: System has %u CPUs but model Verilated with"
|
||||
" --threads %d; may run slow.\n",
|
||||
cpus, nThreads);
|
||||
}
|
||||
}
|
||||
// Create worker threads
|
||||
for (uint32_t threadId = 1; threadId < nThreads; ++threadId) {
|
||||
m_workers.push_back(new VlWorkerThread{threadId, contextp, profiler});
|
||||
}
|
||||
VlThreadPool::VlThreadPool(VerilatedContext* contextp, unsigned nThreads) {
|
||||
for (unsigned i = 0; i < nThreads; ++i) m_workers.push_back(new VlWorkerThread{contextp});
|
||||
}
|
||||
|
||||
VlThreadPool::~VlThreadPool() {
|
||||
|
||||
+29
-31
@@ -24,6 +24,7 @@
|
||||
#define VERILATOR_VERILATED_THREADS_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated.h" // for VerilatedMutex and clang annotations
|
||||
|
||||
#ifndef VL_THREADED
|
||||
@@ -50,6 +51,9 @@
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
class VlExecutionProfiler;
|
||||
class VlThreadPool;
|
||||
|
||||
// VlMTaskVertex and VlThreadpool will work with multiple model class types.
|
||||
// Since the type is opaque to VlMTaskVertex and VlThreadPool, represent it
|
||||
// as a void* here.
|
||||
@@ -99,7 +103,7 @@ public:
|
||||
// Upstream mtasks must call this when they complete.
|
||||
// Returns true when the current MTaskVertex becomes ready to execute,
|
||||
// false while it's still waiting on more dependencies.
|
||||
inline bool signalUpstreamDone(bool evenCycle) {
|
||||
bool signalUpstreamDone(bool evenCycle) {
|
||||
if (evenCycle) {
|
||||
const uint32_t upstreamDepsDone
|
||||
= 1 + m_upstreamDepsDone.fetch_add(1, std::memory_order_release);
|
||||
@@ -112,11 +116,11 @@ public:
|
||||
return (upstreamDepsDone_prev == 1);
|
||||
}
|
||||
}
|
||||
inline bool areUpstreamDepsDone(bool evenCycle) const {
|
||||
bool areUpstreamDepsDone(bool evenCycle) const {
|
||||
const uint32_t target = evenCycle ? m_upstreamDepCount : 0;
|
||||
return m_upstreamDepsDone.load(std::memory_order_acquire) == target;
|
||||
}
|
||||
inline void waitUntilUpstreamDone(bool evenCycle) const {
|
||||
void waitUntilUpstreamDone(bool evenCycle) const {
|
||||
unsigned ct = 0;
|
||||
while (VL_UNLIKELY(!areUpstreamDepsDone(evenCycle))) {
|
||||
VL_CPU_RELAX();
|
||||
@@ -129,20 +133,14 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class VlExecutionProfiler;
|
||||
class VlThreadPool;
|
||||
|
||||
class VlWorkerThread final {
|
||||
private:
|
||||
// TYPES
|
||||
struct ExecRec {
|
||||
VlExecFnp m_fnp; // Function to execute
|
||||
VlSelfP m_selfp; // Symbol table to execute
|
||||
bool m_evenCycle; // Even/odd for flag alternation
|
||||
ExecRec()
|
||||
: m_fnp{nullptr}
|
||||
, m_selfp{nullptr}
|
||||
, m_evenCycle{false} {}
|
||||
VlExecFnp m_fnp = nullptr; // Function to execute
|
||||
VlSelfP m_selfp = nullptr; // Symbol table to execute
|
||||
bool m_evenCycle = false; // Even/odd for flag alternation
|
||||
ExecRec() = default;
|
||||
ExecRec(VlExecFnp fnp, VlSelfP selfp, bool evenCycle)
|
||||
: m_fnp{fnp}
|
||||
, m_selfp{selfp}
|
||||
@@ -162,26 +160,24 @@ private:
|
||||
// Store the size atomically, so we can spin wait
|
||||
std::atomic<size_t> m_ready_size;
|
||||
|
||||
std::atomic<bool> m_exiting; // Worker thread should exit
|
||||
std::thread m_cthread; // Underlying C++ thread record
|
||||
VerilatedContext* const m_contextp; // Context for spawned thread
|
||||
|
||||
VL_UNCOPYABLE(VlWorkerThread);
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit VlWorkerThread(uint32_t threadId, VerilatedContext* contextp,
|
||||
VlExecutionProfiler* profilerp);
|
||||
explicit VlWorkerThread(VerilatedContext* contextp);
|
||||
~VlWorkerThread();
|
||||
|
||||
// METHODS
|
||||
inline void dequeWork(ExecRec* workp) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
template <bool SpinWait>
|
||||
void dequeWork(ExecRec* workp) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
// Spin for a while, waiting for new data
|
||||
for (int i = 0; i < VL_LOCK_SPINS; ++i) {
|
||||
if (VL_LIKELY(m_ready_size.load(std::memory_order_relaxed))) { //
|
||||
break;
|
||||
if VL_CONSTEXPR_CXX17 (SpinWait) {
|
||||
for (unsigned i = 0; i < VL_LOCK_SPINS; ++i) {
|
||||
if (VL_LIKELY(m_ready_size.load(std::memory_order_relaxed))) break;
|
||||
VL_CPU_RELAX();
|
||||
}
|
||||
VL_CPU_RELAX();
|
||||
}
|
||||
VerilatedLockGuard lock{m_mutex};
|
||||
while (m_ready.empty()) {
|
||||
@@ -195,8 +191,7 @@ public:
|
||||
m_ready.erase(m_ready.begin());
|
||||
m_ready_size.fetch_sub(1, std::memory_order_relaxed);
|
||||
}
|
||||
inline void wakeUp() { addTask(nullptr, nullptr, false); }
|
||||
inline void addTask(VlExecFnp fnp, VlSelfP selfp, bool evenCycle)
|
||||
void addTask(VlExecFnp fnp, VlSelfP selfp, bool evenCycle = false)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
bool notify;
|
||||
{
|
||||
@@ -207,12 +202,15 @@ public:
|
||||
}
|
||||
if (notify) m_cv.notify_one();
|
||||
}
|
||||
|
||||
void shutdown(); // Finish current tasks, then terminate thread
|
||||
void wait(); // Blocks calling thread until all tasks complete in this thread
|
||||
|
||||
void workerLoop();
|
||||
static void startWorker(VlWorkerThread* workerp, uint32_t threadId,
|
||||
VlExecutionProfiler* profilerp);
|
||||
static void startWorker(VlWorkerThread* workerp, VerilatedContext* contextp);
|
||||
};
|
||||
|
||||
class VlThreadPool final {
|
||||
class VlThreadPool final : public VerilatedVirtualBase {
|
||||
// MEMBERS
|
||||
std::vector<VlWorkerThread*> m_workers; // our workers
|
||||
|
||||
@@ -221,12 +219,12 @@ public:
|
||||
// Construct a thread pool with 'nThreads' dedicated threads. The thread
|
||||
// pool will create these threads and make them available to execute tasks
|
||||
// via this->workerp(index)->addTask(...)
|
||||
VlThreadPool(VerilatedContext* contextp, int nThreads, VlExecutionProfiler* profilerp);
|
||||
~VlThreadPool();
|
||||
VlThreadPool(VerilatedContext* contextp, unsigned nThreads);
|
||||
~VlThreadPool() override;
|
||||
|
||||
// METHODS
|
||||
inline int numThreads() const { return m_workers.size(); }
|
||||
inline VlWorkerThread* workerp(int index) {
|
||||
int numThreads() const { return m_workers.size(); }
|
||||
VlWorkerThread* workerp(int index) {
|
||||
assert(index >= 0);
|
||||
assert(index < m_workers.size());
|
||||
return m_workers[index];
|
||||
|
||||
+169
-141
@@ -24,21 +24,6 @@
|
||||
|
||||
// clang-format off
|
||||
|
||||
// In FST mode, VL_TRACE_THREADED enables offloading, but only if we also have
|
||||
// the FST writer thread. This means with --trace-threads 1, we get the FST
|
||||
// writer thread only, and with --trace-threads 2 we get offloading as well
|
||||
#if defined(VL_TRACE_FST_WRITER_THREAD) && defined(VL_TRACE_THREADED)
|
||||
# define VL_TRACE_OFFLOAD
|
||||
#endif
|
||||
// VCD tracing can happen fully in parallel
|
||||
#if defined(VM_TRACE_VCD) && VM_TRACE_VCD && defined(VL_TRACE_THREADED)
|
||||
# define VL_TRACE_PARALLEL
|
||||
#endif
|
||||
|
||||
#if defined(VL_TRACE_PARALLEL) && defined(VL_TRACE_OFFLOAD)
|
||||
# error "Cannot have VL_TRACE_PARALLEL and VL_TRACE_OFFLOAD together"
|
||||
#endif
|
||||
|
||||
#include "verilated.h"
|
||||
#include "verilated_trace_defs.h"
|
||||
|
||||
@@ -47,9 +32,10 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
#ifdef VL_THREADED
|
||||
# include <deque>
|
||||
# include <thread>
|
||||
#endif
|
||||
@@ -57,14 +43,18 @@
|
||||
// clang-format on
|
||||
|
||||
class VlThreadPool;
|
||||
template <class T_Trace, class T_Buffer> class VerilatedTraceBuffer;
|
||||
template <class T_Buffer>
|
||||
class VerilatedTraceBuffer;
|
||||
template <class T_Buffer>
|
||||
class VerilatedTraceOffloadBuffer;
|
||||
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
#ifdef VL_THREADED
|
||||
//=============================================================================
|
||||
// Offloaded tracing
|
||||
|
||||
// A simple synchronized first in first out queue
|
||||
template <class T> class VerilatedThreadQueue final { // LCOV_EXCL_LINE // lcov bug
|
||||
template <class T>
|
||||
class VerilatedThreadQueue final { // LCOV_EXCL_LINE // lcov bug
|
||||
private:
|
||||
mutable VerilatedMutex m_mutex; // Protects m_queue
|
||||
std::condition_variable_any m_cv;
|
||||
@@ -129,27 +119,47 @@ public:
|
||||
};
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedTraceConfig
|
||||
|
||||
// Simple data representing trace configuration required by generated models.
|
||||
class VerilatedTraceConfig final {
|
||||
public:
|
||||
const bool m_useParallel; // Use parallel tracing
|
||||
const bool m_useOffloading; // Offloading trace rendering
|
||||
const bool m_useFstWriterThread; // Use the separate FST writer thread
|
||||
|
||||
VerilatedTraceConfig(bool useParallel, bool useOffloading, bool useFstWriterThread)
|
||||
: m_useParallel{useParallel}
|
||||
, m_useOffloading{useOffloading}
|
||||
, m_useFstWriterThread{useFstWriterThread} {}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedTrace
|
||||
|
||||
// T_Trace is the format specific subclass of VerilatedTrace.
|
||||
// T_Buffer is the format specific subclass of VerilatedTraceBuffer.
|
||||
template <class T_Trace, class T_Buffer> class VerilatedTrace VL_NOT_FINAL {
|
||||
// Give the buffer (both base and derived) access to the private bits
|
||||
friend VerilatedTraceBuffer<T_Trace, T_Buffer>;
|
||||
friend T_Buffer;
|
||||
|
||||
// T_Buffer is the format specific base class of VerilatedTraceBuffer.
|
||||
template <class T_Trace, class T_Buffer>
|
||||
class VerilatedTrace VL_NOT_FINAL {
|
||||
public:
|
||||
using Buffer = T_Buffer;
|
||||
using Buffer = VerilatedTraceBuffer<T_Buffer>;
|
||||
using OffloadBuffer = VerilatedTraceOffloadBuffer<T_Buffer>;
|
||||
|
||||
//=========================================================================
|
||||
// Generic tracing internals
|
||||
|
||||
using initCb_t = void (*)(void*, T_Trace*, uint32_t); // Type of init callbacks
|
||||
using dumpCb_t = void (*)(void*, Buffer*); // Type of dump callbacks
|
||||
using dumpOffloadCb_t = void (*)(void*, OffloadBuffer*); // Type of offload dump callbacks
|
||||
using cleanupCb_t = void (*)(void*, T_Trace*); // Type of cleanup callbacks
|
||||
|
||||
private:
|
||||
// Give the buffer (both base and derived) access to the private bits
|
||||
friend T_Buffer;
|
||||
friend Buffer;
|
||||
friend OffloadBuffer;
|
||||
|
||||
struct CallbackRecord {
|
||||
// Note: would make these fields const, but some old STL implementations
|
||||
// (the one in Ubuntu 14.04 with GCC 4.8.4 in particular) use the
|
||||
@@ -158,6 +168,7 @@ private:
|
||||
union { // The callback
|
||||
initCb_t m_initCb;
|
||||
dumpCb_t m_dumpCb;
|
||||
dumpOffloadCb_t m_dumpOffloadCb;
|
||||
cleanupCb_t m_cleanupCb;
|
||||
};
|
||||
void* m_userp; // The user pointer to pass to the callback (the symbol table)
|
||||
@@ -167,12 +178,18 @@ private:
|
||||
CallbackRecord(dumpCb_t cb, void* userp)
|
||||
: m_dumpCb{cb}
|
||||
, m_userp{userp} {}
|
||||
CallbackRecord(dumpOffloadCb_t cb, void* userp)
|
||||
: m_dumpOffloadCb{cb}
|
||||
, m_userp{userp} {}
|
||||
CallbackRecord(cleanupCb_t cb, void* userp)
|
||||
: m_cleanupCb{cb}
|
||||
, m_userp{userp} {}
|
||||
};
|
||||
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
bool m_offload = false; // Use the offload thread (ignored if !VL_THREADED)
|
||||
bool m_parallel = false; // Use parallel tracing (ignored if !VL_THREADED)
|
||||
|
||||
#ifdef VL_THREADED
|
||||
struct ParallelWorkerData {
|
||||
const dumpCb_t m_cb; // The callback
|
||||
void* const m_userp; // The use pointer to pass to the callback
|
||||
@@ -194,19 +211,17 @@ private:
|
||||
static void parallelWorkerTask(void*, bool);
|
||||
#endif
|
||||
|
||||
using ParallelCallbackMap = std::unordered_map<VlThreadPool*, std::vector<CallbackRecord>>;
|
||||
|
||||
protected:
|
||||
uint32_t* m_sigs_oldvalp = nullptr; // Previous value store
|
||||
EData* m_sigs_enabledp = nullptr; // Bit vector of enabled codes (nullptr = all on)
|
||||
private:
|
||||
uint64_t m_timeLastDump = 0; // Last time we did a dump
|
||||
std::vector<bool> m_sigs_enabledVec; // Staging for m_sigs_enabledp
|
||||
std::vector<CallbackRecord> m_initCbs; // Routines to initialize tracing
|
||||
ParallelCallbackMap m_fullCbs; // Routines to perform full dump
|
||||
ParallelCallbackMap m_chgCbs; // Routines to perform incremental dump
|
||||
std::vector<CallbackRecord> m_fullCbs; // Routines to perform full dump
|
||||
std::vector<CallbackRecord> m_fullOffloadCbs; // Routines to perform offloaded full dump
|
||||
std::vector<CallbackRecord> m_chgCbs; // Routines to perform incremental dump
|
||||
std::vector<CallbackRecord> m_chgOffloadCbs; // Routines to perform offloaded incremental dump
|
||||
std::vector<CallbackRecord> m_cleanupCbs; // Routines to call at the end of dump
|
||||
std::vector<VlThreadPool*> m_threadPoolps; // All thread pools, in insertion order
|
||||
bool m_fullDump = true; // Whether a full dump is required on the next call to 'dump'
|
||||
uint32_t m_nextCode = 0; // Next code number to assign
|
||||
uint32_t m_numSignals = 0; // Number of distinct signals
|
||||
@@ -216,24 +231,27 @@ private:
|
||||
char m_scopeEscape = '.';
|
||||
double m_timeRes = 1e-9; // Time resolution (ns/ms etc)
|
||||
double m_timeUnit = 1e-0; // Time units (ns/ms etc)
|
||||
uint64_t m_timeLastDump = 0; // Last time we did a dump
|
||||
bool m_didSomeDump = false; // Did at least one dump (i.e.: m_timeLastDump is valid)
|
||||
VerilatedContext* m_contextp = nullptr; // The context used by the traced models
|
||||
std::unordered_set<const VerilatedModel*> m_models; // The collection of models being traced
|
||||
|
||||
void addThreadPool(VlThreadPool* threadPoolp) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
|
||||
void addCallbackRecord(std::vector<CallbackRecord>& cbVec, CallbackRecord& cbRec)
|
||||
void addCallbackRecord(std::vector<CallbackRecord>& cbVec, CallbackRecord&& cbRec)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
|
||||
// Equivalent to 'this' but is of the sub-type 'T_Trace*'. Use 'self()->'
|
||||
// to access duck-typed functions to avoid a virtual function call.
|
||||
T_Trace* self() { return static_cast<T_Trace*>(this); }
|
||||
|
||||
void runParallelCallbacks(const ParallelCallbackMap& cbMap);
|
||||
void runCallbacks(const std::vector<CallbackRecord>& cbVec);
|
||||
void runOffloadedCallbacks(const std::vector<CallbackRecord>& cbVec);
|
||||
|
||||
// Flush any remaining data for this file
|
||||
static void onFlush(void* selfp) VL_MT_UNSAFE_ONE;
|
||||
// Close the file on termination
|
||||
static void onExit(void* selfp) VL_MT_UNSAFE_ONE;
|
||||
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
#ifdef VL_THREADED
|
||||
// Number of total offload buffers that have been allocated
|
||||
uint32_t m_numOffloadBuffers = 0;
|
||||
// Size of offload buffers
|
||||
@@ -279,7 +297,6 @@ protected:
|
||||
uint32_t numSignals() const { return m_numSignals; }
|
||||
uint32_t maxBits() const { return m_maxBits; }
|
||||
void fullDump(bool value) { m_fullDump = value; }
|
||||
uint64_t timeLastDump() { return m_timeLastDump; }
|
||||
|
||||
double timeRes() const { return m_timeRes; }
|
||||
double timeUnit() const { return m_timeUnit; }
|
||||
@@ -300,6 +317,14 @@ protected:
|
||||
void closeBase();
|
||||
void flushBase();
|
||||
|
||||
#ifdef VL_THREADED
|
||||
bool offload() const { return m_offload; }
|
||||
bool parallel() const { return m_parallel; }
|
||||
#else
|
||||
static constexpr bool offload() { return false; }
|
||||
static constexpr bool parallel() { return false; }
|
||||
#endif
|
||||
|
||||
//=========================================================================
|
||||
// Virtual functions to be provided by the format specific implementation
|
||||
|
||||
@@ -315,6 +340,9 @@ protected:
|
||||
virtual Buffer* getTraceBuffer() = 0;
|
||||
virtual void commitTraceBuffer(Buffer*) = 0;
|
||||
|
||||
// Configure sub-class
|
||||
virtual void configure(const VerilatedTraceConfig&) = 0;
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
// External interface to client code
|
||||
@@ -341,9 +369,12 @@ public:
|
||||
//=========================================================================
|
||||
// Non-hot path internal interface to Verilator generated code
|
||||
|
||||
void addModel(VerilatedModel*) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
void addInitCb(initCb_t cb, void* userp) VL_MT_SAFE;
|
||||
void addFullCb(dumpCb_t cb, void* userp, VlThreadPool* = nullptr) VL_MT_SAFE;
|
||||
void addChgCb(dumpCb_t cb, void* userp, VlThreadPool* = nullptr) VL_MT_SAFE;
|
||||
void addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE;
|
||||
void addFullCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE;
|
||||
void addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE;
|
||||
void addChgCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE;
|
||||
void addCleanupCb(cleanupCb_t cb, void* userp) VL_MT_SAFE;
|
||||
|
||||
void scopeEscape(char flag) { m_scopeEscape = flag; }
|
||||
@@ -355,33 +386,26 @@ public:
|
||||
//=============================================================================
|
||||
// VerilatedTraceBuffer
|
||||
|
||||
// T_Trace is the format specific subclass of VerilatedTrace.
|
||||
// T_Buffer is the format specific subclass of VerilatedTraceBuffer.
|
||||
// T_Buffer is the format specific base class of VerilatedTraceBuffer.
|
||||
// The format-specific hot-path methods use duck-typing via T_Buffer for performance.
|
||||
template <class T_Trace, class T_Buffer> class VerilatedTraceBuffer VL_NOT_FINAL {
|
||||
friend T_Trace; // Give the trace file access to the private bits
|
||||
|
||||
template <class T_Buffer>
|
||||
class VerilatedTraceBuffer VL_NOT_FINAL : public T_Buffer {
|
||||
protected:
|
||||
T_Trace& m_owner; // The VerilatedTrace subclass that owns this buffer
|
||||
// Type of the owner trace file
|
||||
using Trace = typename std::remove_cv<
|
||||
typename std::remove_reference<decltype(T_Buffer::m_owner)>::type>::type;
|
||||
|
||||
// Previous value store
|
||||
uint32_t* const m_sigs_oldvalp = m_owner.m_sigs_oldvalp;
|
||||
// Bit vector of enabled codes (nullptr = all on)
|
||||
EData* const m_sigs_enabledp = m_owner.m_sigs_enabledp;
|
||||
static_assert(std::has_virtual_destructor<T_Buffer>::value, "");
|
||||
static_assert(std::is_base_of<VerilatedTrace<Trace, T_Buffer>, Trace>::value, "");
|
||||
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
// Write pointer into current buffer
|
||||
uint32_t* m_offloadBufferWritep = m_owner.m_offloadBufferWritep;
|
||||
// End of offload buffer
|
||||
uint32_t* const m_offloadBufferEndp = m_owner.m_offloadBufferEndp;
|
||||
#endif
|
||||
friend Trace; // Give the trace file access to the private bits
|
||||
friend std::default_delete<VerilatedTraceBuffer<T_Buffer>>;
|
||||
|
||||
// Equivalent to 'this' but is of the sub-type 'T_Derived*'. Use 'self()->'
|
||||
// to access duck-typed functions to avoid a virtual function call.
|
||||
inline T_Buffer* self() { return static_cast<T_Buffer*>(this); }
|
||||
uint32_t* const m_sigs_oldvalp; // Previous value store
|
||||
EData* const m_sigs_enabledp; // Bit vector of enabled codes (nullptr = all on)
|
||||
|
||||
explicit VerilatedTraceBuffer(T_Trace& owner);
|
||||
virtual ~VerilatedTraceBuffer() = default;
|
||||
explicit VerilatedTraceBuffer(Trace& owner);
|
||||
~VerilatedTraceBuffer() override = default;
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
@@ -401,7 +425,7 @@ public:
|
||||
// duck-typed void emitWData(uint32_t code, const WData* newvalp, int bits) = 0;
|
||||
// duck-typed void emitDouble(uint32_t code, double newval) = 0;
|
||||
|
||||
VL_ATTR_ALWINLINE inline uint32_t* oldp(uint32_t code) { return m_sigs_oldvalp + code; }
|
||||
VL_ATTR_ALWINLINE uint32_t* oldp(uint32_t code) { return m_sigs_oldvalp + code; }
|
||||
|
||||
// Write to previous value buffer value and emit trace entry.
|
||||
void fullBit(uint32_t* oldp, CData newval);
|
||||
@@ -412,93 +436,32 @@ public:
|
||||
void fullWData(uint32_t* oldp, const WData* newvalp, int bits);
|
||||
void fullDouble(uint32_t* oldp, double newval);
|
||||
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
// Offloaded tracing. Just dump everything in the offload buffer
|
||||
inline void chgBit(uint32_t code, CData newval) {
|
||||
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_BIT_0 | newval;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep += 2;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
inline void chgCData(uint32_t code, CData newval, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_CDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep[2] = newval;
|
||||
m_offloadBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
inline void chgSData(uint32_t code, SData newval, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_SDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep[2] = newval;
|
||||
m_offloadBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
inline void chgIData(uint32_t code, IData newval, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_IDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep[2] = newval;
|
||||
m_offloadBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
inline void chgQData(uint32_t code, QData newval, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_QDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
*reinterpret_cast<QData*>(m_offloadBufferWritep + 2) = newval;
|
||||
m_offloadBufferWritep += 4;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
inline void chgWData(uint32_t code, const WData* newvalp, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_WDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep += 2;
|
||||
for (int i = 0; i < (bits + 31) / 32; ++i) { *m_offloadBufferWritep++ = newvalp[i]; }
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
inline void chgDouble(uint32_t code, double newval) {
|
||||
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_DOUBLE;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
*reinterpret_cast<double*>(m_offloadBufferWritep + 2) = newval;
|
||||
m_offloadBufferWritep += 4;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
|
||||
#define chgBit chgBitImpl
|
||||
#define chgCData chgCDataImpl
|
||||
#define chgSData chgSDataImpl
|
||||
#define chgIData chgIDataImpl
|
||||
#define chgQData chgQDataImpl
|
||||
#define chgWData chgWDataImpl
|
||||
#define chgDouble chgDoubleImpl
|
||||
#endif
|
||||
|
||||
// In non-offload mode, these are called directly by the trace callbacks,
|
||||
// and are called chg*. In offload mode, they are called by the worker
|
||||
// thread and are called chg*Impl
|
||||
|
||||
// Check previous dumped value of signal. If changed, then emit trace entry
|
||||
VL_ATTR_ALWINLINE inline void chgBit(uint32_t* oldp, CData newval) {
|
||||
VL_ATTR_ALWINLINE void chgBit(uint32_t* oldp, CData newval) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullBit(oldp, newval);
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgCData(uint32_t* oldp, CData newval, int bits) {
|
||||
VL_ATTR_ALWINLINE void chgCData(uint32_t* oldp, CData newval, int bits) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullCData(oldp, newval, bits);
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgSData(uint32_t* oldp, SData newval, int bits) {
|
||||
VL_ATTR_ALWINLINE void chgSData(uint32_t* oldp, SData newval, int bits) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullSData(oldp, newval, bits);
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgIData(uint32_t* oldp, IData newval, int bits) {
|
||||
VL_ATTR_ALWINLINE void chgIData(uint32_t* oldp, IData newval, int bits) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullIData(oldp, newval, bits);
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgQData(uint32_t* oldp, QData newval, int bits) {
|
||||
VL_ATTR_ALWINLINE void chgQData(uint32_t* oldp, QData newval, int bits) {
|
||||
const uint64_t diff = *reinterpret_cast<QData*>(oldp) ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullQData(oldp, newval, bits);
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgWData(uint32_t* oldp, const WData* newvalp, int bits) {
|
||||
VL_ATTR_ALWINLINE void chgWData(uint32_t* oldp, const WData* newvalp, int bits) {
|
||||
for (int i = 0; i < (bits + 31) / 32; ++i) {
|
||||
if (VL_UNLIKELY(oldp[i] ^ newvalp[i])) {
|
||||
fullWData(oldp, newvalp, bits);
|
||||
@@ -506,20 +469,85 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgDouble(uint32_t* oldp, double newval) {
|
||||
VL_ATTR_ALWINLINE void chgDouble(uint32_t* oldp, double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
if (VL_UNLIKELY(*reinterpret_cast<double*>(oldp) != newval)) fullDouble(oldp, newval);
|
||||
}
|
||||
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
#undef chgBit
|
||||
#undef chgCData
|
||||
#undef chgSData
|
||||
#undef chgIData
|
||||
#undef chgQData
|
||||
#undef chgWData
|
||||
#undef chgDouble
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef VL_THREADED
|
||||
//=============================================================================
|
||||
// VerilatedTraceOffloadBuffer
|
||||
|
||||
// T_Buffer is the format specific base class of VerilatedTraceBuffer.
|
||||
// The format-specific hot-path methods use duck-typing via T_Buffer for performance.
|
||||
template <class T_Buffer>
|
||||
class VerilatedTraceOffloadBuffer final : public VerilatedTraceBuffer<T_Buffer> {
|
||||
using typename VerilatedTraceBuffer<T_Buffer>::Trace;
|
||||
|
||||
friend Trace; // Give the trace file access to the private bits
|
||||
|
||||
uint32_t* m_offloadBufferWritep; // Write pointer into current buffer
|
||||
uint32_t* const m_offloadBufferEndp; // End of offload buffer
|
||||
|
||||
explicit VerilatedTraceOffloadBuffer(Trace& owner);
|
||||
~VerilatedTraceOffloadBuffer() override = default;
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
// Hot path internal interface to Verilator generated code
|
||||
|
||||
// Offloaded tracing. Just dump everything in the offload buffer
|
||||
void chgBit(uint32_t code, CData newval) {
|
||||
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_BIT_0 | newval;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep += 2;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
void chgCData(uint32_t code, CData newval, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_CDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep[2] = newval;
|
||||
m_offloadBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
void chgSData(uint32_t code, SData newval, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_SDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep[2] = newval;
|
||||
m_offloadBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
void chgIData(uint32_t code, IData newval, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_IDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep[2] = newval;
|
||||
m_offloadBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
void chgQData(uint32_t code, QData newval, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_QDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
*reinterpret_cast<QData*>(m_offloadBufferWritep + 2) = newval;
|
||||
m_offloadBufferWritep += 4;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
void chgWData(uint32_t code, const WData* newvalp, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_WDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep += 2;
|
||||
for (int i = 0; i < (bits + 31) / 32; ++i) { *m_offloadBufferWritep++ = newvalp[i]; }
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
void chgDouble(uint32_t code, double newval) {
|
||||
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_DOUBLE;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
*reinterpret_cast<double*>(m_offloadBufferWritep + 2) = newval;
|
||||
m_offloadBufferWritep += 4;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // guard
|
||||
|
||||
+275
-178
@@ -26,7 +26,7 @@
|
||||
|
||||
#include "verilated_intrinsics.h"
|
||||
#include "verilated_trace.h"
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
#ifdef VL_THREADED
|
||||
# include "verilated_threads.h"
|
||||
# include <list>
|
||||
#endif
|
||||
@@ -78,11 +78,12 @@ static std::string doubleToTimescale(double value) {
|
||||
return valuestr; // Gets converted to string, so no ref to stack
|
||||
}
|
||||
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
#ifdef VL_THREADED
|
||||
//=========================================================================
|
||||
// Buffer management
|
||||
|
||||
template <> uint32_t* VerilatedTrace<VL_SUB_T, VL_BUF_T>::getOffloadBuffer() {
|
||||
template <>
|
||||
uint32_t* VerilatedTrace<VL_SUB_T, VL_BUF_T>::getOffloadBuffer() {
|
||||
uint32_t* bufferp;
|
||||
// Some jitter is expected, so some number of alternative offlaod buffers are
|
||||
// required, but don't allocate more than 8 buffers.
|
||||
@@ -101,7 +102,8 @@ template <> uint32_t* VerilatedTrace<VL_SUB_T, VL_BUF_T>::getOffloadBuffer() {
|
||||
return bufferp;
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::waitForOffloadBuffer(const uint32_t* buffp) {
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::waitForOffloadBuffer(const uint32_t* buffp) {
|
||||
// Slow path code only called on flush/shutdown, so use a simple algorithm.
|
||||
// Collect buffers from worker and stash them until we get the one we want.
|
||||
std::deque<uint32_t*> stash;
|
||||
@@ -116,7 +118,8 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::waitForOffloadBuffer(const
|
||||
//=========================================================================
|
||||
// Worker thread
|
||||
|
||||
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
|
||||
bool shutdown = false;
|
||||
|
||||
do {
|
||||
@@ -127,7 +130,7 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
|
||||
|
||||
const uint32_t* readp = bufferp;
|
||||
|
||||
std::unique_ptr<VL_BUF_T> traceBufp; // We own the passed tracebuffer
|
||||
std::unique_ptr<Buffer> traceBufp; // We own the passed tracebuffer
|
||||
|
||||
while (true) {
|
||||
const uint32_t cmd = readp[0];
|
||||
@@ -143,44 +146,44 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
|
||||
// CHG_* commands
|
||||
case VerilatedTraceOffloadCommand::CHG_BIT_0:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_BIT_0 " << top);
|
||||
traceBufp->chgBitImpl(oldp, 0);
|
||||
traceBufp->chgBit(oldp, 0);
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_BIT_1:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_BIT_1 " << top);
|
||||
traceBufp->chgBitImpl(oldp, 1);
|
||||
traceBufp->chgBit(oldp, 1);
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_CDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_CDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
traceBufp->chgCDataImpl(oldp, *readp, top);
|
||||
traceBufp->chgCData(oldp, *readp, top);
|
||||
readp += 1;
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_SDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_SDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
traceBufp->chgSDataImpl(oldp, *readp, top);
|
||||
traceBufp->chgSData(oldp, *readp, top);
|
||||
readp += 1;
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_IDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_IDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
traceBufp->chgIDataImpl(oldp, *readp, top);
|
||||
traceBufp->chgIData(oldp, *readp, top);
|
||||
readp += 1;
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_QDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_QDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
traceBufp->chgQDataImpl(oldp, *reinterpret_cast<const QData*>(readp), top);
|
||||
traceBufp->chgQData(oldp, *reinterpret_cast<const QData*>(readp), top);
|
||||
readp += 2;
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_WDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_WDATA " << top);
|
||||
traceBufp->chgWDataImpl(oldp, readp, top);
|
||||
traceBufp->chgWData(oldp, readp, top);
|
||||
readp += VL_WORDS_I(top);
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_DOUBLE:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_DOUBLE " << top);
|
||||
traceBufp->chgDoubleImpl(oldp, *reinterpret_cast<const double*>(readp));
|
||||
traceBufp->chgDouble(oldp, *reinterpret_cast<const double*>(readp));
|
||||
readp += 2;
|
||||
continue;
|
||||
|
||||
@@ -196,7 +199,7 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
|
||||
case VerilatedTraceOffloadCommand::TRACE_BUFFER:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command TRACE_BUFFER " << top);
|
||||
readp -= 1; // No code in this command, undo increment
|
||||
traceBufp.reset(*reinterpret_cast<VL_BUF_T* const*>(readp));
|
||||
traceBufp.reset(*reinterpret_cast<Buffer* const*>(readp));
|
||||
readp += 2;
|
||||
continue;
|
||||
|
||||
@@ -231,7 +234,8 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
|
||||
} while (VL_LIKELY(!shutdown));
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::shutdownOffloadWorker() {
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::shutdownOffloadWorker() {
|
||||
// If the worker thread is not running, done..
|
||||
if (!m_workerThread) return;
|
||||
|
||||
@@ -251,37 +255,45 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::shutdownOffloadWorker() {
|
||||
//=============================================================================
|
||||
// Life cycle
|
||||
|
||||
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::closeBase() {
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
shutdownOffloadWorker();
|
||||
while (m_numOffloadBuffers) {
|
||||
delete[] m_offloadBuffersFromWorker.get();
|
||||
--m_numOffloadBuffers;
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::closeBase() {
|
||||
#ifdef VL_THREADED
|
||||
if (offload()) {
|
||||
shutdownOffloadWorker();
|
||||
while (m_numOffloadBuffers) {
|
||||
delete[] m_offloadBuffersFromWorker.get();
|
||||
--m_numOffloadBuffers;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::flushBase() {
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
// Hand an empty buffer to the worker thread
|
||||
uint32_t* const bufferp = getOffloadBuffer();
|
||||
*bufferp = VerilatedTraceOffloadCommand::END;
|
||||
m_offloadBuffersToWorker.put(bufferp);
|
||||
// Wait for it to be returned. As the processing is in-order,
|
||||
// this ensures all previous buffers have been processed.
|
||||
waitForOffloadBuffer(bufferp);
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::flushBase() {
|
||||
#ifdef VL_THREADED
|
||||
if (offload()) {
|
||||
// Hand an empty buffer to the worker thread
|
||||
uint32_t* const bufferp = getOffloadBuffer();
|
||||
*bufferp = VerilatedTraceOffloadCommand::END;
|
||||
m_offloadBuffersToWorker.put(bufferp);
|
||||
// Wait for it to be returned. As the processing is in-order,
|
||||
// this ensures all previous buffers have been processed.
|
||||
waitForOffloadBuffer(bufferp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Callbacks to run on global events
|
||||
|
||||
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush(void* selfp) {
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush(void* selfp) {
|
||||
// This calls 'flush' on the derived class (which must then get any mutex)
|
||||
reinterpret_cast<VL_SUB_T*>(selfp)->flush();
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit(void* selfp) {
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit(void* selfp) {
|
||||
// This calls 'close' on the derived class (which must then get any mutex)
|
||||
reinterpret_cast<VL_SUB_T*>(selfp)->close();
|
||||
}
|
||||
@@ -289,25 +301,26 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit(void* selfp) {
|
||||
//=============================================================================
|
||||
// VerilatedTrace
|
||||
|
||||
template <> VerilatedTrace<VL_SUB_T, VL_BUF_T>::VerilatedTrace() {
|
||||
template <>
|
||||
VerilatedTrace<VL_SUB_T, VL_BUF_T>::VerilatedTrace() {
|
||||
set_time_unit(Verilated::threadContextp()->timeunitString());
|
||||
set_time_resolution(Verilated::threadContextp()->timeprecisionString());
|
||||
}
|
||||
|
||||
template <> VerilatedTrace<VL_SUB_T, VL_BUF_T>::~VerilatedTrace() {
|
||||
template <>
|
||||
VerilatedTrace<VL_SUB_T, VL_BUF_T>::~VerilatedTrace() {
|
||||
if (m_sigs_oldvalp) VL_DO_CLEAR(delete[] m_sigs_oldvalp, m_sigs_oldvalp = nullptr);
|
||||
if (m_sigs_enabledp) VL_DO_CLEAR(delete[] m_sigs_enabledp, m_sigs_enabledp = nullptr);
|
||||
Verilated::removeFlushCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush, this);
|
||||
Verilated::removeExitCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit, this);
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
closeBase();
|
||||
#endif
|
||||
if (offload()) closeBase();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Internals available to format specific implementations
|
||||
|
||||
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::traceInit() VL_MT_UNSAFE {
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::traceInit() VL_MT_UNSAFE {
|
||||
// Note: It is possible to re-open a trace file (VCD in particular),
|
||||
// so we must reset the next code here, but it must have the same number
|
||||
// of codes on re-open
|
||||
@@ -355,17 +368,19 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::traceInit() VL_MT_UNSAFE {
|
||||
Verilated::addFlushCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush, this);
|
||||
Verilated::addExitCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit, this);
|
||||
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
// Compute offload buffer size. we need to be able to store a new value for
|
||||
// each signal, which is 'nextCode()' entries after the init callbacks
|
||||
// above have been run, plus up to 2 more words of metadata per signal,
|
||||
// plus fixed overhead of 1 for a termination flag and 3 for a time stamp
|
||||
// update.
|
||||
m_offloadBufferSize = nextCode() + numSignals() * 2 + 4;
|
||||
#ifdef VL_THREADED
|
||||
if (offload()) {
|
||||
// Compute offload buffer size. we need to be able to store a new value for
|
||||
// each signal, which is 'nextCode()' entries after the init callbacks
|
||||
// above have been run, plus up to 2 more words of metadata per signal,
|
||||
// plus fixed overhead of 1 for a termination flag and 3 for a time stamp
|
||||
// update.
|
||||
m_offloadBufferSize = nextCode() + numSignals() * 2 + 4;
|
||||
|
||||
// Start the worker thread
|
||||
m_workerThread.reset(
|
||||
new std::thread{&VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain, this});
|
||||
// Start the worker thread
|
||||
m_workerThread.reset(
|
||||
new std::thread{&VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain, this});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -415,14 +430,16 @@ bool VerilatedTrace<VL_SUB_T, VL_BUF_T>::declCode(uint32_t code, const char* nam
|
||||
//=========================================================================
|
||||
// Internals available to format specific implementations
|
||||
|
||||
template <> std::string VerilatedTrace<VL_SUB_T, VL_BUF_T>::timeResStr() const {
|
||||
template <>
|
||||
std::string VerilatedTrace<VL_SUB_T, VL_BUF_T>::timeResStr() const {
|
||||
return doubleToTimescale(m_timeRes);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// External interface to client code
|
||||
|
||||
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::set_time_unit(const char* unitp) VL_MT_SAFE {
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::set_time_unit(const char* unitp) VL_MT_SAFE {
|
||||
m_timeUnit = timescaleToDouble(unitp);
|
||||
}
|
||||
template <>
|
||||
@@ -451,8 +468,8 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dumpvars(int level, const std::string&
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
template <> //
|
||||
#ifdef VL_THREADED
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::parallelWorkerTask(void* datap, bool) {
|
||||
ParallelWorkerData* const wdp = reinterpret_cast<ParallelWorkerData*>(datap);
|
||||
// Run the task
|
||||
@@ -463,7 +480,8 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::parallelWorkerTask(void* datap, bool) {
|
||||
if (wdp->m_waiting) wdp->m_cv.notify_one();
|
||||
}
|
||||
|
||||
template <> VL_ATTR_NOINLINE void VerilatedTrace<VL_SUB_T, VL_BUF_T>::ParallelWorkerData::wait() {
|
||||
template <>
|
||||
VL_ATTR_NOINLINE void VerilatedTrace<VL_SUB_T, VL_BUF_T>::ParallelWorkerData::wait() {
|
||||
// Spin for a while, waiting for the buffer to become ready
|
||||
for (int i = 0; i < VL_LOCK_SPINS; ++i) {
|
||||
if (VL_LIKELY(m_ready.load(std::memory_order_relaxed))) return;
|
||||
@@ -478,56 +496,70 @@ template <> VL_ATTR_NOINLINE void VerilatedTrace<VL_SUB_T, VL_BUF_T>::ParallelWo
|
||||
#endif
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runParallelCallbacks(const ParallelCallbackMap& cbMap) {
|
||||
for (VlThreadPool* threadPoolp : m_threadPoolps) {
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
// If tracing in parallel, dispatch to the thread pool (if exists)
|
||||
if (threadPoolp && threadPoolp->numThreads()) {
|
||||
// List of work items for thread (std::list, as ParallelWorkerData is not movable)
|
||||
std::list<ParallelWorkerData> workerData;
|
||||
// We use the whole pool + the main thread
|
||||
const unsigned threads = threadPoolp->numThreads() + 1;
|
||||
// Main thread executes all jobs with index % threads == 0
|
||||
std::vector<ParallelWorkerData*> mainThreadWorkerData;
|
||||
// The tracing callbacks to execute on this thread-pool
|
||||
const auto& cbVec = cbMap.at(threadPoolp);
|
||||
// Enuque all the jobs
|
||||
for (unsigned i = 0; i < cbVec.size(); ++i) {
|
||||
const CallbackRecord& cbr = cbVec[i];
|
||||
// Always get the trace buffer on the main thread
|
||||
Buffer* const bufp = getTraceBuffer();
|
||||
// Create new work item
|
||||
workerData.emplace_back(cbr.m_dumpCb, cbr.m_userp, bufp);
|
||||
// Grab the new work item
|
||||
ParallelWorkerData* const itemp = &workerData.back();
|
||||
// Enqueue task to thread pool, or main thread
|
||||
if (unsigned rem = i % threads) {
|
||||
threadPoolp->workerp(rem - 1)->addTask(parallelWorkerTask, itemp, false);
|
||||
} else {
|
||||
mainThreadWorkerData.push_back(itemp);
|
||||
}
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runCallbacks(const std::vector<CallbackRecord>& cbVec) {
|
||||
#ifdef VL_THREADED
|
||||
if (parallel()) {
|
||||
// If tracing in parallel, dispatch to the thread pool
|
||||
VlThreadPool* threadPoolp = static_cast<VlThreadPool*>(m_contextp->threadPoolp());
|
||||
// List of work items for thread (std::list, as ParallelWorkerData is not movable)
|
||||
std::list<ParallelWorkerData> workerData;
|
||||
// We use the whole pool + the main thread
|
||||
const unsigned threads = threadPoolp->numThreads() + 1;
|
||||
// Main thread executes all jobs with index % threads == 0
|
||||
std::vector<ParallelWorkerData*> mainThreadWorkerData;
|
||||
// Enuque all the jobs
|
||||
for (unsigned i = 0; i < cbVec.size(); ++i) {
|
||||
const CallbackRecord& cbr = cbVec[i];
|
||||
// Always get the trace buffer on the main thread
|
||||
Buffer* const bufp = getTraceBuffer();
|
||||
// Create new work item
|
||||
workerData.emplace_back(cbr.m_dumpCb, cbr.m_userp, bufp);
|
||||
// Grab the new work item
|
||||
ParallelWorkerData* const itemp = &workerData.back();
|
||||
// Enqueue task to thread pool, or main thread
|
||||
if (unsigned rem = i % threads) {
|
||||
threadPoolp->workerp(rem - 1)->addTask(parallelWorkerTask, itemp);
|
||||
} else {
|
||||
mainThreadWorkerData.push_back(itemp);
|
||||
}
|
||||
// Execute main thead jobs
|
||||
for (ParallelWorkerData* const itemp : mainThreadWorkerData) {
|
||||
parallelWorkerTask(itemp, false);
|
||||
}
|
||||
// Commit all trace buffers in order
|
||||
for (ParallelWorkerData& item : workerData) {
|
||||
// Wait until ready
|
||||
item.wait();
|
||||
// Commit the buffer
|
||||
commitTraceBuffer(item.m_bufp);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
// Fall back on sequential execution
|
||||
for (const CallbackRecord& cbr : cbMap.at(threadPoolp)) {
|
||||
Buffer* const traceBufferp = getTraceBuffer();
|
||||
cbr.m_dumpCb(cbr.m_userp, traceBufferp);
|
||||
commitTraceBuffer(traceBufferp);
|
||||
// Execute main thead jobs
|
||||
for (ParallelWorkerData* const itemp : mainThreadWorkerData) {
|
||||
parallelWorkerTask(itemp, false);
|
||||
}
|
||||
// Commit all trace buffers in order
|
||||
for (ParallelWorkerData& item : workerData) {
|
||||
// Wait until ready
|
||||
item.wait();
|
||||
// Commit the buffer
|
||||
commitTraceBuffer(item.m_bufp);
|
||||
}
|
||||
|
||||
// Done
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// Fall back on sequential execution
|
||||
for (const CallbackRecord& cbr : cbVec) {
|
||||
Buffer* const traceBufferp = getTraceBuffer();
|
||||
cbr.m_dumpCb(cbr.m_userp, traceBufferp);
|
||||
commitTraceBuffer(traceBufferp);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runOffloadedCallbacks(
|
||||
const std::vector<CallbackRecord>& cbVec) {
|
||||
// Fall back on sequential execution
|
||||
#ifdef VL_THREADED
|
||||
for (const CallbackRecord& cbr : cbVec) {
|
||||
Buffer* traceBufferp = getTraceBuffer();
|
||||
cbr.m_dumpOffloadCb(cbr.m_userp, static_cast<OffloadBuffer*>(traceBufferp));
|
||||
commitTraceBuffer(traceBufferp);
|
||||
}
|
||||
#else
|
||||
if (!cbVec.empty()) VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
|
||||
#endif
|
||||
}
|
||||
|
||||
template <>
|
||||
@@ -536,13 +568,14 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
|
||||
// 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};
|
||||
if (VL_UNCOVERABLE(m_timeLastDump && timeui <= m_timeLastDump)) { // LCOV_EXCL_START
|
||||
if (VL_UNCOVERABLE(m_didSomeDump && timeui <= m_timeLastDump)) { // LCOV_EXCL_START
|
||||
VL_PRINTF_MT("%%Warning: previous dump at t=%" PRIu64 ", requesting t=%" PRIu64
|
||||
", dump call ignored\n",
|
||||
m_timeLastDump, timeui);
|
||||
return;
|
||||
} // LCOV_EXCL_STOP
|
||||
m_timeLastDump = timeui;
|
||||
m_didSomeDump = true;
|
||||
|
||||
Verilated::quiesce();
|
||||
|
||||
@@ -553,35 +586,49 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
|
||||
if (!preChangeDump()) return;
|
||||
}
|
||||
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
// Currently only incremental dumps run on the worker thread
|
||||
#ifdef VL_THREADED
|
||||
uint32_t* bufferp = nullptr;
|
||||
if (VL_LIKELY(!m_fullDump)) {
|
||||
// Get the offload buffer we are about to fill
|
||||
bufferp = getOffloadBuffer();
|
||||
m_offloadBufferWritep = bufferp;
|
||||
m_offloadBufferEndp = bufferp + m_offloadBufferSize;
|
||||
#endif
|
||||
if (offload()) {
|
||||
#ifdef VL_THREADED
|
||||
// Currently only incremental dumps run on the worker thread
|
||||
if (VL_LIKELY(!m_fullDump)) {
|
||||
// Get the offload buffer we are about to fill
|
||||
bufferp = getOffloadBuffer();
|
||||
m_offloadBufferWritep = bufferp;
|
||||
m_offloadBufferEndp = bufferp + m_offloadBufferSize;
|
||||
|
||||
// Tell worker to update time point
|
||||
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::TIME_CHANGE;
|
||||
*reinterpret_cast<uint64_t*>(m_offloadBufferWritep + 1) = timeui;
|
||||
m_offloadBufferWritep += 3;
|
||||
// Tell worker to update time point
|
||||
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::TIME_CHANGE;
|
||||
*reinterpret_cast<uint64_t*>(m_offloadBufferWritep + 1) = timeui;
|
||||
m_offloadBufferWritep += 3;
|
||||
} else {
|
||||
// Update time point
|
||||
flushBase();
|
||||
emitTimeChange(timeui);
|
||||
}
|
||||
#else
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
|
||||
#endif
|
||||
} else {
|
||||
// Update time point
|
||||
flushBase();
|
||||
emitTimeChange(timeui);
|
||||
}
|
||||
#else
|
||||
// Update time point
|
||||
emitTimeChange(timeui);
|
||||
#endif
|
||||
|
||||
// Run the callbacks
|
||||
if (VL_UNLIKELY(m_fullDump)) {
|
||||
m_fullDump = false; // No more need for next dump to be full
|
||||
runParallelCallbacks(m_fullCbs);
|
||||
if (offload()) {
|
||||
runOffloadedCallbacks(m_fullOffloadCbs);
|
||||
} else {
|
||||
runCallbacks(m_fullCbs);
|
||||
}
|
||||
} else {
|
||||
runParallelCallbacks(m_chgCbs);
|
||||
if (offload()) {
|
||||
runOffloadedCallbacks(m_chgOffloadCbs);
|
||||
} else {
|
||||
runCallbacks(m_chgCbs);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < m_cleanupCbs.size(); ++i) {
|
||||
@@ -589,8 +636,8 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
|
||||
cbr.m_cleanupCb(cbr.m_userp, self());
|
||||
}
|
||||
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
if (VL_LIKELY(bufferp)) {
|
||||
#ifdef VL_THREADED
|
||||
if (offload() && VL_LIKELY(bufferp)) {
|
||||
// Mark end of the offload buffer we just filled
|
||||
*m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::END;
|
||||
|
||||
@@ -607,58 +654,98 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
|
||||
// Non-hot path internal interface to Verilator generated code
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addThreadPool(VlThreadPool* threadPoolp)
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addModel(VerilatedModel* modelp)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
for (VlThreadPool* const poolp : m_threadPoolps) {
|
||||
if (poolp == threadPoolp) return;
|
||||
|
||||
const bool firstModel = m_models.empty();
|
||||
const bool newModel = m_models.insert(modelp).second;
|
||||
VerilatedContext* const contextp = modelp->contextp();
|
||||
|
||||
// Validate
|
||||
if (!newModel) { // LCOV_EXCL_START
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"The same model has already been added to this trace file");
|
||||
}
|
||||
m_threadPoolps.push_back(threadPoolp);
|
||||
if (VL_UNCOVERABLE(m_contextp && contextp != m_contextp)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"A trace file instance can only handle models from the same context");
|
||||
}
|
||||
if (VL_UNCOVERABLE(m_didSomeDump)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"Cannot add models to a trace file if 'dump' has already been called");
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
// Keep hold of the context
|
||||
m_contextp = contextp;
|
||||
|
||||
// Get the desired trace config from the model
|
||||
const std::unique_ptr<VerilatedTraceConfig> configp = modelp->traceConfig();
|
||||
#ifndef VL_THREADED
|
||||
if (configp->m_useOffloading) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Cannot use trace offloading without VL_THREADED");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Configure trace base class
|
||||
if (!firstModel) {
|
||||
if (m_offload != configp->m_useOffloading) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"Either all or no models using the same trace file must use offloading");
|
||||
}
|
||||
}
|
||||
m_offload = configp->m_useOffloading;
|
||||
// If at least one model requests parallel tracing, then use it
|
||||
m_parallel |= configp->m_useParallel;
|
||||
|
||||
if (VL_UNCOVERABLE(m_parallel && m_offload)) { // LCOV_EXCL_START
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Cannot use parallel tracing with offloading");
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
// Configure format specific sub class
|
||||
configure(*(configp.get()));
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCallbackRecord(std::vector<CallbackRecord>& cbVec,
|
||||
CallbackRecord& cbRec)
|
||||
CallbackRecord&& cbRec)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
if (VL_UNCOVERABLE(timeLastDump() != 0)) { // LCOV_EXCL_START
|
||||
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_SUB_T, VL_BUF_T>::addInitCb(initCb_t cb, void* userp) VL_MT_SAFE {
|
||||
CallbackRecord cbr{cb, userp};
|
||||
addCallbackRecord(m_initCbs, cbr);
|
||||
addCallbackRecord(m_initCbs, CallbackRecord{cb, userp});
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpCb_t cb, void* userp,
|
||||
VlThreadPool* threadPoolp) VL_MT_SAFE {
|
||||
CallbackRecord cbr{cb, userp};
|
||||
addThreadPool(threadPoolp);
|
||||
addCallbackRecord(m_fullCbs[threadPoolp], cbr);
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_fullCbs, CallbackRecord{cb, userp});
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addChgCb(dumpCb_t cb, void* userp,
|
||||
VlThreadPool* threadPoolp) VL_MT_SAFE {
|
||||
CallbackRecord cbr{cb, userp};
|
||||
addThreadPool(threadPoolp);
|
||||
addCallbackRecord(m_chgCbs[threadPoolp], cbr);
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_fullOffloadCbs, CallbackRecord{cb, userp});
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_chgCbs, CallbackRecord{cb, userp});
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addChgCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_chgOffloadCbs, CallbackRecord{cb, userp});
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCleanupCb(cleanupCb_t cb, void* userp) VL_MT_SAFE {
|
||||
CallbackRecord cbr{cb, userp};
|
||||
addCallbackRecord(m_cleanupCbs, cbr);
|
||||
addCallbackRecord(m_cleanupCbs, CallbackRecord{cb, userp});
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::pushNamePrefix(const std::string& prefix) {
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::pushNamePrefix(const std::string& prefix) {
|
||||
m_namePrefixStack.push_back(m_namePrefixStack.back() + prefix);
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::popNamePrefix(unsigned count) {
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::popNamePrefix(unsigned count) {
|
||||
while (count--) m_namePrefixStack.pop_back();
|
||||
assert(!m_namePrefixStack.empty());
|
||||
}
|
||||
@@ -756,83 +843,93 @@ static inline void cvtQDataToStr(char* dstp, QData value) {
|
||||
//=========================================================================
|
||||
// VerilatedTraceBuffer
|
||||
|
||||
template <> //
|
||||
VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::VerilatedTraceBuffer(VL_SUB_T& owner)
|
||||
: m_owner{owner} {
|
||||
#ifdef VL_TRACE_OFFLOAD
|
||||
if (m_offloadBufferWritep) {
|
||||
using This = VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>*;
|
||||
// Tack on the buffer address
|
||||
static_assert(2 * sizeof(uint32_t) >= sizeof(This),
|
||||
"This should be enough on all plafrorms");
|
||||
*m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::TRACE_BUFFER;
|
||||
*reinterpret_cast<This*>(m_offloadBufferWritep) = this;
|
||||
m_offloadBufferWritep += 2;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
template <>
|
||||
VerilatedTraceBuffer<VL_BUF_T>::VerilatedTraceBuffer(Trace& owner)
|
||||
: VL_BUF_T{owner}
|
||||
, m_sigs_oldvalp{owner.m_sigs_oldvalp}
|
||||
, m_sigs_enabledp{owner.m_sigs_enabledp} {}
|
||||
|
||||
// These functions must write the new value back into the old value store,
|
||||
// and subsequently call the format specific emit* implementations. Note
|
||||
// that this file must be included in the format specific implementation, so
|
||||
// the emit* functions can be inlined for performance.
|
||||
|
||||
template <> //
|
||||
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullBit(uint32_t* oldp, CData newval) {
|
||||
template <>
|
||||
void VerilatedTraceBuffer<VL_BUF_T>::fullBit(uint32_t* oldp, CData newval) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
self()->emitBit(code, newval);
|
||||
emitBit(code, newval);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullCData(uint32_t* oldp, CData newval, int bits) {
|
||||
void VerilatedTraceBuffer<VL_BUF_T>::fullCData(uint32_t* oldp, CData newval, int bits) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
self()->emitCData(code, newval, bits);
|
||||
emitCData(code, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullSData(uint32_t* oldp, SData newval, int bits) {
|
||||
void VerilatedTraceBuffer<VL_BUF_T>::fullSData(uint32_t* oldp, SData newval, int bits) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
self()->emitSData(code, newval, bits);
|
||||
emitSData(code, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullIData(uint32_t* oldp, IData newval, int bits) {
|
||||
void VerilatedTraceBuffer<VL_BUF_T>::fullIData(uint32_t* oldp, IData newval, int bits) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
self()->emitIData(code, newval, bits);
|
||||
emitIData(code, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullQData(uint32_t* oldp, QData newval, int bits) {
|
||||
void VerilatedTraceBuffer<VL_BUF_T>::fullQData(uint32_t* oldp, QData newval, int bits) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*reinterpret_cast<QData*>(oldp) = newval;
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
self()->emitQData(code, newval, bits);
|
||||
emitQData(code, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullWData(uint32_t* oldp, const WData* newvalp,
|
||||
int bits) {
|
||||
void VerilatedTraceBuffer<VL_BUF_T>::fullWData(uint32_t* oldp, const WData* newvalp, int bits) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
for (int i = 0; i < VL_WORDS_I(bits); ++i) oldp[i] = newvalp[i];
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
self()->emitWData(code, newvalp, bits);
|
||||
emitWData(code, newvalp, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullDouble(uint32_t* oldp, double newval) {
|
||||
void VerilatedTraceBuffer<VL_BUF_T>::fullDouble(uint32_t* oldp, double newval) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*reinterpret_cast<double*>(oldp) = newval;
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
self()->emitDouble(code, newval);
|
||||
emitDouble(code, newval);
|
||||
}
|
||||
|
||||
#ifdef VL_THREADED
|
||||
//=========================================================================
|
||||
// VerilatedTraceOffloadBuffer
|
||||
|
||||
template <>
|
||||
VerilatedTraceOffloadBuffer<VL_BUF_T>::VerilatedTraceOffloadBuffer(VL_SUB_T& owner)
|
||||
: VerilatedTraceBuffer<VL_BUF_T>{owner}
|
||||
, m_offloadBufferWritep{owner.m_offloadBufferWritep}
|
||||
, m_offloadBufferEndp{owner.m_offloadBufferEndp} {
|
||||
if (m_offloadBufferWritep) {
|
||||
using This = VerilatedTraceBuffer<VL_BUF_T>*;
|
||||
// Tack on the buffer address
|
||||
static_assert(2 * sizeof(uint32_t) >= sizeof(This),
|
||||
"This should be enough on all plafrorms");
|
||||
*m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::TRACE_BUFFER;
|
||||
*reinterpret_cast<This*>(m_offloadBufferWritep) = static_cast<This>(this);
|
||||
m_offloadBufferWritep += 2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // VL_CPPCHECK
|
||||
|
||||
+67
-36
@@ -91,9 +91,9 @@ class VlReadMem final {
|
||||
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
|
||||
FILE* m_fp = nullptr; // File handle for filename
|
||||
QData m_addr = 0; // Next address to read
|
||||
int m_linenum = 0; // Line number last read from file
|
||||
bool m_anyAddr = false; // Had address directive in the file
|
||||
public:
|
||||
VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end);
|
||||
@@ -107,8 +107,8 @@ public:
|
||||
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
|
||||
FILE* m_fp = nullptr; // File handle for filename
|
||||
QData m_addr = 0; // Next address to write
|
||||
public:
|
||||
VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end);
|
||||
~VlWriteMem();
|
||||
@@ -131,7 +131,8 @@ public:
|
||||
|
||||
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 {
|
||||
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
|
||||
@@ -163,7 +164,8 @@ 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) {
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -174,7 +176,8 @@ template <std::size_t T_Words> std::string VL_TO_STRING(const VlWide<T_Words>& o
|
||||
//
|
||||
// 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 {
|
||||
template <class T_Value, size_t T_MaxSize = 0>
|
||||
class VlQueue final {
|
||||
private:
|
||||
// TYPES
|
||||
using Deque = std::deque<T_Value>;
|
||||
@@ -199,7 +202,8 @@ public:
|
||||
|
||||
// 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) {
|
||||
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;
|
||||
@@ -330,7 +334,8 @@ public:
|
||||
|
||||
// Methods
|
||||
void sort() { std::sort(m_deque.begin(), m_deque.end()); }
|
||||
template <typename Func> void sort(Func with_func) {
|
||||
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
|
||||
@@ -338,7 +343,8 @@ public:
|
||||
});
|
||||
}
|
||||
void rsort() { std::sort(m_deque.rbegin(), m_deque.rend()); }
|
||||
template <typename Func> void rsort(Func with_func) {
|
||||
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
|
||||
@@ -373,7 +379,8 @@ public:
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue find(Func with_func) const {
|
||||
template <typename Func>
|
||||
VlQueue find(Func with_func) const {
|
||||
VlQueue out;
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
@@ -382,7 +389,8 @@ public:
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue<IData> find_index(Func with_func) const {
|
||||
template <typename Func>
|
||||
VlQueue<IData> find_index(Func with_func) const {
|
||||
VlQueue<IData> out;
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
@@ -391,7 +399,8 @@ public:
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue find_first(Func with_func) const {
|
||||
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) {
|
||||
@@ -400,7 +409,8 @@ public:
|
||||
}
|
||||
return VlQueue{};
|
||||
}
|
||||
template <typename Func> VlQueue<IData> find_first_index(Func with_func) const {
|
||||
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);
|
||||
@@ -408,7 +418,8 @@ public:
|
||||
}
|
||||
return VlQueue<IData>{};
|
||||
}
|
||||
template <typename Func> VlQueue find_last(Func with_func) const {
|
||||
template <typename Func>
|
||||
VlQueue find_last(Func with_func) const {
|
||||
IData index = m_deque.size() - 1;
|
||||
for (auto& item : vlstd::reverse_view(m_deque)) {
|
||||
if (with_func(index, item)) return VlQueue::cons(item);
|
||||
@@ -416,7 +427,8 @@ public:
|
||||
}
|
||||
return VlQueue{};
|
||||
}
|
||||
template <typename Func> VlQueue<IData> find_last_index(Func with_func) const {
|
||||
template <typename Func>
|
||||
VlQueue<IData> find_last_index(Func with_func) const {
|
||||
IData index = m_deque.size() - 1;
|
||||
for (auto& item : vlstd::reverse_view(m_deque)) {
|
||||
if (with_func(index, item)) return VlQueue<IData>::cons(index);
|
||||
@@ -442,7 +454,8 @@ public:
|
||||
for (const auto& i : m_deque) out += i;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_sum(Func with_func) const {
|
||||
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);
|
||||
@@ -456,7 +469,8 @@ public:
|
||||
for (; it != m_deque.end(); ++it) out *= *it;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_product(Func with_func) const {
|
||||
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;
|
||||
@@ -474,7 +488,8 @@ public:
|
||||
for (; it != m_deque.end(); ++it) out &= *it;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_and(Func with_func) const {
|
||||
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;
|
||||
@@ -489,7 +504,8 @@ public:
|
||||
for (const auto& i : m_deque) out |= i;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_or(Func with_func) const {
|
||||
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);
|
||||
@@ -500,7 +516,8 @@ public:
|
||||
for (const auto& i : m_deque) out ^= i;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_xor(Func with_func) const {
|
||||
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);
|
||||
@@ -520,7 +537,8 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template <class T_Value> std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
|
||||
template <class T_Value>
|
||||
std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
|
||||
return obj.to_string();
|
||||
}
|
||||
|
||||
@@ -529,7 +547,8 @@ template <class T_Value> std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
|
||||
// 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 {
|
||||
template <class T_Key, class T_Value>
|
||||
class VlAssocArray final {
|
||||
private:
|
||||
// TYPES
|
||||
using Map = std::map<T_Key, T_Value>;
|
||||
@@ -654,19 +673,22 @@ public:
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue<T_Value> find(Func with_func) const {
|
||||
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 {
|
||||
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 {
|
||||
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);
|
||||
@@ -674,7 +696,8 @@ public:
|
||||
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 {
|
||||
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);
|
||||
@@ -682,7 +705,8 @@ public:
|
||||
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 {
|
||||
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);
|
||||
@@ -690,7 +714,8 @@ public:
|
||||
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 {
|
||||
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);
|
||||
@@ -724,7 +749,8 @@ public:
|
||||
for (const auto& i : m_map) out += i.second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_sum(Func with_func) const {
|
||||
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;
|
||||
@@ -737,7 +763,8 @@ public:
|
||||
for (; it != m_map.end(); ++it) out *= it->second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_product(Func with_func) const {
|
||||
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)};
|
||||
@@ -753,7 +780,8 @@ public:
|
||||
for (; it != m_map.end(); ++it) out &= it->second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_and(Func with_func) const {
|
||||
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)};
|
||||
@@ -766,7 +794,8 @@ public:
|
||||
for (const auto& i : m_map) out |= i.second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_or(Func with_func) const {
|
||||
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;
|
||||
@@ -776,7 +805,8 @@ public:
|
||||
for (const auto& i : m_map) out ^= i.second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_xor(Func with_func) const {
|
||||
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;
|
||||
@@ -839,7 +869,8 @@ void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
|
||||
/// 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 {
|
||||
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
|
||||
|
||||
+110
-121
@@ -38,13 +38,13 @@
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifndef O_LARGEFILE // For example on WIN32
|
||||
#ifndef O_LARGEFILE // WIN32 headers omit this
|
||||
# define O_LARGEFILE 0
|
||||
#endif
|
||||
#ifndef O_NONBLOCK
|
||||
#ifndef O_NONBLOCK // WIN32 headers omit this
|
||||
# define O_NONBLOCK 0
|
||||
#endif
|
||||
#ifndef O_CLOEXEC
|
||||
#ifndef O_CLOEXEC // WIN32 headers omit this
|
||||
# define O_CLOEXEC 0
|
||||
#endif
|
||||
|
||||
@@ -65,7 +65,8 @@ constexpr unsigned VL_TRACE_SUFFIX_ENTRY_SIZE = 8; // Size of a suffix entry
|
||||
//=============================================================================
|
||||
// Utility functions: TODO: put these in a common place and share them.
|
||||
|
||||
template <size_t N> static size_t roundUpToMultipleOf(size_t value) {
|
||||
template <size_t N>
|
||||
static size_t roundUpToMultipleOf(size_t value) {
|
||||
static_assert((N & (N - 1)) == 0, "'N' must be a power of 2");
|
||||
size_t mask = N - 1;
|
||||
return (value + mask) & ~mask;
|
||||
@@ -119,13 +120,13 @@ void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
// Set member variables
|
||||
m_filename = filename; // "" is ok, as someone may overload open
|
||||
|
||||
openNextImp(m_rolloverMB != 0);
|
||||
openNextImp(m_rolloverSize != 0);
|
||||
if (!isOpen()) return;
|
||||
|
||||
dumpHeader();
|
||||
|
||||
// When using rollover, the first chunk contains the header only.
|
||||
if (m_rolloverMB) openNextImp(true);
|
||||
if (m_rolloverSize) openNextImp(true);
|
||||
}
|
||||
|
||||
void VerilatedVcd::openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
@@ -179,7 +180,7 @@ void VerilatedVcd::openNextImp(bool incFilename) {
|
||||
}
|
||||
|
||||
bool VerilatedVcd::preChangeDump() {
|
||||
if (VL_UNLIKELY(m_rolloverMB && m_wroteBytes > m_rolloverMB)) openNextImp(true);
|
||||
if (VL_UNLIKELY(m_rolloverSize && m_wroteBytes > m_rolloverSize)) openNextImp(true);
|
||||
return isOpen();
|
||||
}
|
||||
|
||||
@@ -229,9 +230,11 @@ VerilatedVcd::~VerilatedVcd() {
|
||||
if (m_wrBufp) VL_DO_CLEAR(delete[] m_wrBufp, m_wrBufp = nullptr);
|
||||
deleteNameMap();
|
||||
if (m_filep && m_fileNewed) VL_DO_CLEAR(delete m_filep, m_filep = nullptr);
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
assert(m_numBuffers == m_freeBuffers.size());
|
||||
for (auto& pair : m_freeBuffers) VL_DO_CLEAR(delete[] pair.first, pair.first = nullptr);
|
||||
#ifdef VL_THREADED
|
||||
if (parallel()) {
|
||||
assert(m_numBuffers == m_freeBuffers.size());
|
||||
for (auto& pair : m_freeBuffers) VL_DO_CLEAR(delete[] pair.first, pair.first = nullptr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -260,11 +263,6 @@ void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
// This function is on the flush() call path
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
if (!isOpen()) return;
|
||||
if (m_evcd) {
|
||||
printStr("$vcdclose ");
|
||||
printQuad(timeLastDump());
|
||||
printStr(" $end\n");
|
||||
}
|
||||
closePrev();
|
||||
// closePrev() called Super::flush(), so we just
|
||||
// need to shut down the tracing thread here.
|
||||
@@ -514,38 +512,29 @@ void VerilatedVcd::declare(uint32_t code, const char* name, const char* wirep, b
|
||||
|
||||
// Print reference
|
||||
std::string decl = "$var ";
|
||||
if (m_evcd) {
|
||||
decl += "port";
|
||||
} else {
|
||||
decl += wirep; // usually "wire"
|
||||
}
|
||||
decl += wirep; // usually "wire"
|
||||
|
||||
constexpr size_t bufsize = 1000;
|
||||
char buf[bufsize];
|
||||
VL_SNPRINTF(buf, bufsize, " %2d ", bits);
|
||||
decl += buf;
|
||||
if (m_evcd) {
|
||||
VL_SNPRINTF(buf, bufsize, "<%u", code);
|
||||
decl += buf;
|
||||
} else {
|
||||
// Add string code to decl
|
||||
char* const endp = writeCode(buf, code);
|
||||
*endp = '\0';
|
||||
decl += buf;
|
||||
// Build suffix array entry
|
||||
char* const entryp = &m_suffixes[code * VL_TRACE_SUFFIX_ENTRY_SIZE];
|
||||
const size_t length = endp - buf;
|
||||
assert(length <= VL_TRACE_MAX_VCD_CODE_SIZE);
|
||||
// 1 bit values don't have a ' ' separator between value and string code
|
||||
const bool isBit = bits == 1;
|
||||
entryp[0] = ' '; // Separator
|
||||
// Use memcpy as we checked size above, and strcpy is flagged unsafe
|
||||
std::memcpy(entryp + !isBit, buf,
|
||||
std::strlen(buf)); // Code (overwrite separator if isBit)
|
||||
entryp[length + !isBit] = '\n'; // Replace '\0' with line termination '\n'
|
||||
// Set length of suffix (used to increment write pointer)
|
||||
entryp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1] = !isBit + length + 1;
|
||||
}
|
||||
// Add string code to decl
|
||||
char* const endp = writeCode(buf, code);
|
||||
*endp = '\0';
|
||||
decl += buf;
|
||||
// Build suffix array entry
|
||||
char* const entryp = &m_suffixes[code * VL_TRACE_SUFFIX_ENTRY_SIZE];
|
||||
const size_t length = endp - buf;
|
||||
assert(length <= VL_TRACE_MAX_VCD_CODE_SIZE);
|
||||
// 1 bit values don't have a ' ' separator between value and string code
|
||||
const bool isBit = bits == 1;
|
||||
entryp[0] = ' '; // Separator
|
||||
// Use memcpy as we checked size above, and strcpy is flagged unsafe
|
||||
std::memcpy(entryp + !isBit, buf,
|
||||
std::strlen(buf)); // Code (overwrite separator if isBit)
|
||||
entryp[length + !isBit] = '\n'; // Replace '\0' with line termination '\n'
|
||||
// Set length of suffix (used to increment write pointer)
|
||||
entryp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1] = !isBit + length + 1;
|
||||
decl += " ";
|
||||
decl += basename;
|
||||
if (array) {
|
||||
@@ -583,74 +572,70 @@ void VerilatedVcd::declDouble(uint32_t code, const char* name, bool array, int a
|
||||
//=============================================================================
|
||||
// Get/commit trace buffer
|
||||
|
||||
VerilatedVcdBuffer* VerilatedVcd::getTraceBuffer() {
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
// Note: This is called from VeriltedVcd::dump, which already holds the lock
|
||||
// If no buffer available, allocate a new one
|
||||
if (m_freeBuffers.empty()) {
|
||||
constexpr size_t pageSize = 4096;
|
||||
// 4 * m_maxSignalBytes, so we can reserve 2 * m_maxSignalBytes at the end for safety
|
||||
size_t startingSize = roundUpToMultipleOf<pageSize>(4 * m_maxSignalBytes);
|
||||
m_freeBuffers.emplace_back(new char[startingSize], startingSize);
|
||||
++m_numBuffers;
|
||||
VerilatedVcd::Buffer* VerilatedVcd::getTraceBuffer() {
|
||||
VerilatedVcd::Buffer* const bufp = new Buffer{*this};
|
||||
#ifdef VL_THREADED
|
||||
if (parallel()) {
|
||||
// Note: This is called from VeriltedVcd::dump, which already holds the lock
|
||||
// If no buffer available, allocate a new one
|
||||
if (m_freeBuffers.empty()) {
|
||||
constexpr size_t pageSize = 4096;
|
||||
// 4 * m_maxSignalBytes, so we can reserve 2 * m_maxSignalBytes at the end for safety
|
||||
size_t startingSize = roundUpToMultipleOf<pageSize>(4 * m_maxSignalBytes);
|
||||
m_freeBuffers.emplace_back(new char[startingSize], startingSize);
|
||||
++m_numBuffers;
|
||||
}
|
||||
// Grab a buffer
|
||||
const auto pair = m_freeBuffers.back();
|
||||
m_freeBuffers.pop_back();
|
||||
// Initialize
|
||||
bufp->m_writep = bufp->m_bufp = pair.first;
|
||||
bufp->m_size = pair.second;
|
||||
bufp->adjustGrowp();
|
||||
}
|
||||
// Grab a buffer
|
||||
const auto pair = m_freeBuffers.back();
|
||||
m_freeBuffers.pop_back();
|
||||
// Return the buffer
|
||||
return new VerilatedVcdBuffer{*this, pair.first, pair.second};
|
||||
#else
|
||||
return new VerilatedVcdBuffer{*this};
|
||||
#endif
|
||||
// Return the buffer
|
||||
return bufp;
|
||||
}
|
||||
|
||||
void VerilatedVcd::commitTraceBuffer(VerilatedVcdBuffer* bufp) {
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
// Note: This is called from VeriltedVcd::dump, which already holds the lock
|
||||
// Resize output buffer. Note, we use the full size of the trace buffer, as
|
||||
// this is a lot more stable than the actual occupancy of the trace buffer.
|
||||
// This helps us to avoid re-allocations due to small size changes.
|
||||
bufferResize(bufp->m_size);
|
||||
// Compute occupancy of buffer
|
||||
const size_t usedSize = bufp->m_writep - bufp->m_bufp;
|
||||
// Copy to output buffer
|
||||
std::memcpy(m_writep, bufp->m_bufp, usedSize);
|
||||
// Adjust write pointer
|
||||
m_writep += usedSize;
|
||||
// Flush if necessary
|
||||
bufferCheck();
|
||||
// Put buffer back on free list
|
||||
m_freeBuffers.emplace_back(bufp->m_bufp, bufp->m_size);
|
||||
void VerilatedVcd::commitTraceBuffer(VerilatedVcd::Buffer* bufp) {
|
||||
if (parallel()) {
|
||||
#if VL_THREADED
|
||||
// Note: This is called from VeriltedVcd::dump, which already holds the lock
|
||||
// Resize output buffer. Note, we use the full size of the trace buffer, as
|
||||
// this is a lot more stable than the actual occupancy of the trace buffer.
|
||||
// This helps us to avoid re-allocations due to small size changes.
|
||||
bufferResize(bufp->m_size);
|
||||
// Compute occupancy of buffer
|
||||
const size_t usedSize = bufp->m_writep - bufp->m_bufp;
|
||||
// Copy to output buffer
|
||||
std::memcpy(m_writep, bufp->m_bufp, usedSize);
|
||||
// Adjust write pointer
|
||||
m_writep += usedSize;
|
||||
// Flush if necessary
|
||||
bufferCheck();
|
||||
// Put buffer back on free list
|
||||
m_freeBuffers.emplace_back(bufp->m_bufp, bufp->m_size);
|
||||
#else
|
||||
// Needs adjusting for emitTimeChange
|
||||
m_writep = bufp->m_writep;
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
|
||||
#endif
|
||||
} else {
|
||||
// Needs adjusting for emitTimeChange
|
||||
m_writep = bufp->m_writep;
|
||||
}
|
||||
delete bufp;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcdBuffer implementation
|
||||
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
VerilatedVcdBuffer::VerilatedVcdBuffer(VerilatedVcd& owner, char* bufp, size_t size)
|
||||
: VerilatedTraceBuffer<VerilatedVcd, VerilatedVcdBuffer>{owner}
|
||||
, m_writep{bufp}
|
||||
, m_bufp{bufp}
|
||||
, m_size{size} {
|
||||
adjustGrowp();
|
||||
}
|
||||
#else
|
||||
VerilatedVcdBuffer::VerilatedVcdBuffer(VerilatedVcd& owner)
|
||||
: VerilatedTraceBuffer<VerilatedVcd, VerilatedVcdBuffer>{owner} {}
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// Trace rendering primitives
|
||||
|
||||
static inline void
|
||||
VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) VL_ATTR_NO_SANITIZE_ALIGN;
|
||||
static void VerilatedVcdCCopyAndAppendNewLine(char* writep,
|
||||
const char* suffixp) VL_ATTR_NO_SANITIZE_ALIGN;
|
||||
|
||||
static inline void VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) {
|
||||
static void VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) {
|
||||
// Copy the whole suffix (this avoid having hard to predict branches which
|
||||
// helps a lot). Note: The maximum length of the suffix is
|
||||
// VL_TRACE_MAX_VCD_CODE_SIZE + 2 == 7, but we unroll this here for speed.
|
||||
@@ -679,35 +664,39 @@ void VerilatedVcdBuffer::finishLine(uint32_t code, char* writep) {
|
||||
// suffix, which was stored in the last byte of the suffix buffer entry.
|
||||
m_writep = writep + suffixp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1];
|
||||
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
// Double the size of the buffer if necessary
|
||||
if (VL_UNLIKELY(m_writep >= m_growp)) {
|
||||
// Compute occupied size of current buffer
|
||||
const size_t usedSize = m_writep - m_bufp;
|
||||
// We are always doubling the size
|
||||
m_size *= 2;
|
||||
// Allocate the new buffer
|
||||
char* const newBufp = new char[m_size];
|
||||
// Copy from current buffer to new buffer
|
||||
std::memcpy(newBufp, m_bufp, usedSize);
|
||||
// Delete current buffer
|
||||
delete[] m_bufp;
|
||||
// Make new buffer the current buffer
|
||||
m_bufp = newBufp;
|
||||
// Adjust write pointer
|
||||
m_writep = m_bufp + usedSize;
|
||||
// Adjust resize limit
|
||||
adjustGrowp();
|
||||
}
|
||||
if (m_owner.parallel()) {
|
||||
#ifdef VL_THREADED
|
||||
// Double the size of the buffer if necessary
|
||||
if (VL_UNLIKELY(m_writep >= m_growp)) {
|
||||
// Compute occupied size of current buffer
|
||||
const size_t usedSize = m_writep - m_bufp;
|
||||
// We are always doubling the size
|
||||
m_size *= 2;
|
||||
// Allocate the new buffer
|
||||
char* const newBufp = new char[m_size];
|
||||
// Copy from current buffer to new buffer
|
||||
std::memcpy(newBufp, m_bufp, usedSize);
|
||||
// Delete current buffer
|
||||
delete[] m_bufp;
|
||||
// Make new buffer the current buffer
|
||||
m_bufp = newBufp;
|
||||
// Adjust write pointer
|
||||
m_writep = m_bufp + usedSize;
|
||||
// Adjust resize limit
|
||||
adjustGrowp();
|
||||
}
|
||||
#else
|
||||
// Flush the write buffer if there's not enough space left for new information
|
||||
// We only call this once per vector, so we need enough slop for a very wide "b###" line
|
||||
if (VL_UNLIKELY(m_writep > m_wrFlushp)) {
|
||||
m_owner.m_writep = m_writep;
|
||||
m_owner.bufferFlush();
|
||||
m_writep = m_owner.m_writep;
|
||||
}
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
|
||||
#endif
|
||||
} else {
|
||||
// Flush the write buffer if there's not enough space left for new information
|
||||
// We only call this once per vector, so we need enough slop for a very wide "b###" line
|
||||
if (VL_UNLIKELY(m_writep > m_wrFlushp)) {
|
||||
m_owner.m_writep = m_writep;
|
||||
m_owner.bufferFlush();
|
||||
m_writep = m_owner.m_writep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
+60
-50
@@ -41,7 +41,7 @@ public:
|
||||
using Super = VerilatedTrace<VerilatedVcd, VerilatedVcdBuffer>;
|
||||
|
||||
private:
|
||||
friend Buffer; // Give the buffer access to the private bits
|
||||
friend VerilatedVcdBuffer; // Give the buffer access to the private bits
|
||||
|
||||
//=========================================================================
|
||||
// VCD specific internals
|
||||
@@ -49,9 +49,8 @@ private:
|
||||
VerilatedVcdFile* m_filep; // File we're writing to
|
||||
bool m_fileNewed; // m_filep needs destruction
|
||||
bool m_isOpen = false; // True indicates open file
|
||||
bool m_evcd = false; // True for evcd format
|
||||
std::string m_filename; // Filename we're writing to (if open)
|
||||
uint64_t m_rolloverMB = 0; // MB of file size to rollover at
|
||||
uint64_t m_rolloverSize = 0; // File size to rollover at
|
||||
int m_modDepth = 0; // Depth of module hierarchy
|
||||
|
||||
char* m_wrBufp; // Output buffer
|
||||
@@ -66,7 +65,7 @@ private:
|
||||
using NameMap = std::map<const std::string, const std::string>;
|
||||
NameMap* m_namemapp = nullptr; // List of names for the header
|
||||
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
#ifdef VL_THREADED
|
||||
// Vector of free trace buffers as (pointer, size) pairs.
|
||||
std::vector<std::pair<char*, size_t>> m_freeBuffers;
|
||||
size_t m_numBuffers = 0; // Number of trace buffers allocated
|
||||
@@ -74,7 +73,7 @@ private:
|
||||
|
||||
void bufferResize(size_t minsize);
|
||||
void bufferFlush() VL_MT_UNSAFE_ONE;
|
||||
inline void bufferCheck() {
|
||||
void bufferCheck() {
|
||||
// Flush the write buffer if there's not enough space left for new information
|
||||
// We only call this once per vector, so we need enough slop for a very wide "b###" line
|
||||
if (VL_UNLIKELY(m_writep > m_wrFlushp)) bufferFlush();
|
||||
@@ -103,15 +102,18 @@ protected:
|
||||
// Implementation of VerilatedTrace interface
|
||||
|
||||
// Called when the trace moves forward to a new time point
|
||||
virtual void emitTimeChange(uint64_t timeui) override;
|
||||
void emitTimeChange(uint64_t timeui) override;
|
||||
|
||||
// Hooks called from VerilatedTrace
|
||||
virtual bool preFullDump() override { return isOpen(); }
|
||||
virtual bool preChangeDump() override;
|
||||
bool preFullDump() override { return isOpen(); }
|
||||
bool preChangeDump() override;
|
||||
|
||||
// Trace buffer management
|
||||
virtual VerilatedVcdBuffer* getTraceBuffer() override;
|
||||
virtual void commitTraceBuffer(VerilatedVcdBuffer*) override;
|
||||
Buffer* getTraceBuffer() override;
|
||||
void commitTraceBuffer(Buffer*) override;
|
||||
|
||||
// Configure sub-class
|
||||
void configure(const VerilatedTraceConfig&) override{};
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
@@ -122,8 +124,8 @@ public:
|
||||
~VerilatedVcd();
|
||||
|
||||
// ACCESSORS
|
||||
// Set size in megabytes after which new file should be created
|
||||
void rolloverMB(uint64_t rolloverMB) { m_rolloverMB = rolloverMB; }
|
||||
// Set size in bytes after which new file should be created.
|
||||
void rolloverSize(uint64_t size) { m_rolloverSize = size; }
|
||||
|
||||
// METHODS - All must be thread safe
|
||||
// Open the file; call isOpen() to see if errors
|
||||
@@ -149,67 +151,72 @@ public:
|
||||
|
||||
#ifndef DOXYGEN
|
||||
// Declare specialization here as it's used in VerilatedFstC just below
|
||||
template <> void VerilatedVcd::Super::dump(uint64_t time);
|
||||
template <> void VerilatedVcd::Super::set_time_unit(const char* unitp);
|
||||
template <> void VerilatedVcd::Super::set_time_unit(const std::string& unit);
|
||||
template <> void VerilatedVcd::Super::set_time_resolution(const char* unitp);
|
||||
template <> void VerilatedVcd::Super::set_time_resolution(const std::string& unit);
|
||||
template <> void VerilatedVcd::Super::dumpvars(int level, const std::string& hier);
|
||||
template <>
|
||||
void VerilatedVcd::Super::dump(uint64_t time);
|
||||
template <>
|
||||
void VerilatedVcd::Super::set_time_unit(const char* unitp);
|
||||
template <>
|
||||
void VerilatedVcd::Super::set_time_unit(const std::string& unit);
|
||||
template <>
|
||||
void VerilatedVcd::Super::set_time_resolution(const char* unitp);
|
||||
template <>
|
||||
void VerilatedVcd::Super::set_time_resolution(const std::string& unit);
|
||||
template <>
|
||||
void VerilatedVcd::Super::dumpvars(int level, const std::string& hier);
|
||||
#endif // DOXYGEN
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcdBuffer
|
||||
|
||||
class VerilatedVcdBuffer final : public VerilatedTraceBuffer<VerilatedVcd, VerilatedVcdBuffer> {
|
||||
// Give the trace file access to the private bits
|
||||
class VerilatedVcdBuffer VL_NOT_FINAL {
|
||||
// Give the trace file ans sub-classes access to the private bits
|
||||
friend VerilatedVcd;
|
||||
friend VerilatedVcd::Super;
|
||||
friend VerilatedVcd::Buffer;
|
||||
friend VerilatedVcd::OffloadBuffer;
|
||||
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
char* m_writep; // Write pointer into m_bufp
|
||||
char* m_bufp; // The beginning of the trace buffer
|
||||
size_t m_size; // The size of the buffer at m_bufp
|
||||
char* m_growp; // Resize limit pointer
|
||||
#else
|
||||
char* m_writep = m_owner.m_writep; // Write pointer into output buffer
|
||||
char* const m_wrFlushp = m_owner.m_wrFlushp; // Output buffer flush trigger location
|
||||
#endif
|
||||
VerilatedVcd& m_owner; // Trace file owning this buffer. Required by subclasses.
|
||||
|
||||
// Write pointer into output buffer (in parallel mode, this is set up in 'getTraceBuffer')
|
||||
char* m_writep = m_owner.parallel() ? nullptr : m_owner.m_writep;
|
||||
// Output buffer flush trigger location (only used when not parallel)
|
||||
char* const m_wrFlushp = m_owner.parallel() ? nullptr : m_owner.m_wrFlushp;
|
||||
|
||||
// VCD line end string codes + metadata
|
||||
const char* const m_suffixes = m_owner.m_suffixes.data();
|
||||
// The maximum number of bytes a single signal can emit
|
||||
const size_t m_maxSignalBytes = m_owner.m_maxSignalBytes;
|
||||
|
||||
void finishLine(uint32_t code, char* writep);
|
||||
#ifdef VL_THREADED
|
||||
// Additional data for parallel tracing only
|
||||
char* m_bufp = nullptr; // The beginning of the trace buffer
|
||||
size_t m_size = 0; // The size of the buffer at m_bufp
|
||||
char* m_growp = nullptr; // Resize limit pointer
|
||||
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
void adjustGrowp() {
|
||||
m_growp = (m_bufp + m_size) - (2 * m_maxSignalBytes);
|
||||
assert(m_growp >= m_bufp + m_maxSignalBytes);
|
||||
}
|
||||
#endif
|
||||
|
||||
public:
|
||||
void finishLine(uint32_t code, char* writep);
|
||||
|
||||
// CONSTRUCTOR
|
||||
#ifdef VL_TRACE_PARALLEL
|
||||
explicit VerilatedVcdBuffer(VerilatedVcd& owner, char* bufp, size_t size);
|
||||
#else
|
||||
explicit VerilatedVcdBuffer(VerilatedVcd& owner);
|
||||
#endif
|
||||
~VerilatedVcdBuffer() = default;
|
||||
explicit VerilatedVcdBuffer(VerilatedVcd& owner)
|
||||
: m_owner{owner} {}
|
||||
virtual ~VerilatedVcdBuffer() = default;
|
||||
|
||||
//=========================================================================
|
||||
// Implementation of VerilatedTraceBuffer interface
|
||||
|
||||
// Implementations of duck-typed methods for VerilatedTraceBuffer. These are
|
||||
// called from only one place (the full* methods), so always inline them.
|
||||
VL_ATTR_ALWINLINE inline void emitBit(uint32_t code, CData newval);
|
||||
VL_ATTR_ALWINLINE inline void emitCData(uint32_t code, CData newval, int bits);
|
||||
VL_ATTR_ALWINLINE inline void emitSData(uint32_t code, SData newval, int bits);
|
||||
VL_ATTR_ALWINLINE inline void emitIData(uint32_t code, IData newval, int bits);
|
||||
VL_ATTR_ALWINLINE inline void emitQData(uint32_t code, QData newval, int bits);
|
||||
VL_ATTR_ALWINLINE inline void emitWData(uint32_t code, const WData* newvalp, int bits);
|
||||
VL_ATTR_ALWINLINE inline void emitDouble(uint32_t code, double newval);
|
||||
VL_ATTR_ALWINLINE void emitBit(uint32_t code, CData newval);
|
||||
VL_ATTR_ALWINLINE void emitCData(uint32_t code, CData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitIData(uint32_t code, IData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitQData(uint32_t code, QData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitWData(uint32_t code, const WData* newvalp, int bits);
|
||||
VL_ATTR_ALWINLINE void emitDouble(uint32_t code, double newval);
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
@@ -252,7 +259,6 @@ public:
|
||||
/// Destruct, flush, and close the dump
|
||||
virtual ~VerilatedVcdC() { close(); }
|
||||
|
||||
public:
|
||||
// METHODS - User called
|
||||
|
||||
/// Return if file is open
|
||||
@@ -265,8 +271,12 @@ public:
|
||||
/// The header is only in the first file created, this allows
|
||||
/// "cat" to be used to combine the header plus any number of data files.
|
||||
void openNext(bool incFilename = true) VL_MT_SAFE { m_sptrace.openNext(incFilename); }
|
||||
/// Set size in megabytes after which new file should be created
|
||||
void rolloverMB(size_t rolloverMB) VL_MT_SAFE { m_sptrace.rolloverMB(rolloverMB); }
|
||||
/// Set size in bytes after which new file should be created
|
||||
/// This will create a header file, followed by each separate file
|
||||
/// which might be larger than the given size (due to chunking and
|
||||
/// alignment to a start of a given time's dump). Any file but the
|
||||
/// first may be removed. Cat files together to create viewable vcd.
|
||||
void rolloverSize(size_t size) VL_MT_SAFE { m_sptrace.rolloverSize(size); }
|
||||
/// Close dump
|
||||
void close() VL_MT_SAFE { m_sptrace.close(); }
|
||||
/// Flush dump
|
||||
@@ -303,7 +313,7 @@ public:
|
||||
}
|
||||
|
||||
// Internal class access
|
||||
inline VerilatedVcd* spTrace() { return &m_sptrace; }
|
||||
VerilatedVcd* spTrace() { return &m_sptrace; }
|
||||
};
|
||||
|
||||
#endif // guard
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
//=============================================================================
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated_vcd_sc.h"
|
||||
|
||||
//======================================================================
|
||||
@@ -30,7 +31,7 @@
|
||||
void VerilatedVcdSc::open(const char* filename) {
|
||||
if (!sc_core::sc_get_curr_simcontext()->elaboration_done()) {
|
||||
vl_fatal(__FILE__, __LINE__, "VerilatedVcdSc",
|
||||
("%Error: VerilatedVcdSc::open(\"" + std::string(filename)
|
||||
("%Error: VerilatedVcdSc::open(\"" + std::string{filename}
|
||||
+ "\") is called before sc_core::sc_start(). "
|
||||
"Run sc_core::sc_start(sc_core::SC_ZERO_TIME) before opening a wave file.")
|
||||
.c_str());
|
||||
@@ -63,9 +64,6 @@ void VerilatedVcdSc::trace(const unsigned int&, const std::string&, const char**
|
||||
DECL_TRACE_METHOD_B( unsigned short )
|
||||
DECL_TRACE_METHOD_B( unsigned int )
|
||||
DECL_TRACE_METHOD_B( unsigned long )
|
||||
#ifdef SYSTEMC_64BIT_PATCHES
|
||||
DECL_TRACE_METHOD_B( unsigned long long)
|
||||
#endif
|
||||
DECL_TRACE_METHOD_B( char )
|
||||
DECL_TRACE_METHOD_B( short )
|
||||
DECL_TRACE_METHOD_B( int )
|
||||
|
||||
+10
-12
@@ -23,6 +23,7 @@
|
||||
#define VERILATOR_VERILATED_VCD_SC_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated_sc.h"
|
||||
#include "verilated_vcd_c.h"
|
||||
|
||||
@@ -54,34 +55,34 @@ public:
|
||||
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
|
||||
}
|
||||
/// Destruct, flush, and close the dump
|
||||
virtual ~VerilatedVcdSc() { close(); }
|
||||
~VerilatedVcdSc() override { close(); }
|
||||
|
||||
// METHODS - for SC kernel
|
||||
// Called by SystemC simulate()
|
||||
virtual void cycle(bool delta_cycle) {
|
||||
void cycle(bool delta_cycle) override {
|
||||
if (!delta_cycle) this->dump(sc_time_stamp().to_double());
|
||||
}
|
||||
|
||||
// Override VerilatedVcdC. Must be called after starting simulation.
|
||||
virtual void open(const char* filename) /*override*/ VL_MT_SAFE;
|
||||
void open(const char* filename) override VL_MT_SAFE;
|
||||
|
||||
private:
|
||||
// METHODS - Fake outs for linker
|
||||
|
||||
#ifdef NC_SYSTEMC
|
||||
// Cadence Incisive has these as abstract functions so we must create them
|
||||
virtual void set_time_unit(int exponent10_seconds) {} // deprecated
|
||||
void set_time_unit(int exponent10_seconds) override {} // deprecated
|
||||
#endif
|
||||
virtual void set_time_unit(double v, sc_time_unit tu) {} // LCOV_EXCL_LINE
|
||||
void set_time_unit(double v, sc_time_unit tu) override {} // LCOV_EXCL_LINE
|
||||
|
||||
//--------------------------------------------------
|
||||
// SystemC 2.1.v1
|
||||
#define DECL_TRACE_METHOD_A(tp) virtual void trace(const tp& object, const std::string& name);
|
||||
#define DECL_TRACE_METHOD_A(tp) void trace(const tp& object, const std::string& name) override;
|
||||
#define DECL_TRACE_METHOD_B(tp) \
|
||||
virtual void trace(const tp& object, const std::string& name, int width);
|
||||
void trace(const tp& object, const std::string& name, int width) override;
|
||||
|
||||
virtual void write_comment(const std::string&);
|
||||
virtual void trace(const unsigned int&, const std::string&, const char**);
|
||||
void write_comment(const std::string&) override;
|
||||
void trace(const unsigned int&, const std::string&, const char**) override;
|
||||
|
||||
// clang-format off
|
||||
// Formatting matches that of sc_trace.h
|
||||
@@ -98,9 +99,6 @@ private:
|
||||
DECL_TRACE_METHOD_B( unsigned short )
|
||||
DECL_TRACE_METHOD_B( unsigned int )
|
||||
DECL_TRACE_METHOD_B( unsigned long )
|
||||
#ifdef SYSTEMC_64BIT_PATCHES
|
||||
DECL_TRACE_METHOD_B( unsigned long long)
|
||||
#endif
|
||||
DECL_TRACE_METHOD_B( char )
|
||||
DECL_TRACE_METHOD_B( short )
|
||||
DECL_TRACE_METHOD_B( int )
|
||||
|
||||
+48
-47
@@ -26,8 +26,9 @@
|
||||
|
||||
#define VERILATOR_VERILATED_VPI_CPP_
|
||||
|
||||
#include "verilated.h"
|
||||
#include "verilated_vpi.h"
|
||||
|
||||
#include "verilated.h"
|
||||
#include "verilated_imp.h"
|
||||
|
||||
#include <list>
|
||||
@@ -95,7 +96,7 @@ public:
|
||||
*(reinterpret_cast<uint32_t*>(newp)) = activeMagic();
|
||||
return newp + 8;
|
||||
}
|
||||
static void operator delete(void* obj, size_t /*size*/)VL_MT_SAFE {
|
||||
static void operator delete(void* obj, size_t /*size*/) VL_MT_SAFE {
|
||||
uint8_t* const oldp = (static_cast<uint8_t*>(obj)) - 8;
|
||||
if (VL_UNLIKELY(*(reinterpret_cast<uint32_t*>(oldp)) != activeMagic())) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
@@ -113,7 +114,7 @@ public:
|
||||
static VerilatedVpio* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpio*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
inline vpiHandle castVpiHandle() { return reinterpret_cast<vpiHandle>(this); }
|
||||
vpiHandle castVpiHandle() { return reinterpret_cast<vpiHandle>(this); }
|
||||
// ACCESSORS
|
||||
virtual const char* name() const { return "<null>"; }
|
||||
virtual const char* fullname() const { return "<null>"; }
|
||||
@@ -135,12 +136,12 @@ public:
|
||||
VerilatedVpioTimedCb(uint64_t id, QData time)
|
||||
: m_id{id}
|
||||
, m_time{time} {}
|
||||
virtual ~VerilatedVpioTimedCb() override = default;
|
||||
~VerilatedVpioTimedCb() override = default;
|
||||
static VerilatedVpioTimedCb* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioTimedCb*>(reinterpret_cast<VerilatedVpioTimedCb*>(h));
|
||||
}
|
||||
virtual uint32_t type() const override { return vpiCallback; }
|
||||
virtual PLI_INT32 dovpi_remove_cb() override;
|
||||
uint32_t type() const override { return vpiCallback; }
|
||||
PLI_INT32 dovpi_remove_cb() override;
|
||||
};
|
||||
|
||||
class VerilatedVpioReasonCb final : public VerilatedVpio {
|
||||
@@ -154,12 +155,12 @@ public:
|
||||
VerilatedVpioReasonCb(uint64_t id, PLI_INT32 reason)
|
||||
: m_id{id}
|
||||
, m_reason{reason} {}
|
||||
virtual ~VerilatedVpioReasonCb() override = default;
|
||||
~VerilatedVpioReasonCb() override = default;
|
||||
static VerilatedVpioReasonCb* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioReasonCb*>(reinterpret_cast<VerilatedVpioReasonCb*>(h));
|
||||
}
|
||||
virtual uint32_t type() const override { return vpiCallback; }
|
||||
virtual PLI_INT32 dovpi_remove_cb() override;
|
||||
uint32_t type() const override { return vpiCallback; }
|
||||
PLI_INT32 dovpi_remove_cb() override;
|
||||
};
|
||||
|
||||
class VerilatedVpioConst final : public VerilatedVpio {
|
||||
@@ -168,11 +169,11 @@ class VerilatedVpioConst final : public VerilatedVpio {
|
||||
public:
|
||||
explicit VerilatedVpioConst(int32_t num)
|
||||
: m_num{num} {}
|
||||
virtual ~VerilatedVpioConst() override = default;
|
||||
~VerilatedVpioConst() override = default;
|
||||
static VerilatedVpioConst* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioConst*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
virtual uint32_t type() const override { return vpiConstant; }
|
||||
uint32_t type() const override { return vpiConstant; }
|
||||
int32_t num() const { return m_num; }
|
||||
};
|
||||
|
||||
@@ -200,10 +201,10 @@ public:
|
||||
}
|
||||
const VerilatedVar* varp() const { return m_varp; }
|
||||
const VerilatedScope* scopep() const { return m_scopep; }
|
||||
virtual uint32_t size() const override { return get_range().elements(); }
|
||||
virtual const VerilatedRange* rangep() const override { return &get_range(); }
|
||||
virtual const char* name() const override { return m_varp->name(); }
|
||||
virtual const char* fullname() const override {
|
||||
uint32_t size() const override { return get_range().elements(); }
|
||||
const VerilatedRange* rangep() const override { return &get_range(); }
|
||||
const char* name() const override { return m_varp->name(); }
|
||||
const char* fullname() const override {
|
||||
static VL_THREAD_LOCAL std::string t_out;
|
||||
t_out = std::string{m_scopep->name()} + "." + name();
|
||||
return t_out.c_str();
|
||||
@@ -214,12 +215,12 @@ class VerilatedVpioParam final : public VerilatedVpioVarBase {
|
||||
public:
|
||||
VerilatedVpioParam(const VerilatedVar* varp, const VerilatedScope* scopep)
|
||||
: VerilatedVpioVarBase{varp, scopep} {}
|
||||
virtual ~VerilatedVpioParam() override = default;
|
||||
~VerilatedVpioParam() override = default;
|
||||
|
||||
static VerilatedVpioParam* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioParam*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
virtual uint32_t type() const override { return vpiParameter; }
|
||||
uint32_t type() const override { return vpiParameter; }
|
||||
void* varDatap() const { return m_varp->datap(); }
|
||||
};
|
||||
|
||||
@@ -229,13 +230,13 @@ class VerilatedVpioRange final : public VerilatedVpio {
|
||||
public:
|
||||
explicit VerilatedVpioRange(const VerilatedRange* range)
|
||||
: m_range{range} {}
|
||||
virtual ~VerilatedVpioRange() override = default;
|
||||
~VerilatedVpioRange() override = default;
|
||||
static VerilatedVpioRange* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioRange*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
virtual uint32_t type() const override { return vpiRange; }
|
||||
virtual uint32_t size() const override { return m_range->elements(); }
|
||||
virtual const VerilatedRange* rangep() const override { return m_range; }
|
||||
uint32_t type() const override { return vpiRange; }
|
||||
uint32_t size() const override { return m_range->elements(); }
|
||||
const VerilatedRange* rangep() const override { return m_range; }
|
||||
};
|
||||
|
||||
class VerilatedVpioRangeIter final : public VerilatedVpio {
|
||||
@@ -246,12 +247,12 @@ class VerilatedVpioRangeIter final : public VerilatedVpio {
|
||||
public:
|
||||
explicit VerilatedVpioRangeIter(const VerilatedRange* range)
|
||||
: m_range{range} {}
|
||||
virtual ~VerilatedVpioRangeIter() override = default;
|
||||
~VerilatedVpioRangeIter() override = default;
|
||||
static VerilatedVpioRangeIter* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioRangeIter*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
virtual uint32_t type() const override { return vpiIterator; }
|
||||
virtual vpiHandle dovpi_scan() override {
|
||||
uint32_t type() const override { return vpiIterator; }
|
||||
vpiHandle dovpi_scan() override {
|
||||
if (VL_UNLIKELY(m_done)) {
|
||||
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
||||
return nullptr;
|
||||
@@ -268,14 +269,14 @@ protected:
|
||||
public:
|
||||
explicit VerilatedVpioScope(const VerilatedScope* scopep)
|
||||
: m_scopep{scopep} {}
|
||||
virtual ~VerilatedVpioScope() override = default;
|
||||
~VerilatedVpioScope() override = default;
|
||||
static VerilatedVpioScope* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioScope*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
virtual uint32_t type() const override { return vpiScope; }
|
||||
uint32_t type() const override { return vpiScope; }
|
||||
const VerilatedScope* scopep() const { return m_scopep; }
|
||||
virtual const char* name() const override { return m_scopep->name(); }
|
||||
virtual const char* fullname() const override { return m_scopep->name(); }
|
||||
const char* name() const override { return m_scopep->name(); }
|
||||
const char* fullname() const override { return m_scopep->name(); }
|
||||
};
|
||||
|
||||
class VerilatedVpioVar VL_NOT_FINAL : public VerilatedVpioVarBase {
|
||||
@@ -308,7 +309,7 @@ public:
|
||||
m_mask.u32 = 0;
|
||||
}
|
||||
}
|
||||
virtual ~VerilatedVpioVar() override {
|
||||
~VerilatedVpioVar() override {
|
||||
if (m_prevDatap) VL_DO_CLEAR(delete[] m_prevDatap, m_prevDatap = nullptr);
|
||||
}
|
||||
static VerilatedVpioVar* castp(vpiHandle h) {
|
||||
@@ -318,7 +319,7 @@ public:
|
||||
uint8_t mask_byte(int idx) const { return m_mask.u8[idx & 3]; }
|
||||
uint32_t entSize() const { return m_entSize; }
|
||||
uint32_t index() const { return m_index; }
|
||||
virtual uint32_t type() const override {
|
||||
uint32_t type() const override {
|
||||
return (varp()->dims() > 1) ? vpiMemory : vpiReg; // but might be wire, logic
|
||||
}
|
||||
void* prevDatap() const { return m_prevDatap; }
|
||||
@@ -339,14 +340,14 @@ public:
|
||||
m_index = index;
|
||||
m_varDatap = (static_cast<uint8_t*>(varp->datap())) + entSize() * offset;
|
||||
}
|
||||
virtual ~VerilatedVpioMemoryWord() override = default;
|
||||
~VerilatedVpioMemoryWord() override = default;
|
||||
static VerilatedVpioMemoryWord* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioMemoryWord*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
virtual uint32_t type() const override { return vpiMemoryWord; }
|
||||
virtual uint32_t size() const override { return varp()->packed().elements(); }
|
||||
virtual const VerilatedRange* rangep() const override { return &(varp()->packed()); }
|
||||
virtual const char* fullname() const override {
|
||||
uint32_t type() const override { return vpiMemoryWord; }
|
||||
uint32_t size() const override { return varp()->packed().elements(); }
|
||||
const VerilatedRange* rangep() const override { return &(varp()->packed()); }
|
||||
const char* fullname() const override {
|
||||
static VL_THREAD_LOCAL std::string t_out;
|
||||
constexpr size_t LEN_MAX_INDEX = 25;
|
||||
char num[LEN_MAX_INDEX];
|
||||
@@ -364,12 +365,12 @@ class VerilatedVpioVarIter final : public VerilatedVpio {
|
||||
public:
|
||||
explicit VerilatedVpioVarIter(const VerilatedScope* scopep)
|
||||
: m_scopep{scopep} {}
|
||||
virtual ~VerilatedVpioVarIter() override = default;
|
||||
~VerilatedVpioVarIter() override = default;
|
||||
static VerilatedVpioVarIter* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioVarIter*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
virtual uint32_t type() const override { return vpiIterator; }
|
||||
virtual vpiHandle dovpi_scan() override {
|
||||
uint32_t type() const override { return vpiIterator; }
|
||||
vpiHandle dovpi_scan() override {
|
||||
if (VL_LIKELY(m_scopep->varsp())) {
|
||||
const VerilatedVarNameMap* const varsp = m_scopep->varsp();
|
||||
if (VL_UNLIKELY(!m_started)) {
|
||||
@@ -405,15 +406,15 @@ public:
|
||||
, m_varp{varp}
|
||||
, m_iteration{varp->unpacked().right()}
|
||||
, m_direction{VL_LIKELY(varp->unpacked().left() > varp->unpacked().right()) ? 1 : -1} {}
|
||||
virtual ~VerilatedVpioMemoryWordIter() override = default;
|
||||
~VerilatedVpioMemoryWordIter() override = default;
|
||||
static VerilatedVpioMemoryWordIter* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioMemoryWordIter*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
virtual uint32_t type() const override { return vpiIterator; }
|
||||
uint32_t type() const override { return vpiIterator; }
|
||||
void iterationInc() {
|
||||
if (!(m_done = (m_iteration == m_varp->unpacked().left()))) m_iteration += m_direction;
|
||||
}
|
||||
virtual vpiHandle dovpi_scan() override {
|
||||
vpiHandle dovpi_scan() override {
|
||||
if (VL_UNLIKELY(m_done)) {
|
||||
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
||||
return nullptr;
|
||||
@@ -438,9 +439,9 @@ public:
|
||||
static VerilatedVpioModule* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioModule*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
virtual uint32_t type() const override { return vpiModule; }
|
||||
virtual const char* name() const override { return m_name; }
|
||||
virtual const char* fullname() const override { return m_fullname; }
|
||||
uint32_t type() const override { return vpiModule; }
|
||||
const char* name() const override { return m_name; }
|
||||
const char* fullname() const override { return m_fullname; }
|
||||
};
|
||||
|
||||
class VerilatedVpioModuleIter final : public VerilatedVpio {
|
||||
@@ -452,12 +453,12 @@ public:
|
||||
: m_vec{&vec} {
|
||||
m_it = m_vec->begin();
|
||||
}
|
||||
virtual ~VerilatedVpioModuleIter() override = default;
|
||||
~VerilatedVpioModuleIter() override = default;
|
||||
static VerilatedVpioModuleIter* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioModuleIter*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
virtual uint32_t type() const override { return vpiIterator; }
|
||||
virtual vpiHandle dovpi_scan() override {
|
||||
uint32_t type() const override { return vpiIterator; }
|
||||
vpiHandle dovpi_scan() override {
|
||||
if (m_it == m_vec->end()) {
|
||||
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
||||
return nullptr;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#define VERILATOR_VERILATED_VPI_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated.h"
|
||||
#include "verilated_syms.h"
|
||||
|
||||
@@ -47,7 +48,7 @@ public:
|
||||
static bool callValueCbs() VL_MT_UNSAFE_ONE;
|
||||
/// Call callbacks of arbitrary types.
|
||||
/// User wrapper code should call this from their main loops.
|
||||
static bool callCbs(const uint32_t reason) VL_MT_UNSAFE_ONE;
|
||||
static bool callCbs(uint32_t reason) VL_MT_UNSAFE_ONE;
|
||||
/// Returns time of the next registered VPI callback, or
|
||||
/// ~(0ULL) if none are registered
|
||||
static QData cbNextDeadline() VL_MT_UNSAFE_ONE;
|
||||
|
||||
+50
-26
@@ -39,7 +39,7 @@
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define VL_ATTR_ALIGNED(alignment) __attribute__((aligned(alignment)))
|
||||
# define VL_ATTR_ALWINLINE __attribute__((always_inline))
|
||||
# define VL_ATTR_ALWINLINE __attribute__((always_inline)) inline
|
||||
# define VL_ATTR_NOINLINE __attribute__((noinline))
|
||||
# define VL_ATTR_COLD __attribute__((cold))
|
||||
# define VL_ATTR_HOT __attribute__((hot))
|
||||
@@ -218,22 +218,33 @@
|
||||
// C++-2011
|
||||
|
||||
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(VL_CPPCHECK)
|
||||
# ifndef VL_NO_LEGACY
|
||||
// These are deprecated historical defines. We leave them in case users referenced them.
|
||||
# define VL_EQ_DELETE = delete
|
||||
# define vl_unique_ptr std::unique_ptr
|
||||
# define vl_unordered_map std::unordered_map
|
||||
# define vl_unordered_set std::unordered_set
|
||||
# define VL_INCLUDE_UNORDERED_MAP <unordered_map>
|
||||
# define VL_INCLUDE_UNORDERED_SET <unordered_set>
|
||||
# define VL_FINAL final
|
||||
# define VL_MUTABLE mutable
|
||||
# define VL_OVERRIDE override
|
||||
# endif
|
||||
#else
|
||||
# error "Verilator requires a C++11 or newer compiler"
|
||||
#endif
|
||||
|
||||
#ifndef VL_NO_LEGACY
|
||||
// These are deprecated historical defines. We leave them in case users referenced them.
|
||||
# define VL_EQ_DELETE = delete
|
||||
# define vl_unique_ptr std::unique_ptr
|
||||
# define vl_unordered_map std::unordered_map
|
||||
# define vl_unordered_set std::unordered_set
|
||||
# define VL_INCLUDE_UNORDERED_MAP <unordered_map>
|
||||
# define VL_INCLUDE_UNORDERED_SET <unordered_set>
|
||||
# define VL_FINAL final
|
||||
# define VL_MUTABLE mutable
|
||||
# define VL_OVERRIDE override
|
||||
#endif
|
||||
|
||||
//=========================================================================
|
||||
// C++-2017
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
# define VL_CONSTEXPR_CXX17 constexpr
|
||||
#else
|
||||
# define VL_CONSTEXPR_CXX17
|
||||
#endif
|
||||
|
||||
|
||||
//=========================================================================
|
||||
// Optimization
|
||||
|
||||
@@ -245,14 +256,11 @@
|
||||
// Internal coverage
|
||||
|
||||
#ifdef VL_GCOV
|
||||
extern "C" {
|
||||
void __gcov_flush(); // gcc sources gcc/gcov-io.h has the prototype
|
||||
}
|
||||
// Flush internal code coverage data before e.g. std::abort()
|
||||
# define VL_GCOV_FLUSH() \
|
||||
__gcov_flush()
|
||||
extern "C" void __gcov_dump();
|
||||
// Dump internal code coverage data before e.g. std::abort()
|
||||
# define VL_GCOV_DUMP() __gcov_dump()
|
||||
#else
|
||||
# define VL_GCOV_FLUSH()
|
||||
# define VL_GCOV_DUMP()
|
||||
#endif
|
||||
|
||||
//=========================================================================
|
||||
@@ -446,7 +454,8 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
|
||||
// or 0x0 if not implemented on this platform
|
||||
#define VL_GET_CPU_TICK(val) \
|
||||
{ \
|
||||
uint32_t hi, lo; \
|
||||
uint32_t hi; \
|
||||
uint32_t lo; \
|
||||
asm volatile("rdtsc" : "=a"(lo), "=d"(hi)); \
|
||||
(val) = ((uint64_t)lo) | (((uint64_t)hi) << 32); \
|
||||
}
|
||||
@@ -521,25 +530,40 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
|
||||
#define VL_STRINGIFY(x) VL_STRINGIFY2(x)
|
||||
#define VL_STRINGIFY2(x) #x
|
||||
|
||||
//=========================================================================
|
||||
// Offset of field in type
|
||||
|
||||
// Address zero can cause compiler problems
|
||||
#define VL_OFFSETOF(type, field) \
|
||||
(reinterpret_cast<size_t>(&(reinterpret_cast<type*>(0x10000000)->field)) - 0x10000000)
|
||||
|
||||
//=========================================================================
|
||||
// Conversions
|
||||
|
||||
namespace vlstd {
|
||||
|
||||
template <typename T> struct reverse_wrapper {
|
||||
template <typename T>
|
||||
struct reverse_wrapper {
|
||||
const T& m_v;
|
||||
|
||||
explicit reverse_wrapper(const T& a_v)
|
||||
: m_v(a_v) {}
|
||||
inline auto begin() -> decltype(m_v.rbegin()) { return m_v.rbegin(); }
|
||||
inline auto end() -> decltype(m_v.rend()) { return m_v.rend(); }
|
||||
auto begin() -> decltype(m_v.rbegin()) { return m_v.rbegin(); }
|
||||
auto end() -> decltype(m_v.rend()) { return m_v.rend(); }
|
||||
};
|
||||
|
||||
// C++20's std::ranges::reverse_view
|
||||
template <typename T> reverse_wrapper<T> reverse_view(const T& v) { return reverse_wrapper<T>(v); }
|
||||
template <typename T>
|
||||
reverse_wrapper<T> reverse_view(const T& v) {
|
||||
return reverse_wrapper<T>(v);
|
||||
}
|
||||
|
||||
// C++17's std::as_const
|
||||
template <class T> T const& as_const(T& v) { return v; }
|
||||
template <class T>
|
||||
T const& as_const(T& v) {
|
||||
return v;
|
||||
}
|
||||
|
||||
}; // namespace vlstd
|
||||
|
||||
//=========================================================================
|
||||
|
||||
@@ -48,6 +48,7 @@ exclude_line_regexp(r'\bNUM_ASSERT')
|
||||
exclude_line_regexp(r'\bERROR_RSVD_WORD')
|
||||
exclude_line_regexp(r'\bV3ERROR_NA')
|
||||
exclude_line_regexp(r'\bUINFO\b')
|
||||
exclude_line_regexp(r'\bVL_DEFINE_DEBUG_FUNCTIONS\b')
|
||||
|
||||
# Exclude for branch coverage only
|
||||
exclude_branch_regexp(r'\bdebug\(\)')
|
||||
|
||||
+13
-6
@@ -79,7 +79,7 @@ ifeq ($(VL_NOOPT),1)
|
||||
CPPFLAGS += -O0
|
||||
else ifeq ($(VL_DEBUG),)
|
||||
# Optimize
|
||||
CPPFLAGS += -O2
|
||||
CPPFLAGS += -O3
|
||||
else
|
||||
# Debug
|
||||
CPPFLAGS += @CFG_CXXFLAGS_DEBUG@ -DVL_DEBUG -D_GLIBCXX_DEBUG
|
||||
@@ -276,9 +276,16 @@ VLCOV_OBJS = \
|
||||
|
||||
NON_STANDALONE_HEADERS = \
|
||||
V3AstInlines.h \
|
||||
V3AstNodes.h \
|
||||
V3AstNodeDType.h \
|
||||
V3AstNodeMath.h \
|
||||
V3AstNodeOther.h \
|
||||
V3WidthCommit.h \
|
||||
|
||||
AST_DEFS := \
|
||||
V3AstNodeDType.h \
|
||||
V3AstNodeMath.h \
|
||||
V3AstNodeOther.h \
|
||||
|
||||
#### Linking
|
||||
|
||||
ifeq ($(VL_VLCOV),)
|
||||
@@ -301,8 +308,8 @@ V3Number_test: V3Number_test.o
|
||||
|
||||
#### Modules
|
||||
|
||||
%__gen.cpp: %.cpp $(ASTGEN) V3Ast.h V3AstNodes.h
|
||||
$(PYTHON3) $(ASTGEN) -I $(srcdir) $*.cpp
|
||||
%__gen.cpp: %.cpp $(ASTGEN) $(AST_DEFS)
|
||||
$(PYTHON3) $(ASTGEN) -I $(srcdir) $(foreach f,$(AST_DEFS),--astdef $f) $*.cpp
|
||||
|
||||
%.o: %.cpp
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSWALL} -c $< -o $@
|
||||
@@ -332,8 +339,8 @@ vlcovgen.d: $(VLCOVGEN) $(srcdir)/../include/verilated_cov_key.h
|
||||
$(PYTHON3) $(VLCOVGEN) --srcdir $(srcdir)
|
||||
touch $@
|
||||
|
||||
V3Ast__gen_classes.h : $(ASTGEN) V3Ast.h V3AstNodes.h
|
||||
$(PYTHON3) $(ASTGEN) -I $(srcdir) --classes
|
||||
V3Ast__gen_classes.h : $(ASTGEN) $(AST_DEFS)
|
||||
$(PYTHON3) $(ASTGEN) -I $(srcdir) $(foreach f,$(AST_DEFS),--astdef $f) --classes
|
||||
|
||||
V3ParseBison.h: V3ParseBison.c
|
||||
|
||||
|
||||
+51
-55
@@ -29,14 +29,17 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Active.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Const.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Graph.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//***** See below for main transformation engine
|
||||
|
||||
//######################################################################
|
||||
@@ -65,8 +68,8 @@ public:
|
||||
: 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"; }
|
||||
string name() const override { return m_name + " " + typestr(); }
|
||||
string dotColor() const override { return user() ? "green" : "black"; }
|
||||
virtual int type() const { return m_type; }
|
||||
};
|
||||
|
||||
@@ -75,11 +78,9 @@ public:
|
||||
|
||||
class LatchDetectGraph final : public V3Graph {
|
||||
protected:
|
||||
LatchDetectGraphVertex* m_curVertexp; // Current latch detection graph vertex
|
||||
LatchDetectGraphVertex* m_curVertexp = nullptr; // Current latch detection graph vertex
|
||||
std::vector<AstVarRef*> m_outputs; // Vector of lvalues encountered on this pass
|
||||
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
static LatchDetectGraphVertex* castVertexp(void* vertexp) {
|
||||
return reinterpret_cast<LatchDetectGraphVertex*>(vertexp);
|
||||
}
|
||||
@@ -119,7 +120,7 @@ protected:
|
||||
|
||||
public:
|
||||
LatchDetectGraph() { clear(); }
|
||||
virtual ~LatchDetectGraph() override { clear(); }
|
||||
~LatchDetectGraph() override { clear(); }
|
||||
// ACCESSORS
|
||||
LatchDetectGraphVertex* currentp() { return m_curVertexp; }
|
||||
void currentp(LatchDetectGraphVertex* vertex) { m_curVertexp = vertex; }
|
||||
@@ -182,7 +183,7 @@ public:
|
||||
<< " (not all control paths of combinational always assign a value)\n"
|
||||
<< nodep->warnMore()
|
||||
<< "... Suggest use of always_latch for intentional latches");
|
||||
if (debug() >= 9) dumpDotFilePrefixed("latch_" + vrp->name());
|
||||
if (dumpGraph() >= 9) dumpDotFilePrefixed("latch_" + vrp->name());
|
||||
}
|
||||
vertp->user(false); // Clear again (see above)
|
||||
vrp->varp()->isLatched(latch_detected);
|
||||
@@ -197,12 +198,7 @@ public:
|
||||
//######################################################################
|
||||
// Collect existing active names
|
||||
|
||||
class ActiveBaseVisitor VL_NOT_FINAL : public VNVisitor {
|
||||
protected:
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
};
|
||||
|
||||
class ActiveNamer final : public ActiveBaseVisitor {
|
||||
class ActiveNamer final : public VNVisitor {
|
||||
private:
|
||||
// STATE
|
||||
AstScope* m_scopep = nullptr; // Current scope to add statement to
|
||||
@@ -215,10 +211,10 @@ private:
|
||||
// METHODS
|
||||
void addActive(AstActive* nodep) {
|
||||
UASSERT_OBJ(m_scopep, nodep, "nullptr scope");
|
||||
m_scopep->addActivep(nodep);
|
||||
m_scopep->addBlocksp(nodep);
|
||||
}
|
||||
// VISITORS
|
||||
virtual void visit(AstScope* nodep) override {
|
||||
void visit(AstScope* nodep) override {
|
||||
m_scopep = nodep;
|
||||
m_iActivep = nullptr;
|
||||
m_cActivep = nullptr;
|
||||
@@ -226,13 +222,13 @@ private:
|
||||
iterateChildren(nodep);
|
||||
// Don't clear scopep, the namer persists beyond this visit
|
||||
}
|
||||
virtual void visit(AstSenTree* nodep) override {
|
||||
void visit(AstSenTree* nodep) override {
|
||||
// Simplify sensitivity list
|
||||
VL_DO_DANGLING(V3Const::constifyExpensiveEdit(nodep), nodep);
|
||||
}
|
||||
//--------------------
|
||||
virtual void visit(AstNodeStmt*) override {} // Accelerate
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNodeStmt*) override {} // Accelerate
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
@@ -274,14 +270,14 @@ public:
|
||||
|
||||
// CONSTRUCTORS
|
||||
ActiveNamer() = default;
|
||||
virtual ~ActiveNamer() override = default;
|
||||
~ActiveNamer() override = default;
|
||||
void main(AstScope* nodep) { iterate(nodep); }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Latch checking visitor
|
||||
|
||||
class ActiveLatchCheckVisitor final : public ActiveBaseVisitor {
|
||||
class ActiveLatchCheckVisitor final : public VNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Input:
|
||||
@@ -290,25 +286,25 @@ private:
|
||||
// STATE
|
||||
LatchDetectGraph m_graph; // Graph used to detect latches in combo always
|
||||
// VISITORS
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
void visit(AstVarRef* nodep) override {
|
||||
const AstVar* const varp = nodep->varp();
|
||||
if (nodep->access().isWriteOrRW() && varp->isSignal() && !varp->isUsedLoopIdx()) {
|
||||
m_graph.addAssignment(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeIf* nodep) {
|
||||
void visit(AstNodeIf* nodep) override {
|
||||
if (!nodep->isBoundsCheck()) {
|
||||
LatchDetectGraphVertex* const parentp = m_graph.currentp();
|
||||
LatchDetectGraphVertex* const branchp = m_graph.addPathVertex(parentp, "BRANCH", true);
|
||||
m_graph.addPathVertex(branchp, "IF");
|
||||
iterateAndNextNull(nodep->ifsp());
|
||||
iterateAndNextNull(nodep->thensp());
|
||||
m_graph.addPathVertex(branchp, "ELSE");
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
m_graph.currentp(parentp);
|
||||
}
|
||||
}
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) { iterateChildren(nodep); }
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -317,13 +313,13 @@ public:
|
||||
iterate(nodep);
|
||||
m_graph.latchCheck(nodep, kwd == VAlwaysKwd::ALWAYS_LATCH);
|
||||
}
|
||||
virtual ~ActiveLatchCheckVisitor() = default;
|
||||
~ActiveLatchCheckVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Replace unsupported non-blocking assignments with blocking assignments
|
||||
|
||||
class ActiveDlyVisitor final : public ActiveBaseVisitor {
|
||||
class ActiveDlyVisitor final : public VNVisitor {
|
||||
public:
|
||||
enum CheckType : uint8_t { CT_SEQ, CT_COMB, CT_INITIAL };
|
||||
|
||||
@@ -332,7 +328,7 @@ private:
|
||||
const CheckType m_check; // Process type we are checking
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstAssignDly* nodep) override {
|
||||
void visit(AstAssignDly* nodep) override {
|
||||
// Non-blocking assignments are OK in sequential processes
|
||||
if (m_check == CT_SEQ) return;
|
||||
|
||||
@@ -356,7 +352,7 @@ private:
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstAssign* nodep) override {
|
||||
void visit(AstAssign* nodep) override {
|
||||
// Blocking assignments are always OK in combinational (and initial/final) processes
|
||||
if (m_check != CT_SEQ) return;
|
||||
|
||||
@@ -379,7 +375,7 @@ private:
|
||||
}
|
||||
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -387,13 +383,13 @@ public:
|
||||
: m_check{check} {
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~ActiveDlyVisitor() override = default;
|
||||
~ActiveDlyVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Active class functions
|
||||
|
||||
class ActiveVisitor final : public ActiveBaseVisitor {
|
||||
class ActiveVisitor final : public VNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Each call to V3Const::constify
|
||||
@@ -406,7 +402,7 @@ private:
|
||||
bool m_itemSequent = false; // Found a SenItem sequential
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstScope* nodep) override {
|
||||
void visit(AstScope* nodep) override {
|
||||
// Create required actives and add to scope
|
||||
UINFO(4, " SCOPE " << nodep << endl);
|
||||
// Clear last scope's names, and collect this scope's existing names
|
||||
@@ -414,10 +410,10 @@ private:
|
||||
m_scopeFinalp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstActive* nodep) override {
|
||||
void visit(AstActive* nodep) override {
|
||||
// Actives are being formed, so we can ignore any already made
|
||||
}
|
||||
virtual void visit(AstInitialStatic* nodep) override {
|
||||
void visit(AstInitialStatic* nodep) override {
|
||||
// Relink to IACTIVE, unless already under it
|
||||
UINFO(4, " INITIAL " << nodep << endl);
|
||||
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
|
||||
@@ -425,7 +421,7 @@ private:
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
}
|
||||
virtual void visit(AstInitial* nodep) override {
|
||||
void visit(AstInitial* nodep) override {
|
||||
// Relink to IACTIVE, unless already under it
|
||||
UINFO(4, " INITIAL " << nodep << endl);
|
||||
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
|
||||
@@ -433,31 +429,31 @@ private:
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
}
|
||||
virtual void visit(AstAssignAlias* nodep) override {
|
||||
void visit(AstAssignAlias* nodep) override {
|
||||
// Relink to CACTIVE, unless already under it
|
||||
UINFO(4, " ASSIGNW " << nodep << endl);
|
||||
AstActive* const wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
}
|
||||
virtual void visit(AstAssignW* nodep) override {
|
||||
void visit(AstAssignW* nodep) override {
|
||||
// Relink to CACTIVE, unless already under it
|
||||
UINFO(4, " ASSIGNW " << nodep << endl);
|
||||
AstActive* const wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
}
|
||||
virtual void visit(AstCoverToggle* nodep) override {
|
||||
void visit(AstCoverToggle* nodep) override {
|
||||
// Relink to CACTIVE, unless already under it
|
||||
UINFO(4, " COVERTOGGLE " << nodep << endl);
|
||||
AstActive* const wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
}
|
||||
virtual void visit(AstFinal* nodep) override {
|
||||
void visit(AstFinal* nodep) override {
|
||||
// Relink to CFUNC for the final
|
||||
UINFO(4, " FINAL " << nodep << endl);
|
||||
if (!nodep->bodysp()) { // Empty, Kill it.
|
||||
if (!nodep->stmtsp()) { // Empty, Kill it.
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
@@ -470,10 +466,10 @@ private:
|
||||
m_scopeFinalp->isStatic(false);
|
||||
m_scopeFinalp->isLoose(true);
|
||||
m_scopeFinalp->slow(true);
|
||||
m_namer.scopep()->addActivep(m_scopeFinalp);
|
||||
m_namer.scopep()->addBlocksp(m_scopeFinalp);
|
||||
}
|
||||
nodep->unlinkFrBack();
|
||||
m_scopeFinalp->addStmtsp(nodep->bodysp()->unlinkFrBackWithNext());
|
||||
m_scopeFinalp->addStmtsp(nodep->stmtsp()->unlinkFrBackWithNext());
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
|
||||
@@ -530,39 +526,39 @@ private:
|
||||
|
||||
// Warn and/or convert any delayed assignments
|
||||
if (combo && !sequent) {
|
||||
ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_COMB};
|
||||
{ ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_COMB}; }
|
||||
const ActiveLatchCheckVisitor latchvisitor{nodep, kwd};
|
||||
} else if (!combo && sequent) {
|
||||
ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_SEQ};
|
||||
}
|
||||
}
|
||||
virtual void visit(AstAlways* nodep) override {
|
||||
void visit(AstAlways* nodep) override {
|
||||
// Move always to appropriate ACTIVE based on its sense list
|
||||
UINFO(4, " ALW " << nodep << endl);
|
||||
// if (debug() >= 9) nodep->dumpTree(cout, " Alw: ");
|
||||
|
||||
if (!nodep->bodysp()) {
|
||||
if (!nodep->stmtsp()) {
|
||||
// Empty always. Kill it.
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
visitAlways(nodep, nodep->sensesp(), nodep->keyword());
|
||||
}
|
||||
virtual void visit(AstAlwaysPostponed* nodep) override {
|
||||
void visit(AstAlwaysPostponed* nodep) override {
|
||||
UINFO(4, " ALW " << nodep << endl);
|
||||
if (!nodep->bodysp()) {
|
||||
if (!nodep->stmtsp()) {
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
visitAlways(nodep, nullptr, VAlwaysKwd::ALWAYS);
|
||||
}
|
||||
virtual void visit(AstAlwaysPublic* nodep) override {
|
||||
void visit(AstAlwaysPublic* nodep) override {
|
||||
// Move always to appropriate ACTIVE based on its sense list
|
||||
UINFO(4, " ALWPub " << nodep << endl);
|
||||
// if (debug() >= 9) nodep->dumpTree(cout, " Alw: ");
|
||||
visitAlways(nodep, nodep->sensesp(), VAlwaysKwd::ALWAYS);
|
||||
}
|
||||
virtual void visit(AstSenItem* nodep) override {
|
||||
void visit(AstSenItem* nodep) override {
|
||||
if (nodep->varrefp()) {
|
||||
if (const AstBasicDType* const basicp = nodep->varrefp()->dtypep()->basicp()) {
|
||||
if (basicp->isEventValue()) {
|
||||
@@ -591,14 +587,14 @@ private:
|
||||
}
|
||||
|
||||
//--------------------
|
||||
virtual void visit(AstNodeMath*) override {} // Accelerate
|
||||
virtual void visit(AstVarScope*) override {} // Accelerate
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNodeMath*) override {} // Accelerate
|
||||
void visit(AstVarScope*) override {} // Accelerate
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit ActiveVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~ActiveVisitor() override = default;
|
||||
~ActiveVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -607,5 +603,5 @@ public:
|
||||
void V3Active::activeAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ ActiveVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("active", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("active", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+16
-14
@@ -26,11 +26,14 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3ActiveTop.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3SenTree.h"
|
||||
#include "V3Const.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3SenTree.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Active class functions
|
||||
@@ -48,16 +51,15 @@ private:
|
||||
SenTreeFinder m_finder; // Find global sentree's / add them under the AstTopScope
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
// Create required actives and add to module
|
||||
// We can start ordering at a module, or a scope
|
||||
UINFO(4, " MOD " << nodep << endl);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstActive* nodep) override {
|
||||
void visit(AstActive* nodep) override {
|
||||
UINFO(4, " ACTIVE " << nodep << endl);
|
||||
// Remove duplicate clocks and such; sensesp() may change!
|
||||
V3Const::constifyExpensiveEdit(nodep);
|
||||
@@ -100,27 +102,27 @@ private:
|
||||
// No need to do statements under it, they're already moved.
|
||||
// iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeProcedure* nodep) override { // LCOV_EXCL_LINE
|
||||
void visit(AstNodeProcedure* nodep) override { // LCOV_EXCL_LINE
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
}
|
||||
virtual void visit(AstAssignAlias* nodep) override { // LCOV_EXCL_LINE
|
||||
void visit(AstAssignAlias* nodep) override { // LCOV_EXCL_LINE
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
}
|
||||
virtual void visit(AstAssignW* nodep) override { // LCOV_EXCL_LINE
|
||||
void visit(AstAssignW* nodep) override { // LCOV_EXCL_LINE
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
}
|
||||
virtual void visit(AstAlwaysPublic* nodep) override { // LCOV_EXCL_LINE
|
||||
void visit(AstAlwaysPublic* nodep) override { // LCOV_EXCL_LINE
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
}
|
||||
//--------------------
|
||||
virtual void visit(AstNodeMath*) override {} // Accelerate
|
||||
virtual void visit(AstVarScope*) override {} // Accelerate
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNodeMath*) override {} // Accelerate
|
||||
void visit(AstVarScope*) override {} // Accelerate
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit ActiveTopVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~ActiveTopVisitor() override = default;
|
||||
~ActiveTopVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -129,5 +131,5 @@ public:
|
||||
void V3ActiveTop::activeTopAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ ActiveTopVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("activetop", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("activetop", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+29
-26
@@ -17,11 +17,14 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Assert.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Assert class functions
|
||||
|
||||
@@ -69,7 +72,7 @@ private:
|
||||
if (!m_monitorNumVarp) {
|
||||
m_monitorNumVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorNum",
|
||||
nodep->findUInt64DType()};
|
||||
v3Global.rootp()->dollarUnitPkgAddp()->addStmtp(m_monitorNumVarp);
|
||||
v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorNumVarp);
|
||||
}
|
||||
const auto varrefp = new AstVarRef(nodep->fileline(), m_monitorNumVarp, access);
|
||||
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
|
||||
@@ -79,7 +82,7 @@ private:
|
||||
if (!m_monitorOffVarp) {
|
||||
m_monitorOffVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorOff",
|
||||
nodep->findBitDType()};
|
||||
v3Global.rootp()->dollarUnitPkgAddp()->addStmtp(m_monitorOffVarp);
|
||||
v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorOffVarp);
|
||||
}
|
||||
const auto varrefp = new AstVarRef(nodep->fileline(), m_monitorOffVarp, access);
|
||||
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
|
||||
@@ -146,7 +149,7 @@ private:
|
||||
selfDestruct = true;
|
||||
} else {
|
||||
// V3Coverage assigned us a bucket to increment.
|
||||
AstCoverInc* const covincp = VN_AS(snodep->coverincp(), CoverInc);
|
||||
AstCoverInc* const covincp = VN_AS(snodep->coverincsp(), CoverInc);
|
||||
UASSERT_OBJ(covincp, snodep, "Missing AstCoverInc under assertion");
|
||||
covincp->unlinkFrBackWithNext(); // next() might have AstAssign for trace
|
||||
if (message != "") covincp->declp()->comment(message);
|
||||
@@ -195,7 +198,7 @@ private:
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstIf* nodep) override {
|
||||
void visit(AstIf* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
if (nodep->uniquePragma() || nodep->unique0Pragma()) {
|
||||
const AstNodeIf* ifp = nodep;
|
||||
@@ -208,7 +211,7 @@ private:
|
||||
iterateAndNextNull(ifp->condp());
|
||||
|
||||
// Recurse into the true case.
|
||||
iterateAndNextNull(ifp->ifsp());
|
||||
iterateAndNextNull(ifp->thensp());
|
||||
|
||||
// If the last else is not an else if, recurse into that too.
|
||||
if (ifp->elsesp() && !nextifp) { //
|
||||
@@ -253,7 +256,7 @@ private:
|
||||
}
|
||||
|
||||
//========== Case assertions
|
||||
virtual void visit(AstCase* nodep) override {
|
||||
void visit(AstCase* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->user1SetOnce()) {
|
||||
bool has_default = false;
|
||||
@@ -322,7 +325,7 @@ private:
|
||||
}
|
||||
|
||||
//========== Past
|
||||
virtual void visit(AstPast* nodep) override {
|
||||
void visit(AstPast* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
uint32_t ticks = 1;
|
||||
if (nodep->ticksp()) {
|
||||
@@ -337,28 +340,28 @@ private:
|
||||
sentreep->unlinkFrBack();
|
||||
AstAlways* const alwaysp
|
||||
= new AstAlways(nodep->fileline(), VAlwaysKwd::ALWAYS, sentreep, nullptr);
|
||||
m_modp->addStmtp(alwaysp);
|
||||
m_modp->addStmtsp(alwaysp);
|
||||
for (uint32_t i = 0; i < ticks; ++i) {
|
||||
AstVar* const outvarp = new AstVar(
|
||||
nodep->fileline(), VVarType::MODULETEMP,
|
||||
"_Vpast_" + cvtToStr(m_modPastNum++) + "_" + cvtToStr(i), inp->dtypep());
|
||||
m_modp->addStmtp(outvarp);
|
||||
m_modp->addStmtsp(outvarp);
|
||||
AstNode* const assp = new AstAssignDly(
|
||||
nodep->fileline(), new AstVarRef(nodep->fileline(), outvarp, VAccess::WRITE), inp);
|
||||
alwaysp->addStmtp(assp);
|
||||
alwaysp->addStmtsp(assp);
|
||||
// if (debug() >= 9) assp->dumpTree(cout, "-ass: ");
|
||||
invarp = outvarp;
|
||||
inp = new AstVarRef(nodep->fileline(), invarp, VAccess::READ);
|
||||
}
|
||||
nodep->replaceWith(inp);
|
||||
}
|
||||
virtual void visit(AstSampled* nodep) override {
|
||||
void visit(AstSampled* nodep) override {
|
||||
nodep->replaceWith(nodep->exprp()->unlinkFrBack());
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
|
||||
//========== Statements
|
||||
virtual void visit(AstDisplay* nodep) override {
|
||||
void visit(AstDisplay* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
// Replace the special types with standard text
|
||||
if (nodep->displayType() == VDisplayType::DT_INFO) {
|
||||
@@ -386,7 +389,7 @@ private:
|
||||
stmtsp};
|
||||
ifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||
AstNode* const newp = new AstAlwaysPostponed{fl, ifp};
|
||||
m_modp->addStmtp(newp);
|
||||
m_modp->addStmtsp(newp);
|
||||
} else if (nodep->displayType() == VDisplayType::DT_STROBE) {
|
||||
nodep->displayType(VDisplayType::DT_DISPLAY);
|
||||
// Need one-shot
|
||||
@@ -394,7 +397,7 @@ private:
|
||||
const auto varp
|
||||
= new AstVar{fl, VVarType::MODULETEMP, "__Vstrobe" + cvtToStr(m_modStrobeNum++),
|
||||
nodep->findBitDType()};
|
||||
m_modp->addStmtp(varp);
|
||||
m_modp->addStmtsp(varp);
|
||||
// Where $strobe was we do "__Vstrobe = '1;"
|
||||
const auto newsetp = new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
|
||||
new AstConst{fl, AstConst::BitTrue{}}};
|
||||
@@ -406,35 +409,35 @@ private:
|
||||
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);
|
||||
m_modp->addStmtsp(newp);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstMonitorOff* nodep) override {
|
||||
void visit(AstMonitorOff* nodep) override {
|
||||
const auto newp
|
||||
= new AstAssign(nodep->fileline(), newMonitorOffVarRefp(nodep, VAccess::WRITE),
|
||||
new AstConst(nodep->fileline(), AstConst::BitTrue{}, nodep->off()));
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
virtual void visit(AstAssert* nodep) override {
|
||||
void visit(AstAssert* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
newPslAssertion(nodep, nodep->failsp());
|
||||
}
|
||||
virtual void visit(AstAssertIntrinsic* nodep) override {
|
||||
void visit(AstAssertIntrinsic* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
newPslAssertion(nodep, nodep->failsp());
|
||||
}
|
||||
virtual void visit(AstCover* nodep) override {
|
||||
void visit(AstCover* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
newPslAssertion(nodep, nullptr);
|
||||
}
|
||||
virtual void visit(AstRestrict* nodep) override {
|
||||
void visit(AstRestrict* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
// IEEE says simulator ignores these
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
VL_RESTORER(m_modPastNum);
|
||||
VL_RESTORER(m_modStrobeNum);
|
||||
@@ -445,7 +448,7 @@ private:
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstBegin* nodep) override {
|
||||
void visit(AstBegin* nodep) override {
|
||||
// This code is needed rather than a visitor in V3Begin,
|
||||
// because V3Assert is called before V3Begin
|
||||
VL_RESTORER(m_beginp);
|
||||
@@ -455,12 +458,12 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit AssertVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~AssertVisitor() override {
|
||||
~AssertVisitor() override {
|
||||
V3Stats::addStat("Assertions, assert non-immediate statements", m_statAsNotImm);
|
||||
V3Stats::addStat("Assertions, assert immediate statements", m_statAsImm);
|
||||
V3Stats::addStat("Assertions, cover statements", m_statCover);
|
||||
@@ -474,5 +477,5 @@ public:
|
||||
void V3Assert::assertAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ AssertVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("assert", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("assert", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+1
-1
@@ -20,8 +20,8 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3Error.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
|
||||
+18
-16
@@ -20,9 +20,12 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3AssertPre.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Assert class functions
|
||||
@@ -44,7 +47,6 @@ private:
|
||||
AstNode* m_disablep = nullptr; // Last disable
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
AstSenTree* newSenTree(AstNode* nodep) {
|
||||
// Create sentree based on clocked or default clock
|
||||
@@ -68,7 +70,7 @@ private:
|
||||
|
||||
// VISITORS
|
||||
//========== Statements
|
||||
virtual void visit(AstClocking* nodep) override {
|
||||
void visit(AstClocking* nodep) override {
|
||||
UINFO(8, " CLOCKING" << nodep << endl);
|
||||
// Store the new default clock, reset on new module
|
||||
m_seniDefaultp = nodep->sensesp();
|
||||
@@ -80,14 +82,14 @@ private:
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
virtual void visit(AstAlways* nodep) override {
|
||||
void visit(AstAlways* nodep) override {
|
||||
iterateAndNextNull(nodep->sensesp());
|
||||
if (nodep->sensesp()) m_seniAlwaysp = nodep->sensesp()->sensesp();
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
m_seniAlwaysp = nullptr;
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeCoverOrAssert* nodep) override {
|
||||
void visit(AstNodeCoverOrAssert* nodep) override {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
clearAssertInfo();
|
||||
// Find Clocking's buried under nodep->exprsp
|
||||
@@ -95,7 +97,7 @@ private:
|
||||
if (!nodep->immediate()) nodep->sentreep(newSenTree(nodep));
|
||||
clearAssertInfo();
|
||||
}
|
||||
virtual void visit(AstFell* nodep) override {
|
||||
void visit(AstFell* nodep) override {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
iterateChildren(nodep);
|
||||
FileLine* const fl = nodep->fileline();
|
||||
@@ -109,12 +111,12 @@ private:
|
||||
nodep->sentreep(newSenTree(nodep));
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
virtual void visit(AstPast* nodep) override {
|
||||
void visit(AstPast* nodep) override {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
iterateChildren(nodep);
|
||||
nodep->sentreep(newSenTree(nodep));
|
||||
}
|
||||
virtual void visit(AstRose* nodep) override {
|
||||
void visit(AstRose* nodep) override {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
iterateChildren(nodep);
|
||||
FileLine* const fl = nodep->fileline();
|
||||
@@ -128,7 +130,7 @@ private:
|
||||
nodep->sentreep(newSenTree(nodep));
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
virtual void visit(AstStable* nodep) override {
|
||||
void visit(AstStable* nodep) override {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
iterateChildren(nodep);
|
||||
FileLine* const fl = nodep->fileline();
|
||||
@@ -142,7 +144,7 @@ private:
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstImplication* nodep) override {
|
||||
void visit(AstImplication* nodep) override {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
|
||||
FileLine* const fl = nodep->fileline();
|
||||
@@ -160,7 +162,7 @@ private:
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstPropClocked* nodep) override {
|
||||
void visit(AstPropClocked* nodep) override {
|
||||
// No need to iterate the body, once replace will get iterated
|
||||
iterateAndNextNull(nodep->sensesp());
|
||||
if (m_senip)
|
||||
@@ -182,12 +184,12 @@ private:
|
||||
nodep->replaceWith(blockp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
// Reset defaults
|
||||
m_seniDefaultp = nullptr;
|
||||
}
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -196,7 +198,7 @@ public:
|
||||
// Process
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~AssertPreVisitor() override = default;
|
||||
~AssertPreVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -205,5 +207,5 @@ public:
|
||||
void V3AssertPre::assertPreAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ AssertPreVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("assertpre", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("assertpre", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+85
-64
@@ -18,15 +18,18 @@
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3File.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include "V3Broken.h"
|
||||
#include "V3EmitV.h"
|
||||
#include "V3File.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3String.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//======================================================================
|
||||
// Statics
|
||||
|
||||
@@ -164,22 +167,22 @@ string AstNode::prettyName(const string& namein) {
|
||||
continue;
|
||||
}
|
||||
if (pos[0] == '_' && pos[1] == '_') { // Short-circuit
|
||||
if (0 == strncmp(pos, "__BRA__", 7)) {
|
||||
if (0 == std::strncmp(pos, "__BRA__", 7)) {
|
||||
pretty += "[";
|
||||
pos += 7;
|
||||
continue;
|
||||
}
|
||||
if (0 == strncmp(pos, "__KET__", 7)) {
|
||||
if (0 == std::strncmp(pos, "__KET__", 7)) {
|
||||
pretty += "]";
|
||||
pos += 7;
|
||||
continue;
|
||||
}
|
||||
if (0 == strncmp(pos, "__DOT__", 7)) {
|
||||
if (0 == std::strncmp(pos, "__DOT__", 7)) {
|
||||
pretty += ".";
|
||||
pos += 7;
|
||||
continue;
|
||||
}
|
||||
if (0 == strncmp(pos, "__PVT__", 7)) {
|
||||
if (0 == std::strncmp(pos, "__PVT__", 7)) {
|
||||
pretty += "";
|
||||
pos += 7;
|
||||
continue;
|
||||
@@ -205,14 +208,13 @@ string AstNode::prettyName(const string& namein) {
|
||||
|
||||
string AstNode::prettyTypeName() const {
|
||||
if (name() == "") return typeName();
|
||||
return string(typeName()) + " '" + prettyName() + "'";
|
||||
return std::string{typeName()} + " '" + prettyName() + "'";
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// Insertion
|
||||
|
||||
inline void AstNode::debugTreeChange(const AstNode* nodep, const char* prefix, int lineno,
|
||||
bool next){
|
||||
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
|
||||
@@ -233,7 +235,8 @@ inline void AstNode::debugTreeChange(const AstNode* nodep, const char* prefix, i
|
||||
#endif
|
||||
}
|
||||
|
||||
AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp) {
|
||||
template <>
|
||||
AstNode* AstNode::addNext<AstNode, AstNode>(AstNode* nodep, AstNode* newp) {
|
||||
// Add to m_nextp, returns this
|
||||
UDEBUGONLY(UASSERT_OBJ(newp, nodep, "Null item passed to addNext"););
|
||||
debugTreeChange(nodep, "-addNextThs: ", __LINE__, false);
|
||||
@@ -271,23 +274,18 @@ AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp) {
|
||||
return nodep;
|
||||
}
|
||||
|
||||
AstNode* AstNode::addNextNull(AstNode* nodep, AstNode* newp) {
|
||||
if (!newp) return nodep;
|
||||
return addNext(nodep, newp);
|
||||
}
|
||||
|
||||
void AstNode::addNextHere(AstNode* newp) {
|
||||
// Add to m_nextp on exact node passed, not at the end.
|
||||
// This could be at head, tail, or both (single)
|
||||
// New could be head of single node, or list
|
||||
UASSERT(newp, "Null item passed to addNext");
|
||||
UASSERT(!newp->backp(), "New node (back) already assigned?");
|
||||
UASSERT_OBJ(!newp->backp(), newp, "New node (back) already assigned?");
|
||||
debugTreeChange(this, "-addHereThs: ", __LINE__, false);
|
||||
debugTreeChange(newp, "-addHereNew: ", __LINE__, true);
|
||||
newp->editCountInc();
|
||||
|
||||
AstNode* const addlastp = newp->m_headtailp; // Last node in list to be added
|
||||
UASSERT(!addlastp->m_nextp, "Headtailp tail isn't at the tail");
|
||||
UASSERT_OBJ(!addlastp->m_nextp, addlastp, "Headtailp tail isn't at the tail");
|
||||
|
||||
// Forward links
|
||||
AstNode* const oldnextp = this->m_nextp;
|
||||
@@ -437,7 +435,7 @@ void VNRelinker::dump(std::ostream& str) const {
|
||||
AstNode* AstNode::unlinkFrBackWithNext(VNRelinker* linkerp) {
|
||||
debugTreeChange(this, "-unlinkWNextThs: ", __LINE__, true);
|
||||
AstNode* const oldp = this;
|
||||
UASSERT(oldp->m_backp, "Node has no back, already unlinked?");
|
||||
UASSERT_OBJ(oldp->m_backp, oldp, "Node has no back, already unlinked?");
|
||||
oldp->editCountInc();
|
||||
AstNode* const backp = oldp->m_backp;
|
||||
if (linkerp) {
|
||||
@@ -497,7 +495,7 @@ AstNode* AstNode::unlinkFrBackWithNext(VNRelinker* linkerp) {
|
||||
AstNode* AstNode::unlinkFrBack(VNRelinker* linkerp) {
|
||||
debugTreeChange(this, "-unlinkFrBkThs: ", __LINE__, true);
|
||||
AstNode* const oldp = this;
|
||||
UASSERT(oldp->m_backp, "Node has no back, already unlinked?");
|
||||
UASSERT_OBJ(oldp->m_backp, oldp, "Node has no back, already unlinked?");
|
||||
oldp->editCountInc();
|
||||
AstNode* const backp = oldp->m_backp;
|
||||
if (linkerp) {
|
||||
@@ -565,7 +563,7 @@ void AstNode::relink(VNRelinker* linkerp) {
|
||||
}
|
||||
AstNode* const newp = this;
|
||||
UASSERT(linkerp && linkerp->m_backp, "Need non-empty linker");
|
||||
UASSERT(!newp->backp(), "New node already linked?");
|
||||
UASSERT_OBJ(!newp->m_backp, newp, "New node already linked?");
|
||||
newp->editCountInc();
|
||||
|
||||
if (debug() > 8) {
|
||||
@@ -631,11 +629,56 @@ void AstNode::relinkOneLink(AstNode*& pointpr, // Ref to pointer that gets set
|
||||
}
|
||||
|
||||
void AstNode::addHereThisAsNext(AstNode* newp) {
|
||||
// {old}->this->{next} becomes {old}->new->this->{next}
|
||||
VNRelinker handle;
|
||||
this->unlinkFrBackWithNext(&handle);
|
||||
newp->addNext(this);
|
||||
handle.relink(newp);
|
||||
// {back}->this->{next} becomes {back}->new->this->{next}
|
||||
UASSERT_OBJ(!newp->backp(), newp, "New node already linked?");
|
||||
UASSERT_OBJ(this->m_backp, this, "'this' node has no back, already unlinked?");
|
||||
UASSERT_OBJ(newp->m_headtailp, newp, "m_headtailp not set on new node");
|
||||
//
|
||||
AstNode* const backp = this->m_backp;
|
||||
AstNode* const newLastp = newp->m_headtailp;
|
||||
//
|
||||
this->editCountInc();
|
||||
// Common linkage
|
||||
newLastp->m_nextp = this;
|
||||
this->m_backp = newLastp;
|
||||
newp->m_backp = backp;
|
||||
// newLastp will not be the last node in the list as 'this' will follow it.
|
||||
// If newLastp == newp, then below handles newp becoming head
|
||||
newLastp->m_headtailp = nullptr;
|
||||
// Linkage dependent on position
|
||||
if (backp && backp->m_nextp == this) {
|
||||
// If 'this' is not at the head of a list, then the new node will also not be at the head
|
||||
// of a list, so we can just link in the new node in the middle.
|
||||
backp->m_nextp = newp;
|
||||
newp->m_headtailp = nullptr;
|
||||
} else {
|
||||
// If 'this' is at the head of a list, then the new node becomes the head of that list.
|
||||
if (backp) {
|
||||
if (backp->m_op1p == this) {
|
||||
backp->m_op1p = newp;
|
||||
} else if (backp->m_op2p == this) {
|
||||
backp->m_op2p = newp;
|
||||
} else if (backp->m_op3p == this) {
|
||||
backp->m_op3p = newp;
|
||||
} else {
|
||||
UASSERT_OBJ(backp->m_op4p == this, this, "Don't know where newp should go");
|
||||
backp->m_op4p = newp;
|
||||
}
|
||||
}
|
||||
// We also need to update m_headtailp.
|
||||
AstNode* const tailp = this->m_headtailp;
|
||||
this->m_headtailp = nullptr;
|
||||
newp->m_headtailp = tailp;
|
||||
tailp->m_headtailp = newp;
|
||||
}
|
||||
// Iterator fixup
|
||||
if (newLastp->m_iterpp) *(newLastp->m_iterpp) = this;
|
||||
if (this->m_iterpp) {
|
||||
*(this->m_iterpp) = newp;
|
||||
this->m_iterpp = nullptr;
|
||||
}
|
||||
//
|
||||
debugTreeChange(this, "-addHereThisAsNext: ", __LINE__, true);
|
||||
}
|
||||
|
||||
void AstNode::swapWith(AstNode* bp) {
|
||||
@@ -879,7 +922,7 @@ AstNode* AstNode::iterateSubtreeReturnEdits(VNVisitor& v) {
|
||||
} else if (!nodep->backp()) {
|
||||
// Calling on standalone tree; insert a shim node so we can keep
|
||||
// track, then delete it on completion
|
||||
AstBegin* const 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
|
||||
@@ -958,43 +1001,12 @@ bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ig
|
||||
//======================================================================
|
||||
// Debugging
|
||||
|
||||
void AstNode::checkTreeIter(AstNode* backp) {
|
||||
void AstNode::checkTreeIter(const AstNode* backp) const {
|
||||
// private: Check a tree and children
|
||||
UASSERT_OBJ(backp == this->backp(), this, "Back node inconsistent");
|
||||
if (VN_IS(this, NodeTermop) || VN_IS(this, NodeVarRef)) {
|
||||
// Termops have a short-circuited iterateChildren, so check usage
|
||||
UASSERT_OBJ(!(op1p() || op2p() || op3p() || op4p()), this,
|
||||
"Terminal operation with non-terminals");
|
||||
}
|
||||
if (m_op1p) m_op1p->checkTreeIterList(this);
|
||||
if (m_op2p) m_op2p->checkTreeIterList(this);
|
||||
if (m_op3p) m_op3p->checkTreeIterList(this);
|
||||
if (m_op4p) m_op4p->checkTreeIterList(this);
|
||||
}
|
||||
|
||||
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* const headp = this;
|
||||
const AstNode* tailp = this;
|
||||
for (AstNode* nodep = headp; nodep; nodep = nodep->nextp()) {
|
||||
nodep->checkTreeIter(backp);
|
||||
UASSERT_OBJ(headp == this || !nextp(), this,
|
||||
"Headtailp should be null in middle of lists");
|
||||
tailp = nodep;
|
||||
backp = nodep;
|
||||
}
|
||||
UASSERT_OBJ(headp->m_headtailp == tailp, headp, "Tail in headtailp is inconsistent");
|
||||
UASSERT_OBJ(tailp->m_headtailp == headp, tailp, "Head in headtailp is inconsistent");
|
||||
}
|
||||
|
||||
void AstNode::checkTree() {
|
||||
if (!debug()) return;
|
||||
if (this->backp()) {
|
||||
// Linked tree- check only the passed node
|
||||
this->checkTreeIter(this->backp());
|
||||
} else {
|
||||
this->checkTreeIterList(this->backp());
|
||||
switch (this->type()) {
|
||||
#include "V3Ast__gen_op_checks.h"
|
||||
default: VL_UNREACHABLE; // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1110,7 +1122,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo
|
||||
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";
|
||||
if (editCountGbl() == editCountLast() && !(v3Global.opt.dumpTree() >= 9)) {
|
||||
if (editCountGbl() == editCountLast() && ::dumpTree() < 9) {
|
||||
*logsp << '\n';
|
||||
*logsp << "No changes since last dump!\n";
|
||||
} else {
|
||||
@@ -1120,7 +1132,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo
|
||||
}
|
||||
}
|
||||
if (doDump && v3Global.opt.debugEmitV()) V3EmitV::debugEmitV(filename + ".v");
|
||||
if (doCheck && (v3Global.opt.debugCheck() || v3Global.opt.dumpTree())) {
|
||||
if (doCheck && (v3Global.opt.debugCheck() || ::dumpTree())) {
|
||||
// Error check
|
||||
checkTree();
|
||||
// Broken isn't part of check tree because it can munge iterp's
|
||||
@@ -1171,7 +1183,11 @@ void AstNode::v3errorEnd(std::ostringstream& str) const {
|
||||
const_cast<AstNode*>(this)->dump(nsstr);
|
||||
nsstr << endl;
|
||||
}
|
||||
m_fileline->v3errorEnd(nsstr, instanceStr());
|
||||
// Don't look for instance name when warning is disabled.
|
||||
// In case of large number of warnings, this can
|
||||
// take significant amount of time
|
||||
m_fileline->v3errorEnd(nsstr,
|
||||
m_fileline->warnIsOff(V3Error::errorCode()) ? "" : instanceStr());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1248,3 +1264,8 @@ void VNDeleter::doDeletes() {
|
||||
for (AstNode* const nodep : m_deleteps) nodep->deleteTree();
|
||||
m_deleteps.clear();
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// VNVisitor
|
||||
|
||||
#include "V3Ast__gen_visitor_defns.h" // From ./astgen
|
||||
|
||||
+627
-1471
File diff suppressed because it is too large
Load Diff
+141
-37
@@ -17,71 +17,175 @@
|
||||
#ifndef VERILATOR_V3ASTINLINES_H_
|
||||
#define VERILATOR_V3ASTINLINES_H_
|
||||
|
||||
#ifndef VERILATOR_V3ASTNODES_H_
|
||||
#ifndef VERILATOR_V3AST_H_
|
||||
#error "Use V3Ast.h as the include"
|
||||
#include "V3AstNodes.h" // This helps code analysis tools pick up symbols in V3Ast.h and V3AstNodes.h
|
||||
#include "V3Ast.h" // This helps code analysis tools pick up symbols in V3Ast.h and relaed
|
||||
#endif
|
||||
|
||||
//######################################################################
|
||||
// Inline ACCESSORS
|
||||
// Inline METHODS
|
||||
|
||||
inline int AstNode::width() const { return dtypep() ? dtypep()->width() : 0; }
|
||||
inline int AstNode::widthMin() const { return dtypep() ? dtypep()->widthMin() : 0; }
|
||||
inline bool AstNode::width1() const { // V3Const uses to know it can optimize
|
||||
int AstNode::width() const { return dtypep() ? dtypep()->width() : 0; }
|
||||
int AstNode::widthMin() const { return dtypep() ? dtypep()->widthMin() : 0; }
|
||||
bool AstNode::width1() const { // V3Const uses to know it can optimize
|
||||
return dtypep() && dtypep()->width() == 1;
|
||||
}
|
||||
inline int AstNode::widthInstrs() const {
|
||||
int AstNode::widthInstrs() const {
|
||||
return (!dtypep() ? 1 : (dtypep()->isWide() ? dtypep()->widthWords() : 1));
|
||||
}
|
||||
inline bool AstNode::isDouble() const {
|
||||
bool AstNode::isDouble() const {
|
||||
return dtypep() && VN_IS(dtypep(), BasicDType) && VN_AS(dtypep(), BasicDType)->isDouble();
|
||||
}
|
||||
inline bool AstNode::isString() const {
|
||||
bool AstNode::isString() const {
|
||||
return dtypep() && dtypep()->basicp() && dtypep()->basicp()->isString();
|
||||
}
|
||||
inline bool AstNode::isSigned() const { return dtypep() && dtypep()->isSigned(); }
|
||||
bool AstNode::isSigned() const { return dtypep() && dtypep()->isSigned(); }
|
||||
|
||||
inline bool AstNode::isZero() const {
|
||||
bool AstNode::isZero() const {
|
||||
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqZero());
|
||||
}
|
||||
inline bool AstNode::isNeqZero() const {
|
||||
bool AstNode::isNeqZero() const {
|
||||
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isNeqZero());
|
||||
}
|
||||
inline bool AstNode::isOne() const {
|
||||
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqOne());
|
||||
}
|
||||
inline bool AstNode::isAllOnes() const {
|
||||
bool AstNode::isOne() const { return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqOne()); }
|
||||
bool AstNode::isAllOnes() const {
|
||||
return (VN_IS(this, Const) && VN_AS(this, Const)->isEqAllOnes());
|
||||
}
|
||||
inline bool AstNode::isAllOnesV() const {
|
||||
bool AstNode::isAllOnesV() const {
|
||||
return (VN_IS(this, Const) && VN_AS(this, Const)->isEqAllOnesV());
|
||||
}
|
||||
inline bool AstNode::sameTree(const AstNode* node2p) const {
|
||||
bool AstNode::sameTree(const AstNode* node2p) const {
|
||||
return sameTreeIter(this, node2p, true, false);
|
||||
}
|
||||
inline bool AstNode::sameGateTree(const AstNode* node2p) const {
|
||||
bool AstNode::sameGateTree(const AstNode* node2p) const {
|
||||
return sameTreeIter(this, node2p, true, true);
|
||||
}
|
||||
|
||||
inline void AstNodeVarRef::varp(AstVar* varp) {
|
||||
m_varp = varp;
|
||||
int AstNodeArrayDType::left() const { return rangep()->leftConst(); }
|
||||
int AstNodeArrayDType::right() const { return rangep()->rightConst(); }
|
||||
int AstNodeArrayDType::hi() const { return rangep()->hiConst(); }
|
||||
int AstNodeArrayDType::lo() const { return rangep()->loConst(); }
|
||||
int AstNodeArrayDType::elementsConst() const { return rangep()->elementsConst(); }
|
||||
VNumRange AstNodeArrayDType::declRange() const { return VNumRange{left(), right()}; }
|
||||
|
||||
AstRange::AstRange(FileLine* fl, int left, int right)
|
||||
: ASTGEN_SUPER_Range(fl) {
|
||||
leftp(new AstConst{fl, static_cast<uint32_t>(left)});
|
||||
rightp(new AstConst{fl, static_cast<uint32_t>(right)});
|
||||
}
|
||||
AstRange::AstRange(FileLine* fl, const VNumRange& range)
|
||||
: ASTGEN_SUPER_Range(fl) {
|
||||
leftp(new AstConst{fl, static_cast<uint32_t>(range.left())});
|
||||
rightp(new AstConst{fl, static_cast<uint32_t>(range.right())});
|
||||
}
|
||||
int AstRange::leftConst() const {
|
||||
AstConst* const constp = VN_CAST(leftp(), Const);
|
||||
return (constp ? constp->toSInt() : 0);
|
||||
}
|
||||
int AstRange::rightConst() const {
|
||||
AstConst* const constp = VN_CAST(rightp(), Const);
|
||||
return (constp ? constp->toSInt() : 0);
|
||||
}
|
||||
|
||||
int AstQueueDType::boundConst() const {
|
||||
AstConst* const constp = VN_CAST(boundp(), Const);
|
||||
return (constp ? constp->toSInt() : 0);
|
||||
}
|
||||
|
||||
AstPin::AstPin(FileLine* fl, int pinNum, AstVarRef* varname, AstNode* exprp)
|
||||
: ASTGEN_SUPER_Pin(fl)
|
||||
, m_pinNum{pinNum}
|
||||
, m_name{varname->name()} {
|
||||
this->exprp(exprp);
|
||||
}
|
||||
|
||||
AstDpiExportUpdated::AstDpiExportUpdated(FileLine* fl, AstVarScope* varScopep)
|
||||
: ASTGEN_SUPER_DpiExportUpdated(fl) {
|
||||
this->varRefp(new AstVarRef{fl, varScopep, VAccess::WRITE});
|
||||
}
|
||||
|
||||
AstVarScope* AstDpiExportUpdated::varScopep() const { return varRefp()->varScopep(); }
|
||||
|
||||
AstPackArrayDType::AstPackArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp,
|
||||
AstRange* rangep)
|
||||
: ASTGEN_SUPER_PackArrayDType(fl) {
|
||||
childDTypep(dtp); // Only for parser
|
||||
refDTypep(nullptr);
|
||||
this->rangep(rangep);
|
||||
dtypep(nullptr); // V3Width will resolve
|
||||
const int width = subDTypep()->width() * rangep->elementsConst();
|
||||
widthForce(width, width);
|
||||
}
|
||||
AstPackArrayDType::AstPackArrayDType(FileLine* fl, AstNodeDType* dtp, AstRange* rangep)
|
||||
: ASTGEN_SUPER_PackArrayDType(fl) {
|
||||
refDTypep(dtp);
|
||||
this->rangep(rangep);
|
||||
dtypep(this);
|
||||
const int width = subDTypep()->width() * rangep->elementsConst();
|
||||
widthForce(width, width);
|
||||
}
|
||||
|
||||
int AstBasicDType::hi() const { return (rangep() ? rangep()->hiConst() : m.m_nrange.hi()); }
|
||||
int AstBasicDType::lo() const { return (rangep() ? rangep()->loConst() : m.m_nrange.lo()); }
|
||||
int AstBasicDType::elements() const {
|
||||
return (rangep() ? rangep()->elementsConst() : m.m_nrange.elements());
|
||||
}
|
||||
bool AstBasicDType::littleEndian() const {
|
||||
return (rangep() ? rangep()->littleEndian() : m.m_nrange.littleEndian());
|
||||
}
|
||||
|
||||
bool AstActive::hasInitial() const { return m_sensesp->hasInitial(); }
|
||||
bool AstActive::hasSettle() const { return m_sensesp->hasSettle(); }
|
||||
bool AstActive::hasClocked() const { return m_sensesp->hasClocked(); }
|
||||
|
||||
AstElabDisplay::AstElabDisplay(FileLine* fl, VDisplayType dispType, AstNode* exprsp)
|
||||
: ASTGEN_SUPER_ElabDisplay(fl) {
|
||||
addFmtp(new AstSFormatF{fl, AstSFormatF::NoFormat(), exprsp});
|
||||
m_displayType = dispType;
|
||||
}
|
||||
|
||||
AstCStmt::AstCStmt(FileLine* fl, const string& textStmt)
|
||||
: ASTGEN_SUPER_CStmt(fl) {
|
||||
addExprsp(new AstText{fl, textStmt, true});
|
||||
}
|
||||
|
||||
AstCMath::AstCMath(FileLine* fl, const string& textStmt, int setwidth, bool cleanOut)
|
||||
: ASTGEN_SUPER_CMath(fl)
|
||||
, m_cleanOut{cleanOut}
|
||||
, m_pure{true} {
|
||||
addExprsp(new AstText{fl, textStmt, true});
|
||||
if (setwidth) dtypeSetLogicSized(setwidth, VSigning::UNSIGNED);
|
||||
}
|
||||
|
||||
AstVarRef::AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarRef(fl, varp->name(), varp, access) {}
|
||||
// This form only allowed post-link (see above)
|
||||
AstVarRef::AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarRef(fl, varscp->varp()->name(), varscp->varp(), access) {
|
||||
varScopep(varscp);
|
||||
}
|
||||
bool AstVarRef::same(const AstVarRef* samep) const {
|
||||
if (varScopep()) {
|
||||
return (varScopep() == samep->varScopep() && access() == samep->access());
|
||||
} else {
|
||||
return (selfPointer() == samep->selfPointer() && varp()->name() == samep->varp()->name()
|
||||
&& access() == samep->access());
|
||||
}
|
||||
}
|
||||
bool AstVarRef::sameNoLvalue(AstVarRef* samep) const {
|
||||
if (varScopep()) {
|
||||
return (varScopep() == samep->varScopep());
|
||||
} else {
|
||||
return (selfPointer() == samep->selfPointer()
|
||||
&& (!selfPointer().empty() || !samep->selfPointer().empty())
|
||||
&& varp()->name() == samep->varp()->name());
|
||||
}
|
||||
}
|
||||
|
||||
AstVarXRef::AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarXRef(fl, varp->name(), varp, access)
|
||||
, m_dotted{dotted} {
|
||||
dtypeFrom(varp);
|
||||
}
|
||||
|
||||
inline bool AstNodeDType::isFourstate() const { return basicp()->isFourstate(); }
|
||||
|
||||
inline void AstNodeArrayDType::rangep(AstRange* nodep) { setOp2p(nodep); }
|
||||
inline int AstNodeArrayDType::left() const { return rangep()->leftConst(); }
|
||||
inline int AstNodeArrayDType::right() const { return rangep()->rightConst(); }
|
||||
inline int AstNodeArrayDType::hi() const { return rangep()->hiConst(); }
|
||||
inline int AstNodeArrayDType::lo() const { return rangep()->loConst(); }
|
||||
inline int AstNodeArrayDType::elementsConst() const { return rangep()->elementsConst(); }
|
||||
inline VNumRange AstNodeArrayDType::declRange() const { return VNumRange{left(), right()}; }
|
||||
|
||||
inline void AstIfaceRefDType::cloneRelink() {
|
||||
if (m_cellp && m_cellp->clonep()) m_cellp = m_cellp->clonep();
|
||||
if (m_ifacep && m_ifacep->clonep()) m_ifacep = m_ifacep->clonep();
|
||||
if (m_modportp && m_modportp->clonep()) m_modportp = m_modportp->clonep();
|
||||
}
|
||||
|
||||
#endif // Guard
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+4287
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+324
-49
@@ -26,7 +26,7 @@
|
||||
#include "V3PartitionGraph.h" // Just for mtask dumping
|
||||
#include "V3String.h"
|
||||
|
||||
#include "V3AstNodes__gen_macros.h" // Generated by 'astgen'
|
||||
#include "V3Ast__gen_macros.h" // Generated by 'astgen'
|
||||
|
||||
#include <iomanip>
|
||||
#include <iterator>
|
||||
@@ -155,6 +155,51 @@ void AstNodeCond::numberOperate(V3Number& out, const V3Number& lhs, const V3Numb
|
||||
}
|
||||
}
|
||||
|
||||
void AstBasicDType::init(VBasicDTypeKwd kwd, VSigning numer, int wantwidth, int wantwidthmin,
|
||||
AstRange* rangep) {
|
||||
// wantwidth=0 means figure it out, but if a widthmin is >=0
|
||||
// we allow width 0 so that {{0{x}},y} works properly
|
||||
// wantwidthmin=-1: default, use wantwidth if it is non zero
|
||||
m.m_keyword = kwd;
|
||||
// Implicitness: // "parameter X" is implicit and sized from initial
|
||||
// value, "parameter reg x" not
|
||||
if (keyword() == VBasicDTypeKwd::LOGIC_IMPLICIT) {
|
||||
if (rangep || wantwidth) m.m_keyword = VBasicDTypeKwd::LOGIC;
|
||||
}
|
||||
if (numer == VSigning::NOSIGN) {
|
||||
if (keyword().isSigned()) {
|
||||
numer = VSigning::SIGNED;
|
||||
} else if (keyword().isUnsigned()) {
|
||||
numer = VSigning::UNSIGNED;
|
||||
}
|
||||
}
|
||||
numeric(numer);
|
||||
if (!rangep && (wantwidth || wantwidthmin >= 0)) { // Constant width
|
||||
if (wantwidth > 1) m.m_nrange.init(wantwidth - 1, 0, false);
|
||||
const int wmin = wantwidthmin >= 0 ? wantwidthmin : wantwidth;
|
||||
widthForce(wantwidth, wmin);
|
||||
} else if (!rangep) { // Set based on keyword properties
|
||||
// V3Width will pull from this width
|
||||
if (keyword().width() > 1 && !isOpaque()) {
|
||||
m.m_nrange.init(keyword().width() - 1, 0, false);
|
||||
}
|
||||
widthForce(keyword().width(), keyword().width());
|
||||
} else {
|
||||
widthForce(rangep->elementsConst(),
|
||||
rangep->elementsConst()); // Maybe unknown if parameters underneath it
|
||||
}
|
||||
this->rangep(rangep);
|
||||
this->dtypep(this);
|
||||
}
|
||||
|
||||
void AstBasicDType::cvtRangeConst() {
|
||||
if (rangep() && VN_IS(rangep()->leftp(), Const) && VN_IS(rangep()->rightp(), Const)) {
|
||||
m.m_nrange = VNumRange{rangep()->leftConst(), rangep()->rightConst()};
|
||||
rangep()->unlinkFrBackWithNext()->deleteTree();
|
||||
rangep(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
int AstBasicDType::widthAlignBytes() const {
|
||||
if (width() <= 8) {
|
||||
return 1;
|
||||
@@ -481,7 +526,7 @@ string AstVar::cPubArgType(bool named, bool forReturn) const {
|
||||
}
|
||||
} else {
|
||||
// Newer internal-compatible types
|
||||
arg += dtypep()->cType((named ? name() : string{}), true, isRef);
|
||||
arg += dtypep()->cType((named ? name() : std::string{}), true, isRef);
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
@@ -489,7 +534,7 @@ string AstVar::cPubArgType(bool named, bool forReturn) const {
|
||||
class dpiTypesToStringConverter VL_NOT_FINAL {
|
||||
public:
|
||||
virtual string openArray(const AstVar*) const { return "const svOpenArrayHandle"; }
|
||||
virtual string bitLogicVector(const AstVar* varp, bool isBit) const {
|
||||
virtual string bitLogicVector(const AstVar* /*varp*/, bool isBit) const {
|
||||
return isBit ? "svBitVecVal" : "svLogicVecVal";
|
||||
}
|
||||
virtual string primitive(const AstVar* varp) const {
|
||||
@@ -519,11 +564,11 @@ string AstVar::dpiArgType(bool named, bool forReturn) const {
|
||||
return dpiTypesToStringConverter{}.convert(this);
|
||||
} else {
|
||||
class converter final : public dpiTypesToStringConverter {
|
||||
virtual string bitLogicVector(const AstVar* varp, bool isBit) const override {
|
||||
string bitLogicVector(const AstVar* varp, bool isBit) const override {
|
||||
return string(varp->isReadOnly() ? "const " : "")
|
||||
+ dpiTypesToStringConverter::bitLogicVector(varp, isBit) + '*';
|
||||
}
|
||||
virtual string primitive(const AstVar* varp) const override {
|
||||
string primitive(const AstVar* varp) const override {
|
||||
string type = dpiTypesToStringConverter::primitive(varp);
|
||||
if (varp->isWritable() || VN_IS(varp->dtypep()->skipRefp(), UnpackArrayDType)) {
|
||||
if (!varp->isWritable() && varp->basicp()->keyword() != VBasicDTypeKwd::STRING)
|
||||
@@ -555,16 +600,16 @@ string AstVar::dpiTmpVarType(const string& varName) const {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
virtual string openArray(const AstVar* varp) const override {
|
||||
string openArray(const AstVar* varp) const override {
|
||||
return dpiTypesToStringConverter::openArray(varp) + ' ' + m_name
|
||||
+ arraySuffix(varp, 0);
|
||||
}
|
||||
virtual string bitLogicVector(const AstVar* varp, bool isBit) const override {
|
||||
string bitLogicVector(const AstVar* varp, bool isBit) const override {
|
||||
string type = dpiTypesToStringConverter::bitLogicVector(varp, isBit);
|
||||
type += ' ' + m_name + arraySuffix(varp, varp->widthWords());
|
||||
return type;
|
||||
}
|
||||
virtual string primitive(const AstVar* varp) const override {
|
||||
string primitive(const AstVar* varp) const override {
|
||||
string type = dpiTypesToStringConverter::primitive(varp);
|
||||
if (varp->isWritable() || VN_IS(varp->dtypep()->skipRefp(), UnpackArrayDType)) {
|
||||
if (!varp->isWritable() && varp->basicp()->keyword() == VBasicDTypeKwd::CHANDLE)
|
||||
@@ -615,29 +660,21 @@ AstVar* AstVar::scVarRecurse(AstNode* nodep) {
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
} else if (VN_IS(nodep, VarRef)) {
|
||||
if (VN_AS(nodep, VarRef)->varp()->isSc()) {
|
||||
return VN_AS(nodep, VarRef)->varp();
|
||||
} else if (AstVarRef* const vrefp = VN_CAST(nodep, VarRef)) {
|
||||
if (vrefp->varp()->isSc()) {
|
||||
return vrefp->varp();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
} else if (VN_IS(nodep, ArraySel)) {
|
||||
if (nodep->op1p()) {
|
||||
if (AstVar* p = scVarRecurse(nodep->op1p())) return p;
|
||||
}
|
||||
if (nodep->op2p()) {
|
||||
if (AstVar* p = scVarRecurse(nodep->op2p())) return p;
|
||||
}
|
||||
if (nodep->op3p()) {
|
||||
if (AstVar* p = scVarRecurse(nodep->op3p())) return p;
|
||||
}
|
||||
if (nodep->op4p()) {
|
||||
if (AstVar* p = scVarRecurse(nodep->op4p())) return p;
|
||||
}
|
||||
} else if (AstArraySel* const arraySelp = VN_CAST(nodep, ArraySel)) {
|
||||
if (AstVar* const p = scVarRecurse(arraySelp->fromp())) return p;
|
||||
if (AstVar* const p = scVarRecurse(arraySelp->bitp())) return p;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool AstNodeDType::isFourstate() const { return basicp()->isFourstate(); }
|
||||
|
||||
class AstNodeDType::CTypeRecursed final {
|
||||
public:
|
||||
string m_type; // The base type, e.g.: "Foo_t"s
|
||||
@@ -659,7 +696,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
string AstNodeDType::cType(const string& name, bool forFunc, bool isRef) const {
|
||||
string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef) const {
|
||||
const CTypeRecursed info = cTypeRecurse(false);
|
||||
return info.render(name, isRef);
|
||||
}
|
||||
@@ -673,6 +710,9 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
|
||||
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* const adtypep = VN_CAST(dtypep, WildcardArrayDType)) {
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
info.m_type = "VlAssocArray<std::string, " + sub.m_type + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, DynArrayDType)) {
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
info.m_type = "VlQueue<" + sub.m_type + ">";
|
||||
@@ -1020,8 +1060,13 @@ AstConstPool::AstConstPool(FileLine* fl)
|
||||
: ASTGEN_SUPER_ConstPool(fl)
|
||||
, m_modp{new AstModule(fl, "@CONST-POOL@")}
|
||||
, m_scopep{new AstScope(fl, m_modp, "@CONST-POOL@", nullptr, nullptr)} {
|
||||
addOp1p(m_modp);
|
||||
m_modp->addStmtp(m_scopep);
|
||||
this->modulep(m_modp);
|
||||
m_modp->addStmtsp(m_scopep);
|
||||
}
|
||||
const char* AstConstPool::broken() const {
|
||||
BROKEN_RTN(m_modp && !m_modp->brokeExists());
|
||||
BROKEN_RTN(m_scopep && !m_scopep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AstVarScope* AstConstPool::createNewEntry(const string& name, AstNode* initp) {
|
||||
@@ -1030,9 +1075,9 @@ AstVarScope* AstConstPool::createNewEntry(const string& name, AstNode* initp) {
|
||||
varp->isConst(true);
|
||||
varp->isStatic(true);
|
||||
varp->valuep(initp->cloneTree(false));
|
||||
m_modp->addStmtp(varp);
|
||||
m_modp->addStmtsp(varp);
|
||||
AstVarScope* const varScopep = new AstVarScope(fl, m_scopep, varp);
|
||||
m_scopep->addVarp(varScopep);
|
||||
m_scopep->addVarsp(varScopep);
|
||||
return varScopep;
|
||||
}
|
||||
|
||||
@@ -1178,7 +1223,7 @@ void AstWhile::addBeforeStmt(AstNode* newp, AstNode* belowp) {
|
||||
} else if (belowp == condp()) {
|
||||
// Goes before condition, IE in preconditions
|
||||
addPrecondsp(newp);
|
||||
} else if (belowp == bodysp()) {
|
||||
} else if (belowp == stmtsp()) {
|
||||
// Was first statement in body, so new front
|
||||
belowp->addHereThisAsNext(newp);
|
||||
} else {
|
||||
@@ -1195,12 +1240,12 @@ void AstWhile::addNextStmt(AstNode* newp, AstNode* belowp) {
|
||||
belowp->addNextHere(newp);
|
||||
} else if (belowp == condp()) {
|
||||
// Becomes first statement in body, body may have been empty
|
||||
if (bodysp()) {
|
||||
bodysp()->addHereThisAsNext(newp);
|
||||
if (stmtsp()) {
|
||||
stmtsp()->addHereThisAsNext(newp);
|
||||
} else {
|
||||
addBodysp(newp);
|
||||
addStmtsp(newp);
|
||||
}
|
||||
} else if (belowp == bodysp()) {
|
||||
} else if (belowp == stmtsp()) {
|
||||
// Next statement in body
|
||||
belowp->addNextHere(newp);
|
||||
} else {
|
||||
@@ -1221,10 +1266,10 @@ static std::string nodeAddr(const AstNode* nodep) {
|
||||
}
|
||||
|
||||
void AstNode::dump(std::ostream& str) const {
|
||||
str << typeName() << " "
|
||||
<< nodeAddr(this)
|
||||
//<< " " << nodeAddr(m_backp)
|
||||
str << typeName() << " " << nodeAddr(this)
|
||||
#ifdef VL_DEBUG
|
||||
<< " <e" << std::dec << editCount() << ((editCount() >= editCountLast()) ? "#>" : ">")
|
||||
#endif
|
||||
<< " {" << fileline()->filenameLetters() << std::dec << fileline()->lastLineno()
|
||||
<< fileline()->firstColumnLetters() << "}";
|
||||
if (user1p()) str << " u1=" << nodeAddr(user1p());
|
||||
@@ -1294,11 +1339,19 @@ void AstCell::dump(std::ostream& str) const {
|
||||
str << " ->UNLINKED:" << modName();
|
||||
}
|
||||
}
|
||||
const char* AstCell::broken() const {
|
||||
BROKEN_RTN(m_modp && !m_modp->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
void AstCellInline::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " -> " << origModName();
|
||||
str << " [scopep=" << reinterpret_cast<const void*>(scopep()) << "]";
|
||||
}
|
||||
const char* AstCellInline::broken() const {
|
||||
BROKEN_RTN(m_scopep && !m_scopep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
const char* AstClassPackage::broken() const {
|
||||
BROKEN_BASE_RTN(AstNodeModule::broken());
|
||||
BROKEN_RTN(m_classp && !m_classp->brokeExists());
|
||||
@@ -1323,8 +1376,9 @@ void AstClass::repairCache() {
|
||||
clearCache();
|
||||
for (auto* itemp = membersp(); itemp; itemp = itemp->nextp()) {
|
||||
if (const auto* const scopep = VN_CAST(itemp, Scope)) {
|
||||
for (auto* itemp = scopep->blocksp(); itemp; itemp = itemp->nextp())
|
||||
insertCache(itemp);
|
||||
for (auto* blockp = scopep->blocksp(); blockp; blockp = blockp->nextp()) {
|
||||
insertCache(blockp);
|
||||
}
|
||||
} else {
|
||||
insertCache(itemp);
|
||||
}
|
||||
@@ -1342,6 +1396,17 @@ void AstClass::dump(std::ostream& str) const {
|
||||
if (isExtended()) str << " [EXT]";
|
||||
if (isVirtual()) str << " [VIRT]";
|
||||
}
|
||||
const char* AstClass::broken() const {
|
||||
BROKEN_BASE_RTN(AstNodeModule::broken());
|
||||
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
void AstClass::cloneRelink() {
|
||||
AstNodeModule::cloneRelink();
|
||||
if (m_classOrPackagep && m_classOrPackagep->clonep()) {
|
||||
m_classOrPackagep = m_classOrPackagep->clonep();
|
||||
}
|
||||
}
|
||||
AstClass* AstClassExtends::classp() const {
|
||||
const AstClassRefDType* refp = VN_CAST(dtypep(), ClassRefDType);
|
||||
if (VL_UNLIKELY(!refp)) { // LinkDot uses this for 'super.'
|
||||
@@ -1351,7 +1416,7 @@ AstClass* AstClassExtends::classp() const {
|
||||
return refp->classp();
|
||||
}
|
||||
void AstClassRefDType::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
this->AstNodeDType::dump(str);
|
||||
if (classOrPackagep()) str << " cpkg=" << nodeAddr(classOrPackagep());
|
||||
if (classp()) {
|
||||
str << " -> ";
|
||||
@@ -1364,6 +1429,18 @@ void AstClassRefDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "class:" << name();
|
||||
}
|
||||
const char* AstClassRefDType::broken() const {
|
||||
BROKEN_RTN(m_classp && !m_classp->brokeExists());
|
||||
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
void AstClassRefDType::cloneRelink() {
|
||||
if (m_classp && m_classp->clonep()) m_classp = m_classp->clonep();
|
||||
if (m_classOrPackagep && m_classOrPackagep->clonep()) {
|
||||
m_classOrPackagep = m_classOrPackagep->clonep();
|
||||
}
|
||||
}
|
||||
string AstClassRefDType::name() const { return classp() ? classp()->name() : "<unlinked>"; }
|
||||
void AstNodeCoverOrAssert::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
if (immediate()) str << " [IMMEDIATE]";
|
||||
@@ -1381,8 +1458,13 @@ void AstEnumItemRef::dump(std::ostream& str) const {
|
||||
str << "UNLINKED";
|
||||
}
|
||||
}
|
||||
const char* AstEnumItemRef::broken() const {
|
||||
BROKEN_RTN(m_itemp && !m_itemp->brokeExists());
|
||||
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
void AstIfaceRefDType::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
this->AstNodeDType::dump(str);
|
||||
if (cellName() != "") str << " cell=" << cellName();
|
||||
if (ifaceName() != "") str << " if=" << ifaceName();
|
||||
if (modportName() != "") str << " mp=" << modportName();
|
||||
@@ -1400,6 +1482,11 @@ void AstIfaceRefDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "iface";
|
||||
}
|
||||
void AstIfaceRefDType::cloneRelink() {
|
||||
if (m_cellp && m_cellp->clonep()) m_cellp = m_cellp->clonep();
|
||||
if (m_ifacep && m_ifacep->clonep()) m_ifacep = m_ifacep->clonep();
|
||||
if (m_modportp && m_modportp->clonep()) m_modportp = m_modportp->clonep();
|
||||
}
|
||||
void AstInitArray::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
int n = 0;
|
||||
@@ -1412,6 +1499,42 @@ void AstInitArray::dump(std::ostream& str) const {
|
||||
str << " [" << itr.first << "]=" << reinterpret_cast<const void*>(itr.second);
|
||||
}
|
||||
}
|
||||
const char* AstInitArray::broken() const {
|
||||
for (KeyItemMap::const_iterator it = m_map.begin(); it != m_map.end(); ++it) {
|
||||
BROKEN_RTN(!it->second);
|
||||
BROKEN_RTN(!it->second->brokeExists());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
void AstInitArray::cloneRelink() {
|
||||
for (KeyItemMap::iterator it = m_map.begin(); it != m_map.end(); ++it) {
|
||||
if (it->second->clonep()) it->second = it->second->clonep();
|
||||
}
|
||||
}
|
||||
void AstInitArray::addIndexValuep(uint64_t index, AstNode* newp) {
|
||||
const auto it = m_map.find(index);
|
||||
if (it != m_map.end()) {
|
||||
it->second->valuep(newp);
|
||||
} else {
|
||||
AstInitItem* const itemp = new AstInitItem(fileline(), newp);
|
||||
m_map.emplace(index, itemp);
|
||||
addInitsp(itemp);
|
||||
}
|
||||
}
|
||||
AstNode* AstInitArray::getIndexValuep(uint64_t index) const {
|
||||
const auto it = m_map.find(index);
|
||||
if (it == m_map.end()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return it->second->valuep();
|
||||
}
|
||||
}
|
||||
AstNode* AstInitArray::getIndexDefaultedValuep(uint64_t index) const {
|
||||
AstNode* valuep = getIndexValuep(index);
|
||||
if (!valuep) valuep = defaultp();
|
||||
return valuep;
|
||||
}
|
||||
|
||||
void AstJumpGo::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
str << " -> ";
|
||||
@@ -1421,6 +1544,13 @@ void AstJumpGo::dump(std::ostream& str) const {
|
||||
str << "%Error:UNLINKED";
|
||||
}
|
||||
}
|
||||
const char* AstJumpGo::broken() const {
|
||||
BROKEN_RTN(!labelp()->brokeExistsBelow());
|
||||
return nullptr;
|
||||
}
|
||||
void AstJumpGo::cloneRelink() {
|
||||
if (m_labelp->clonep()) m_labelp = m_labelp->clonep();
|
||||
}
|
||||
void AstJumpLabel::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
str << " -> ";
|
||||
@@ -1431,7 +1561,7 @@ void AstJumpLabel::dump(std::ostream& str) const {
|
||||
}
|
||||
}
|
||||
void AstLogOr::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
this->AstNodeMath::dump(str);
|
||||
if (sideEffect()) str << " [SIDE]";
|
||||
}
|
||||
void AstMemberSel::dump(std::ostream& str) const {
|
||||
@@ -1443,8 +1573,15 @@ void AstMemberSel::dump(std::ostream& str) const {
|
||||
str << "%Error:UNLINKED";
|
||||
}
|
||||
}
|
||||
void AstMemberSel::cloneRelink() {
|
||||
if (m_varp && m_varp->clonep()) m_varp = m_varp->clonep();
|
||||
}
|
||||
const char* AstMemberSel::broken() const {
|
||||
BROKEN_RTN(m_varp && !m_varp->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
void AstMethodCall::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
this->AstNodeFTaskRef::dump(str);
|
||||
if (isStatement()) str << " [STMT]";
|
||||
str << " -> ";
|
||||
if (taskp()) {
|
||||
@@ -1464,6 +1601,13 @@ void AstModportFTaskRef::dump(std::ostream& str) const {
|
||||
str << " -> UNLINKED";
|
||||
}
|
||||
}
|
||||
const char* AstModportFTaskRef::broken() const {
|
||||
BROKEN_RTN(m_ftaskp && !m_ftaskp->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
void AstModportFTaskRef::cloneRelink() {
|
||||
if (m_ftaskp && m_ftaskp->clonep()) m_ftaskp = m_ftaskp->clonep();
|
||||
}
|
||||
void AstModportVarRef::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (direction().isAny()) str << " " << direction();
|
||||
@@ -1474,6 +1618,13 @@ void AstModportVarRef::dump(std::ostream& str) const {
|
||||
str << " -> UNLINKED";
|
||||
}
|
||||
}
|
||||
const char* AstModportVarRef::broken() const {
|
||||
BROKEN_RTN(m_varp && !m_varp->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
void AstModportVarRef::cloneRelink() {
|
||||
if (m_varp && m_varp->clonep()) m_varp = m_varp->clonep();
|
||||
}
|
||||
void AstPin::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (modVarp()) {
|
||||
@@ -1484,6 +1635,17 @@ void AstPin::dump(std::ostream& str) const {
|
||||
}
|
||||
if (svImplicit()) str << " [.SV]";
|
||||
}
|
||||
const char* AstPin::broken() const {
|
||||
BROKEN_RTN(m_modVarp && !m_modVarp->brokeExists());
|
||||
BROKEN_RTN(m_modPTypep && !m_modPTypep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
string AstPin::prettyOperatorName() const {
|
||||
return modVarp()
|
||||
? ((modVarp()->direction().isAny() ? modVarp()->direction().prettyName() + " " : "")
|
||||
+ "port connection " + modVarp()->prettyNameQ())
|
||||
: "port connection";
|
||||
}
|
||||
void AstPrintTimeScale::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
str << " " << timeunit();
|
||||
@@ -1529,8 +1691,25 @@ void AstRefDType::dump(std::ostream& str) const {
|
||||
str << " -> UNLINKED";
|
||||
}
|
||||
}
|
||||
const char* AstRefDType::broken() const {
|
||||
BROKEN_RTN(m_typedefp && !m_typedefp->brokeExists());
|
||||
BROKEN_RTN(m_refDTypep && !m_refDTypep->brokeExists());
|
||||
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
void AstRefDType::cloneRelink() {
|
||||
if (m_typedefp && m_typedefp->clonep()) m_typedefp = m_typedefp->clonep();
|
||||
if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep();
|
||||
if (m_classOrPackagep && m_classOrPackagep->clonep()) {
|
||||
m_classOrPackagep = m_classOrPackagep->clonep();
|
||||
}
|
||||
}
|
||||
AstNodeDType* AstRefDType::subDTypep() const {
|
||||
if (typedefp()) return typedefp()->subDTypep();
|
||||
return refDTypep(); // Maybe nullptr
|
||||
}
|
||||
void AstNodeUOrStructDType::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
this->AstNodeDType::dump(str);
|
||||
if (packed()) str << " [PACKED]";
|
||||
if (isFourstate()) str << " [4STATE]";
|
||||
}
|
||||
@@ -1599,6 +1778,32 @@ void AstNetlist::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " [" << timeunit() << "/" << timeprecision() << "]";
|
||||
}
|
||||
const char* AstNetlist::broken() const {
|
||||
BROKEN_RTN(m_typeTablep && !m_typeTablep->brokeExists());
|
||||
BROKEN_RTN(m_constPoolp && !m_constPoolp->brokeExists());
|
||||
BROKEN_RTN(m_dollarUnitPkgp && !m_dollarUnitPkgp->brokeExists());
|
||||
BROKEN_RTN(m_evalp && !m_evalp->brokeExists());
|
||||
BROKEN_RTN(m_dpiExportTriggerp && !m_dpiExportTriggerp->brokeExists());
|
||||
BROKEN_RTN(m_topScopep && !m_topScopep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
AstPackage* AstNetlist::dollarUnitPkgAddp() {
|
||||
if (!m_dollarUnitPkgp) {
|
||||
m_dollarUnitPkgp = new AstPackage(fileline(), AstPackage::dollarUnitName());
|
||||
// packages are always libraries; don't want to make them a "top"
|
||||
m_dollarUnitPkgp->inLibrary(true);
|
||||
m_dollarUnitPkgp->modTrace(false); // may reconsider later
|
||||
m_dollarUnitPkgp->internal(true);
|
||||
addModulesp(m_dollarUnitPkgp);
|
||||
}
|
||||
return m_dollarUnitPkgp;
|
||||
}
|
||||
void AstNetlist::createTopScope(AstScope* scopep) {
|
||||
UASSERT(scopep, "Must not be nullptr");
|
||||
UASSERT_OBJ(!m_topScopep, scopep, "TopScope already exits");
|
||||
m_topScopep = new AstTopScope{scopep->modp()->fileline(), scopep};
|
||||
scopep->modp()->addStmtsp(v3Global.rootp()->topScopep());
|
||||
}
|
||||
void AstNodeModule::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " L" << level();
|
||||
@@ -1616,12 +1821,26 @@ void AstPackageExport::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " -> " << packagep();
|
||||
}
|
||||
const char* AstPackageExport ::broken() const {
|
||||
BROKEN_RTN(!m_packagep || !m_packagep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
void AstPackageExport::cloneRelink() {
|
||||
if (m_packagep && m_packagep->clonep()) m_packagep = m_packagep->clonep();
|
||||
}
|
||||
void AstPackageImport::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " -> " << packagep();
|
||||
}
|
||||
const char* AstPackageImport::broken() const {
|
||||
BROKEN_RTN(!m_packagep || !m_packagep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
void AstPackageImport::cloneRelink() {
|
||||
if (m_packagep && m_packagep->clonep()) m_packagep = m_packagep->clonep();
|
||||
}
|
||||
void AstPatMember::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
this->AstNodeMath::dump(str);
|
||||
if (isDefault()) str << " [DEFAULT]";
|
||||
}
|
||||
void AstNodeTriop::dump(std::ostream& str) const { this->AstNodeMath::dump(str); }
|
||||
@@ -1646,7 +1865,7 @@ void AstTypeTable::dump(std::ostream& str) const {
|
||||
for (int i = 0; i < static_cast<int>(VBasicDTypeKwd::_ENUM_MAX); ++i) {
|
||||
if (AstBasicDType* const subnodep = m_basicps[i]) {
|
||||
str << '\n'; // Newline from caller, so newline first
|
||||
str << "\t\t" << std::setw(8) << VBasicDTypeKwd(i).ascii();
|
||||
str << "\t\t" << std::setw(8) << VBasicDTypeKwd{i}.ascii();
|
||||
str << " -> ";
|
||||
subnodep->dump(str);
|
||||
}
|
||||
@@ -1683,10 +1902,34 @@ string AstQueueDType::prettyDTypeName() const {
|
||||
if (boundConst()) str += ":" + cvtToStr(boundConst());
|
||||
return str + "]";
|
||||
}
|
||||
void AstWildcardArrayDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "[*]";
|
||||
}
|
||||
bool AstWildcardArrayDType::same(const AstNode* samep) const {
|
||||
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
if (!asamep->subDTypep()) return false;
|
||||
return (subDTypep() == asamep->subDTypep());
|
||||
}
|
||||
bool AstWildcardArrayDType::similarDType(AstNodeDType* samep) const {
|
||||
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
return type() == samep->type() && asamep->subDTypep()
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
||||
}
|
||||
void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "[]";
|
||||
}
|
||||
bool AstUnsizedArrayDType::same(const AstNode* samep) const {
|
||||
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
if (!asamep->subDTypep()) return false;
|
||||
return (subDTypep() == asamep->subDTypep());
|
||||
}
|
||||
bool AstUnsizedArrayDType::similarDType(AstNodeDType* samep) const {
|
||||
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
return type() == samep->type() && asamep->subDTypep()
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
||||
}
|
||||
void AstEmptyQueueDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "emptyq";
|
||||
@@ -1734,6 +1977,9 @@ void AstVarRef::dump(std::ostream& str) const {
|
||||
str << "UNLINKED";
|
||||
}
|
||||
}
|
||||
bool AstVarRef::same(const AstNode* samep) const {
|
||||
return same(static_cast<const AstVarRef*>(samep));
|
||||
}
|
||||
void AstVar::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (isSc()) str << " [SC]";
|
||||
@@ -1766,7 +2012,7 @@ void AstScope::dump(std::ostream& str) const {
|
||||
str << " [modp=" << reinterpret_cast<const void*>(modp()) << "]";
|
||||
}
|
||||
void AstScopeName::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
this->AstNodeMath::dump(str);
|
||||
if (dpiExport()) str << " [DPIEX]";
|
||||
if (forFormat()) str << " [FMT]";
|
||||
}
|
||||
@@ -1778,6 +2024,10 @@ void AstSenItem::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " [" << edgeType().ascii() << "]";
|
||||
}
|
||||
void AstStrengthSpec::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " (" << m_s0.ascii() << ", " << m_s1.ascii() << ")";
|
||||
}
|
||||
void AstParseRef::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " [" << expect().ascii() << "]";
|
||||
@@ -1792,6 +2042,13 @@ void AstClassOrPackageRef::dump(std::ostream& str) const {
|
||||
str << "UNLINKED";
|
||||
}
|
||||
}
|
||||
AstNodeModule* AstClassOrPackageRef::classOrPackagep() const {
|
||||
AstNode* foundp = m_classOrPackageNodep;
|
||||
while (auto* const anodep = VN_CAST(foundp, Typedef)) foundp = anodep->subDTypep();
|
||||
if (auto* const anodep = VN_CAST(foundp, ClassRefDType)) foundp = anodep->classp();
|
||||
return VN_CAST(foundp, NodeModule);
|
||||
}
|
||||
|
||||
void AstDot::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (colon()) str << " [::]";
|
||||
@@ -1805,6 +2062,16 @@ void AstActive::dump(std::ostream& str) const {
|
||||
str << "UNLINKED";
|
||||
}
|
||||
}
|
||||
const char* AstActive::broken() const {
|
||||
BROKEN_RTN(m_sensesp && !m_sensesp->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
void AstActive::cloneRelink() {
|
||||
if (m_sensesp->clonep()) {
|
||||
m_sensesp = m_sensesp->clonep();
|
||||
UASSERT(m_sensesp, "Bad clone cross link: " << this);
|
||||
}
|
||||
}
|
||||
void AstNodeFTaskRef::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
if (classOrPackagep()) str << " pkg=" << nodeAddr(classOrPackagep());
|
||||
@@ -1833,7 +2100,7 @@ void AstNodeBlock::dump(std::ostream& str) const {
|
||||
if (unnamed()) str << " [UNNAMED]";
|
||||
}
|
||||
void AstBegin::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
this->AstNodeBlock::dump(str);
|
||||
if (generate()) str << " [GEN]";
|
||||
if (genforp()) str << " [GENFOR]";
|
||||
if (implied()) str << " [IMPLIED]";
|
||||
@@ -1859,7 +2126,7 @@ void AstCoverInc::dump(std::ostream& str) const {
|
||||
}
|
||||
}
|
||||
void AstFork::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
this->AstNodeBlock::dump(str);
|
||||
if (!joinType().join()) str << " [" << joinType() << "]";
|
||||
}
|
||||
void AstTraceDecl::dump(std::ostream& str) const {
|
||||
@@ -1908,6 +2175,14 @@ void AstCFunc::dump(std::ostream& str) const {
|
||||
if (isDestructor()) str << " [DTOR]";
|
||||
if (isVirtual()) str << " [VIRT]";
|
||||
}
|
||||
const char* AstCFunc::broken() const {
|
||||
BROKEN_RTN((m_scopep && !m_scopep->brokeExists()));
|
||||
return nullptr;
|
||||
}
|
||||
void AstCFunc::cloneRelink() {
|
||||
if (m_scopep && m_scopep->clonep()) m_scopep = m_scopep->clonep();
|
||||
}
|
||||
|
||||
void AstCUse::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " [" << useType() << "]";
|
||||
|
||||
-9422
File diff suppressed because it is too large
Load Diff
+22
-24
@@ -27,25 +27,25 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
template <class T_Node, class T_Data, int T_UserN> class AstUserAllocatorBase VL_NOT_FINAL {
|
||||
template <class T_Node, class T_Data, int T_UserN>
|
||||
class AstUserAllocatorBase VL_NOT_FINAL {
|
||||
static_assert(1 <= T_UserN && T_UserN <= 5, "Wrong user pointer number");
|
||||
static_assert(std::is_base_of<AstNode, T_Node>::value, "T_Node must be an AstNode type");
|
||||
|
||||
private:
|
||||
std::vector<T_Data*> m_allocated;
|
||||
|
||||
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) {
|
||||
T_Data* getUserp(const T_Node* nodep) const {
|
||||
if VL_CONSTEXPR_CXX17 (T_UserN == 1) {
|
||||
const VNUser user = nodep->user1u();
|
||||
return user.to<T_Data*>();
|
||||
} else if (T_UserN == 2) {
|
||||
} else if VL_CONSTEXPR_CXX17 (T_UserN == 2) {
|
||||
const VNUser user = nodep->user2u();
|
||||
return user.to<T_Data*>();
|
||||
} else if (T_UserN == 3) {
|
||||
} else if VL_CONSTEXPR_CXX17 (T_UserN == 3) {
|
||||
const VNUser user = nodep->user3u();
|
||||
return user.to<T_Data*>();
|
||||
} else if (T_UserN == 4) {
|
||||
} else if VL_CONSTEXPR_CXX17 (T_UserN == 4) {
|
||||
const VNUser user = nodep->user4u();
|
||||
return user.to<T_Data*>();
|
||||
} else {
|
||||
@@ -54,31 +54,29 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
inline void setUserp(T_Node* nodep, T_Data* userp) const {
|
||||
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'.
|
||||
if (T_UserN == 1) {
|
||||
nodep->user1u(VNUser(userp));
|
||||
} else if (T_UserN == 2) {
|
||||
nodep->user2u(VNUser(userp));
|
||||
} else if (T_UserN == 3) {
|
||||
nodep->user3u(VNUser(userp));
|
||||
} else if (T_UserN == 4) {
|
||||
nodep->user4u(VNUser(userp));
|
||||
void setUserp(T_Node* nodep, T_Data* userp) const {
|
||||
if VL_CONSTEXPR_CXX17 (T_UserN == 1) {
|
||||
nodep->user1u(VNUser{userp});
|
||||
} else if VL_CONSTEXPR_CXX17 (T_UserN == 2) {
|
||||
nodep->user2u(VNUser{userp});
|
||||
} else if VL_CONSTEXPR_CXX17 (T_UserN == 3) {
|
||||
nodep->user3u(VNUser{userp});
|
||||
} else if VL_CONSTEXPR_CXX17 (T_UserN == 4) {
|
||||
nodep->user4u(VNUser{userp});
|
||||
} else {
|
||||
nodep->user5u(VNUser(userp));
|
||||
nodep->user5u(VNUser{userp});
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
AstUserAllocatorBase() {
|
||||
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'.
|
||||
if (T_UserN == 1) {
|
||||
if VL_CONSTEXPR_CXX17 (T_UserN == 1) {
|
||||
VNUser1InUse::check();
|
||||
} else if (T_UserN == 2) {
|
||||
} else if VL_CONSTEXPR_CXX17 (T_UserN == 2) {
|
||||
VNUser2InUse::check();
|
||||
} else if (T_UserN == 3) {
|
||||
} else if VL_CONSTEXPR_CXX17 (T_UserN == 3) {
|
||||
VNUser3InUse::check();
|
||||
} else if (T_UserN == 4) {
|
||||
} else if VL_CONSTEXPR_CXX17 (T_UserN == 4) {
|
||||
VNUser4InUse::check();
|
||||
} else {
|
||||
VNUser5InUse::check();
|
||||
@@ -94,7 +92,7 @@ protected:
|
||||
|
||||
public:
|
||||
// Get a reference to the user data. If does not exist, construct it with given arguments.
|
||||
template <typename... Args> //
|
||||
template <typename... Args>
|
||||
T_Data& operator()(T_Node* nodep, Args&&... args) {
|
||||
T_Data* userp = getUserp(nodep);
|
||||
if (!userp) {
|
||||
|
||||
+29
-31
@@ -28,12 +28,15 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Begin.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
|
||||
class BeginState final {
|
||||
@@ -69,7 +72,6 @@ private:
|
||||
int m_ifDepth = 0; // Current if depth
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
string dot(const string& a, const string& b) {
|
||||
if (a == "") return b;
|
||||
@@ -84,7 +86,7 @@ private:
|
||||
string::size_type pos;
|
||||
while ((pos = dottedname.find("__DOT__")) != string::npos) {
|
||||
const string ident = dottedname.substr(0, pos);
|
||||
dottedname = dottedname.substr(pos + strlen("__DOT__"));
|
||||
dottedname = dottedname.substr(pos + std::strlen("__DOT__"));
|
||||
if (nodep->name() != "") {
|
||||
m_displayScope = dot(m_displayScope, ident);
|
||||
m_namedScope = dot(m_namedScope, ident);
|
||||
@@ -114,19 +116,19 @@ private:
|
||||
}
|
||||
} else {
|
||||
// Move to module
|
||||
m_modp->addStmtp(nodep);
|
||||
m_modp->addStmtsp(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
{
|
||||
m_modp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeFTask* nodep) override {
|
||||
void visit(AstNodeFTask* nodep) override {
|
||||
UINFO(8, " " << nodep << endl);
|
||||
// Rename it
|
||||
if (m_unnamedScope != "") {
|
||||
@@ -161,7 +163,7 @@ private:
|
||||
m_ftaskp = nullptr;
|
||||
}
|
||||
}
|
||||
virtual void visit(AstBegin* nodep) override {
|
||||
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);
|
||||
@@ -176,11 +178,7 @@ private:
|
||||
AstNode* addsp = nullptr;
|
||||
if (AstNode* const stmtsp = nodep->stmtsp()) {
|
||||
stmtsp->unlinkFrBackWithNext();
|
||||
if (addsp) {
|
||||
addsp = addsp->addNextNull(stmtsp);
|
||||
} else {
|
||||
addsp = stmtsp;
|
||||
}
|
||||
addsp = AstNode::addNext(addsp, stmtsp);
|
||||
}
|
||||
if (addsp) {
|
||||
nodep->replaceWith(addsp);
|
||||
@@ -190,7 +188,7 @@ private:
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstVar* nodep) override {
|
||||
void visit(AstVar* nodep) override {
|
||||
if (m_unnamedScope != "") {
|
||||
// Rename it
|
||||
nodep->name(dot(m_unnamedScope, nodep->name()));
|
||||
@@ -199,7 +197,7 @@ private:
|
||||
liftNode(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstTypedef* nodep) override {
|
||||
void visit(AstTypedef* nodep) override {
|
||||
if (m_unnamedScope != "") {
|
||||
// Rename it
|
||||
nodep->name(dot(m_unnamedScope, nodep->name()));
|
||||
@@ -208,7 +206,7 @@ private:
|
||||
liftNode(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCell* nodep) override {
|
||||
void visit(AstCell* nodep) override {
|
||||
UINFO(8, " CELL " << nodep << endl);
|
||||
if (m_namedScope != "") {
|
||||
m_statep->userMarkChanged(nodep);
|
||||
@@ -217,18 +215,18 @@ private:
|
||||
UINFO(8, " rename to " << nodep->name() << endl);
|
||||
// Move to module
|
||||
nodep->unlinkFrBack();
|
||||
m_modp->addStmtp(nodep);
|
||||
m_modp->addStmtsp(nodep);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstVarXRef* nodep) override {
|
||||
void visit(AstVarXRef* nodep) override {
|
||||
UINFO(9, " VARXREF " << nodep << endl);
|
||||
if (m_namedScope != "" && nodep->inlinedDots() == "" && !m_ftaskp) {
|
||||
nodep->inlinedDots(m_namedScope);
|
||||
UINFO(9, " rescope to " << nodep << endl);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstScopeName* nodep) override {
|
||||
void visit(AstScopeName* nodep) override {
|
||||
// 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
|
||||
@@ -236,20 +234,20 @@ private:
|
||||
const string scname = nodep->forFormat() ? m_displayScope : m_namedScope;
|
||||
if (!scname.empty()) {
|
||||
// To keep correct visual order, must add before other Text's
|
||||
AstNode* const afterp = nodep->scopeAttrp();
|
||||
AstText* const afterp = nodep->scopeAttrp();
|
||||
if (afterp) afterp->unlinkFrBackWithNext();
|
||||
nodep->scopeAttrp(new AstText{nodep->fileline(), string("__DOT__") + scname});
|
||||
if (afterp) nodep->scopeAttrp(afterp);
|
||||
nodep->addScopeAttrp(new AstText{nodep->fileline(), string("__DOT__") + scname});
|
||||
if (afterp) nodep->addScopeAttrp(afterp);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCoverDecl* nodep) override {
|
||||
void visit(AstCoverDecl* nodep) override {
|
||||
// Don't need to fix path in coverage statements, they're not under
|
||||
// any BEGINs, but V3Coverage adds them all under the module itself.
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
// VISITORS - LINT CHECK
|
||||
virtual void visit(AstIf* nodep) override { // not AstNodeIf; other types not covered
|
||||
void visit(AstIf* nodep) override { // not AstNodeIf; other types not covered
|
||||
// Check IFDEPTH warning - could be in other transform files if desire
|
||||
VL_RESTORER(m_ifDepth);
|
||||
if (m_ifDepth == -1 || v3Global.opt.ifDepth() < 1) { // Turned off
|
||||
@@ -263,7 +261,7 @@ private:
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -271,7 +269,7 @@ public:
|
||||
: m_statep{statep} {
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~BeginVisitor() override = default;
|
||||
~BeginVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -284,21 +282,21 @@ private:
|
||||
// AstNodeFTask::user1p // Node replaced, rename it
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeFTaskRef* nodep) override {
|
||||
void visit(AstNodeFTaskRef* nodep) override {
|
||||
if (nodep->taskp()->user1()) { // It was converted
|
||||
UINFO(9, " relinkFTask " << nodep << endl);
|
||||
nodep->name(nodep->taskp()->name());
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep) override {
|
||||
void visit(AstVarRef* nodep) override {
|
||||
if (nodep->varp()->user1()) { // It was converted
|
||||
UINFO(9, " relinVarRef " << nodep << endl);
|
||||
nodep->name(nodep->varp()->name());
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstIfaceRefDType* nodep) override {
|
||||
void visit(AstIfaceRefDType* nodep) override {
|
||||
// May have changed cell names
|
||||
// TypeTable is always after all modules, so names are stable
|
||||
UINFO(8, " IFACEREFDTYPE " << nodep << endl);
|
||||
@@ -307,12 +305,12 @@ private:
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
BeginRelinkVisitor(AstNetlist* nodep, BeginState*) { iterate(nodep); }
|
||||
virtual ~BeginRelinkVisitor() override = default;
|
||||
~BeginRelinkVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -325,5 +323,5 @@ void V3Begin::debeginAll(AstNetlist* nodep) {
|
||||
{ BeginVisitor{nodep, &state}; }
|
||||
if (state.anyFuncInBegin()) { BeginRelinkVisitor{nodep, &state}; }
|
||||
} // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("begin", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("begin", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+10
-8
@@ -26,12 +26,15 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Branch.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Branch state, as a visitor of each AstNode
|
||||
|
||||
@@ -48,7 +51,6 @@ private:
|
||||
std::vector<AstCFunc*> m_cfuncsp; // List of all tasks
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void reset() {
|
||||
m_likely = false;
|
||||
@@ -62,14 +64,14 @@ private:
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeIf* nodep) override {
|
||||
void visit(AstNodeIf* nodep) override {
|
||||
UINFO(4, " IF: " << nodep << endl);
|
||||
VL_RESTORER(m_likely);
|
||||
VL_RESTORER(m_unlikely);
|
||||
{
|
||||
// Do if
|
||||
reset();
|
||||
iterateAndNextNull(nodep->ifsp());
|
||||
iterateAndNextNull(nodep->thensp());
|
||||
const int ifLikely = m_likely;
|
||||
const int ifUnlikely = m_unlikely;
|
||||
// Do else
|
||||
@@ -86,17 +88,17 @@ private:
|
||||
} // else leave unknown
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeCCall* nodep) override {
|
||||
void visit(AstNodeCCall* nodep) override {
|
||||
checkUnlikely(nodep);
|
||||
nodep->funcp()->user1Inc();
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
void visit(AstCFunc* nodep) override {
|
||||
checkUnlikely(nodep);
|
||||
m_cfuncsp.push_back(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
void visit(AstNode* nodep) override {
|
||||
checkUnlikely(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
@@ -115,7 +117,7 @@ public:
|
||||
iterateChildren(nodep);
|
||||
calc_tasks();
|
||||
}
|
||||
virtual ~BranchVisitor() override = default;
|
||||
~BranchVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
+19
-16
@@ -25,9 +25,10 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Broken.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
// This visitor does not edit nodes, and is called at error-exit, so should use constant iterators
|
||||
#include "V3AstConstOnly.h"
|
||||
@@ -35,6 +36,8 @@
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Generation counter for AstNode::m_brokenState
|
||||
|
||||
@@ -48,7 +51,7 @@ static class BrokenCntGlobal {
|
||||
uint8_t m_count = MIN_VALUE;
|
||||
|
||||
public:
|
||||
uint8_t get() {
|
||||
uint8_t get() const {
|
||||
UASSERT(MIN_VALUE <= m_count && m_count <= MAX_VALUE, "Invalid generation number");
|
||||
return m_count;
|
||||
}
|
||||
@@ -131,8 +134,8 @@ private:
|
||||
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; }
|
||||
void addLinkable(const AstNode* nodep) { m_linkable.emplace(nodep); }
|
||||
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); }
|
||||
@@ -213,27 +216,27 @@ private:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep) override {
|
||||
void visit(AstNodeAssign* nodep) override {
|
||||
processAndIterate(nodep);
|
||||
UASSERT_OBJ(!(v3Global.assertDTypesResolved() && nodep->brokeLhsMustBeLvalue()
|
||||
&& VN_IS(nodep->lhsp(), NodeVarRef)
|
||||
&& !VN_AS(nodep->lhsp(), NodeVarRef)->access().isWriteOrRW()),
|
||||
nodep, "Assignment LHS is not an lvalue");
|
||||
}
|
||||
virtual void visit(AstRelease* nodep) override {
|
||||
void visit(AstRelease* nodep) override {
|
||||
processAndIterate(nodep);
|
||||
UASSERT_OBJ(!(v3Global.assertDTypesResolved() && VN_IS(nodep->lhsp(), NodeVarRef)
|
||||
&& !VN_AS(nodep->lhsp(), NodeVarRef)->access().isWriteOrRW()),
|
||||
nodep, "Release LHS is not an lvalue");
|
||||
}
|
||||
virtual void visit(AstScope* nodep) override {
|
||||
void visit(AstScope* nodep) override {
|
||||
VL_RESTORER(m_inScope);
|
||||
{
|
||||
m_inScope = true;
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeVarRef* nodep) override {
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
processAndIterate(nodep);
|
||||
// m_inScope because some Vars have initial variable references without scopes
|
||||
// This might false fire with some debug flags, as not certain we don't have temporary
|
||||
@@ -251,7 +254,7 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
void visit(AstCFunc* nodep) override {
|
||||
UASSERT_OBJ(!m_cfuncp, nodep, "Nested AstCFunc");
|
||||
m_cfuncp = nodep;
|
||||
m_localVars.clear();
|
||||
@@ -269,14 +272,14 @@ private:
|
||||
|
||||
m_cfuncp = nullptr;
|
||||
}
|
||||
virtual void visit(AstNodeIf* nodep) override {
|
||||
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()) {
|
||||
if (AstNode* const thensp = nodep->thensp()) {
|
||||
pushLocalScope();
|
||||
processAndIterateList(ifsp);
|
||||
processAndIterateList(thensp);
|
||||
popLocalScope();
|
||||
}
|
||||
if (AstNode* const elsesp = nodep->elsesp()) {
|
||||
@@ -287,21 +290,21 @@ private:
|
||||
processExit(nodep);
|
||||
popLocalScope();
|
||||
}
|
||||
virtual void visit(AstNodeStmt* nodep) override {
|
||||
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 {
|
||||
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 {
|
||||
void visit(AstNode* nodep) override {
|
||||
// Process not just iterate
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
@@ -309,7 +312,7 @@ private:
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit BrokenCheckVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~BrokenCheckVisitor() override = default;
|
||||
~BrokenCheckVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
+9
-6
@@ -27,13 +27,16 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3CCtors.h"
|
||||
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
class VCtorType final {
|
||||
public:
|
||||
enum en : uint8_t { MODULE, CLASS, COVERAGE };
|
||||
@@ -43,7 +46,7 @@ private:
|
||||
|
||||
public:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline VCtorType(en _e)
|
||||
constexpr VCtorType(en _e)
|
||||
: m_e{_e} {}
|
||||
bool isClass() const { return m_e == CLASS; }
|
||||
bool isCoverage() const { return m_e == COVERAGE; }
|
||||
@@ -76,7 +79,7 @@ private:
|
||||
if (!preventUnusedStmt.empty()) {
|
||||
funcp->addStmtsp(new AstCStmt{m_modp->fileline(), preventUnusedStmt});
|
||||
}
|
||||
m_modp->addStmtp(funcp);
|
||||
m_modp->addStmtsp(funcp);
|
||||
m_numStmts = 0;
|
||||
return funcp;
|
||||
}
|
||||
@@ -135,7 +138,7 @@ void V3CCtors::evalAsserts() {
|
||||
funcp->isLoose(true);
|
||||
funcp->slow(false);
|
||||
funcp->ifdef("VL_DEBUG");
|
||||
modp->addStmtp(funcp);
|
||||
modp->addStmtsp(funcp);
|
||||
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
||||
if (AstVar* const varp = VN_CAST(np, Var)) {
|
||||
if (varp->isPrimaryInish() && !varp->isSc()) {
|
||||
@@ -208,7 +211,7 @@ void V3CCtors::cctorsAll() {
|
||||
// If can be referred to by base pointer, need virtual delete
|
||||
funcp->isVirtual(classp->isExtended());
|
||||
funcp->slow(false);
|
||||
modp->addStmtp(funcp);
|
||||
modp->addStmtsp(funcp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-8
@@ -25,12 +25,15 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3CUse.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
|
||||
// Visit within a module all nodes and data types they reference, finding
|
||||
@@ -50,13 +53,13 @@ class CUseVisitor final : public VNVisitor {
|
||||
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);
|
||||
m_modp->addStmtsp(newp);
|
||||
UINFO(8, "Insert " << newp << endl);
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstClassRefDType* nodep) override {
|
||||
void visit(AstClassRefDType* nodep) override {
|
||||
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
|
||||
@@ -66,17 +69,17 @@ class CUseVisitor final : public VNVisitor {
|
||||
iterateChildren(nodep->classp()); // This also gets all extend classes
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeDType* nodep) override {
|
||||
void visit(AstNodeDType* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (nodep->virtRefDTypep()) iterate(nodep->virtRefDTypep());
|
||||
if (nodep->virtRefDType2p()) iterate(nodep->virtRefDType2p());
|
||||
}
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
void visit(AstNode* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (nodep->dtypep() && !nodep->dtypep()->user1()) iterate(nodep->dtypep());
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCell* nodep) override {
|
||||
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());
|
||||
@@ -89,7 +92,7 @@ public:
|
||||
: m_modp(modp) {
|
||||
iterate(modp);
|
||||
}
|
||||
virtual ~CUseVisitor() override = default;
|
||||
~CUseVisitor() override = default;
|
||||
VL_UNCOPYABLE(CUseVisitor);
|
||||
};
|
||||
|
||||
@@ -105,5 +108,5 @@ void V3CUse::cUseAll() {
|
||||
// for each output file and put under that
|
||||
CUseVisitor{modp};
|
||||
}
|
||||
V3Global::dumpCheckGlobalTree("cuse", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("cuse", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+17
-16
@@ -37,13 +37,16 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Case.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
#define CASE_OVERLAP_WIDTH 16 // Maximum width we can check for overlaps in
|
||||
#define CASE_BARF 999999 // Magic width when non-constant
|
||||
#define CASE_ENCODER_GROUP_DEPTH 8 // Levels of priority to be ORed together in top IF tree
|
||||
@@ -56,9 +59,8 @@ private:
|
||||
= nullptr; // Under a CASE value node, if so the relevant case statement
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
virtual void visit(AstNodeCase* nodep) override {
|
||||
void visit(AstNodeCase* nodep) override {
|
||||
if (VN_IS(nodep, Case) && VN_AS(nodep, Case)->casex()) {
|
||||
nodep->v3warn(CASEX, "Suggest casez (with ?'s) in place of casex (with X's)");
|
||||
}
|
||||
@@ -85,7 +87,7 @@ private:
|
||||
m_caseExprp = nullptr;
|
||||
}
|
||||
}
|
||||
virtual void visit(AstConst* nodep) override {
|
||||
void visit(AstConst* nodep) override {
|
||||
// See also neverItem
|
||||
if (m_caseExprp && nodep->num().isFourState()) {
|
||||
if (VN_IS(m_caseExprp, GenCase)) {
|
||||
@@ -106,12 +108,12 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CaseLintVisitor(AstNodeCase* nodep) { iterate(nodep); }
|
||||
virtual ~CaseLintVisitor() override = default;
|
||||
~CaseLintVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -137,7 +139,6 @@ private:
|
||||
std::array<AstNode*, 1 << CASE_OVERLAP_WIDTH> m_valueItem;
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
bool isCaseTreeFast(AstCase* nodep) {
|
||||
int width = 0;
|
||||
@@ -244,7 +245,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_AS(m_valueItem[i], CaseItem)->bodysp();
|
||||
m_valueItem[i] = VN_AS(m_valueItem[i], CaseItem)->stmtsp();
|
||||
}
|
||||
return true; // All is fine
|
||||
}
|
||||
@@ -345,7 +346,7 @@ private:
|
||||
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()));
|
||||
itemp->addCondsp(new AstConst(itemp->fileline(), AstConst::BitTrue()));
|
||||
hadDefault = true;
|
||||
} else {
|
||||
// Expressioned clause
|
||||
@@ -396,7 +397,7 @@ private:
|
||||
}
|
||||
}
|
||||
// Replace expression in tree
|
||||
itemp->condsp(ifexprp);
|
||||
itemp->addCondsp(ifexprp);
|
||||
}
|
||||
}
|
||||
VL_DO_DANGLING(cexprp->deleteTree(), cexprp);
|
||||
@@ -419,7 +420,7 @@ private:
|
||||
AstIf* itemnextp = nullptr;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), CaseItem)) {
|
||||
AstNode* const istmtsp = itemp->bodysp(); // Maybe null -- no action.
|
||||
AstNode* const istmtsp = itemp->stmtsp(); // Maybe null -- no action.
|
||||
if (istmtsp) istmtsp->unlinkFrBackWithNext();
|
||||
// Expressioned clause
|
||||
AstNode* const ifexprp = itemp->condsp()->unlinkFrBack();
|
||||
@@ -451,7 +452,7 @@ private:
|
||||
if (itemnextp) {
|
||||
itemnextp->addElsesp(newp);
|
||||
} else {
|
||||
groupnextp->addIfsp(newp); // First in a new group
|
||||
groupnextp->addThensp(newp); // First in a new group
|
||||
}
|
||||
itemnextp = newp;
|
||||
}
|
||||
@@ -492,7 +493,7 @@ private:
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstCase* nodep) override {
|
||||
void visit(AstCase* nodep) override {
|
||||
V3Case::caseLint(nodep);
|
||||
iterateChildren(nodep);
|
||||
if (debug() >= 9) nodep->dumpTree(cout, " case_old: ");
|
||||
@@ -509,7 +510,7 @@ private:
|
||||
}
|
||||
}
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
void visit(AstNode* nodep) override {
|
||||
if (VN_IS(nodep, Always)) m_alwaysp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
@@ -520,7 +521,7 @@ public:
|
||||
for (auto& itr : m_valueItem) itr = nullptr;
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CaseVisitor() override {
|
||||
~CaseVisitor() override {
|
||||
V3Stats::addStat("Optimizations, Cases parallelized", m_statCaseFast);
|
||||
V3Stats::addStat("Optimizations, Cases complex", m_statCaseSlow);
|
||||
}
|
||||
@@ -532,7 +533,7 @@ public:
|
||||
void V3Case::caseAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CaseVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("case", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("case", 0, dumpTree() >= 3);
|
||||
}
|
||||
void V3Case::caseLint(AstNodeCase* nodep) {
|
||||
UINFO(4, __FUNCTION__ << ": " << endl);
|
||||
|
||||
+19
-17
@@ -40,12 +40,15 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Cast.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Cast state, as a visitor of each AstNode
|
||||
|
||||
@@ -59,7 +62,6 @@ private:
|
||||
// STATE
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void insertCast(AstNode* nodep, int needsize) { // We'll insert ABOVE passed node
|
||||
UINFO(4, " NeedCast " << nodep << endl);
|
||||
@@ -110,25 +112,25 @@ private:
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeUniop* nodep) override {
|
||||
void visit(AstNodeUniop* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1());
|
||||
if (nodep->sizeMattersLhs()) ensureCast(nodep->lhsp());
|
||||
}
|
||||
virtual void visit(AstNodeBiop* nodep) override {
|
||||
void visit(AstNodeBiop* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1() | nodep->rhsp()->user1());
|
||||
if (nodep->sizeMattersLhs()) ensureCast(nodep->lhsp());
|
||||
if (nodep->sizeMattersRhs()) ensureCast(nodep->rhsp());
|
||||
}
|
||||
virtual void visit(AstNodeTriop* nodep) override {
|
||||
void visit(AstNodeTriop* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1() | nodep->rhsp()->user1() | nodep->thsp()->user1());
|
||||
if (nodep->sizeMattersLhs()) ensureCast(nodep->lhsp());
|
||||
if (nodep->sizeMattersRhs()) ensureCast(nodep->rhsp());
|
||||
if (nodep->sizeMattersThs()) ensureCast(nodep->thsp());
|
||||
}
|
||||
virtual void visit(AstNodeQuadop* nodep) override {
|
||||
void visit(AstNodeQuadop* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1() | nodep->rhsp()->user1() | nodep->thsp()->user1()
|
||||
| nodep->fhsp()->user1());
|
||||
@@ -137,12 +139,12 @@ private:
|
||||
if (nodep->sizeMattersThs()) ensureCast(nodep->thsp());
|
||||
if (nodep->sizeMattersFhs()) ensureCast(nodep->fhsp());
|
||||
}
|
||||
virtual void visit(AstCCast* nodep) override {
|
||||
void visit(AstCCast* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureLower32Cast(nodep);
|
||||
nodep->user1(1);
|
||||
}
|
||||
virtual void visit(AstNegate* nodep) override {
|
||||
void visit(AstNegate* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1());
|
||||
if (nodep->lhsp()->widthMin() == 1) {
|
||||
@@ -154,7 +156,7 @@ private:
|
||||
ensureCast(nodep->lhsp());
|
||||
}
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep) override {
|
||||
void visit(AstVarRef* nodep) override {
|
||||
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()
|
||||
@@ -165,7 +167,7 @@ private:
|
||||
}
|
||||
nodep->user1(1);
|
||||
}
|
||||
virtual void visit(AstConst* nodep) override {
|
||||
void visit(AstConst* nodep) override {
|
||||
// Constants are of unknown size if smaller than 33 bits, because
|
||||
// we're too lazy to wrap every constant in the universe in
|
||||
// ((IData)#).
|
||||
@@ -173,29 +175,29 @@ private:
|
||||
}
|
||||
|
||||
// Null dereference protection
|
||||
virtual void visit(AstNullCheck* nodep) override {
|
||||
void visit(AstNullCheck* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1());
|
||||
}
|
||||
virtual void visit(AstCMethodCall* nodep) override {
|
||||
void visit(AstCMethodCall* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureNullChecked(nodep->fromp());
|
||||
}
|
||||
virtual void visit(AstMemberSel* nodep) override {
|
||||
void visit(AstMemberSel* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureNullChecked(nodep->fromp());
|
||||
}
|
||||
|
||||
// NOPs
|
||||
virtual void visit(AstVar*) override {}
|
||||
void visit(AstVar*) override {}
|
||||
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CastVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~CastVisitor() override = default;
|
||||
~CastVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -204,5 +206,5 @@ public:
|
||||
void V3Cast::castAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CastVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("cast", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("cast", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+47
-49
@@ -24,26 +24,28 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Cdc.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Graph.h"
|
||||
#include "V3Const.h"
|
||||
#include "V3EmitV.h"
|
||||
#include "V3File.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Graph.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
constexpr int CDC_WEIGHT_ASYNC = 0x1000; // Weight for edges that feed async logic
|
||||
|
||||
//######################################################################
|
||||
|
||||
class CdcBaseVisitor VL_NOT_FINAL : public VNVisitor {
|
||||
public:
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -66,9 +68,9 @@ public:
|
||||
, m_srcDomainSet{false}
|
||||
, m_dstDomainSet{false}
|
||||
, m_asyncPath{false} {}
|
||||
virtual ~CdcEitherVertex() override = default;
|
||||
~CdcEitherVertex() override = default;
|
||||
// ACCESSORS
|
||||
virtual FileLine* fileline() const override { return nodep()->fileline(); }
|
||||
FileLine* fileline() const override { return nodep()->fileline(); }
|
||||
AstScope* scopep() const { return m_scopep; }
|
||||
AstNode* nodep() const { return m_nodep; }
|
||||
AstSenTree* srcDomainp() const { return m_srcDomainp; }
|
||||
@@ -92,11 +94,11 @@ public:
|
||||
CdcVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
|
||||
: CdcEitherVertex{graphp, scopep, varScp}
|
||||
, m_varScp{varScp} {}
|
||||
virtual ~CdcVarVertex() override = default;
|
||||
~CdcVarVertex() override = default;
|
||||
// ACCESSORS
|
||||
AstVarScope* varScp() const { return m_varScp; }
|
||||
virtual string name() const override { return (cvtToHex(m_varScp) + " " + varScp()->name()); }
|
||||
virtual string dotColor() const override {
|
||||
string name() const override { return (cvtToHex(m_varScp) + " " + varScp()->name()); }
|
||||
string dotColor() const override {
|
||||
return fromFlop() ? "green" : cntAsyncRst() ? "red" : "blue";
|
||||
}
|
||||
int cntAsyncRst() const { return m_cntAsyncRst; }
|
||||
@@ -117,12 +119,10 @@ public:
|
||||
srcDomainp(sensenodep);
|
||||
dstDomainp(sensenodep);
|
||||
}
|
||||
virtual ~CdcLogicVertex() override = default;
|
||||
~CdcLogicVertex() override = default;
|
||||
// ACCESSORS
|
||||
virtual string name() const override {
|
||||
return (cvtToHex(nodep()) + "@" + scopep()->prettyName());
|
||||
}
|
||||
virtual string dotColor() const override { return hazard() ? "black" : "yellow"; }
|
||||
string name() const override { return (cvtToHex(nodep()) + "@" + scopep()->prettyName()); }
|
||||
string dotColor() const override { return hazard() ? "black" : "yellow"; }
|
||||
bool hazard() const { return m_hazard; }
|
||||
void setHazard(AstNode* nodep) {
|
||||
m_hazard = true;
|
||||
@@ -143,7 +143,7 @@ private:
|
||||
std::ofstream* const m_ofp = nullptr; // Output file
|
||||
string m_prefix;
|
||||
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
void visit(AstNode* nodep) override {
|
||||
*m_ofp << m_prefix;
|
||||
if (nodep->user3()) {
|
||||
*m_ofp << " %%";
|
||||
@@ -170,7 +170,7 @@ public:
|
||||
, m_prefix{prefix} {
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CdcDumpVisitor() override = default;
|
||||
~CdcDumpVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -180,7 +180,7 @@ private:
|
||||
int m_maxLineno = 0;
|
||||
size_t m_maxFilenameLen = 0;
|
||||
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
void visit(AstNode* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
// Keeping line+filename lengths separate is much faster than calling ascii().length()
|
||||
if (nodep->fileline()->lineno() >= m_maxLineno) {
|
||||
@@ -194,7 +194,7 @@ private:
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CdcWidthVisitor(AstNode* nodep) { iterate(nodep); }
|
||||
virtual ~CdcWidthVisitor() override = default;
|
||||
~CdcWidthVisitor() override = default;
|
||||
// ACCESSORS
|
||||
int maxWidth() const {
|
||||
size_t width = 1;
|
||||
@@ -324,13 +324,11 @@ private:
|
||||
|
||||
void analyze() {
|
||||
UINFO(3, __FUNCTION__ << ": " << endl);
|
||||
// if (debug() > 6) m_graph.dump();
|
||||
if (debug() > 6) m_graph.dumpDotFilePrefixed("cdc_pre");
|
||||
if (dumpGraph() > 6) m_graph.dumpDotFilePrefixed("cdc_pre");
|
||||
// This will MAX across edge weights
|
||||
m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue);
|
||||
//
|
||||
m_graph.removeRedundantEdges(
|
||||
&V3GraphEdge::followAlwaysTrue); // This will MAX across edge weights
|
||||
//
|
||||
m_graph.dumpDotFilePrefixed("cdc_simp");
|
||||
if (dumpGraph() >= 3) m_graph.dumpDotFilePrefixed("cdc_simp");
|
||||
//
|
||||
analyzeReset();
|
||||
}
|
||||
@@ -627,21 +625,21 @@ private:
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
{
|
||||
m_modp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstScope* nodep) override {
|
||||
void visit(AstScope* nodep) override {
|
||||
UINFO(4, " SCOPE " << nodep << endl);
|
||||
m_scopep = nodep;
|
||||
m_logicVertexp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
m_scopep = nullptr;
|
||||
}
|
||||
virtual void visit(AstActive* nodep) override {
|
||||
void visit(AstActive* nodep) override {
|
||||
// Create required blocks and add to module
|
||||
UINFO(4, " BLOCK " << nodep << endl);
|
||||
AstNode::user2ClearTree();
|
||||
@@ -653,7 +651,7 @@ private:
|
||||
m_domainp = nullptr;
|
||||
AstNode::user2ClearTree();
|
||||
}
|
||||
virtual void visit(AstNodeVarRef* nodep) override {
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
if (m_scopep) {
|
||||
UASSERT_OBJ(m_logicVertexp, nodep, "Var ref not under a logic block");
|
||||
AstVarScope* const varscp = nodep->varScopep();
|
||||
@@ -681,53 +679,53 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstAssignDly* nodep) override {
|
||||
void visit(AstAssignDly* nodep) override {
|
||||
m_inDly = true;
|
||||
iterateChildren(nodep);
|
||||
m_inDly = false;
|
||||
}
|
||||
virtual void visit(AstSenItem* nodep) override {
|
||||
void visit(AstSenItem* nodep) override {
|
||||
m_inSenItem = true;
|
||||
iterateChildren(nodep);
|
||||
m_inSenItem = false;
|
||||
}
|
||||
virtual void visit(AstAlways* nodep) override { iterateNewStmt(nodep); }
|
||||
virtual void visit(AstAlwaysPublic* nodep) override {
|
||||
void visit(AstAlways* nodep) override { iterateNewStmt(nodep); }
|
||||
void visit(AstAlwaysPublic* nodep) override {
|
||||
// CDC doesn't care about public variables
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) override { iterateNewStmt(nodep); }
|
||||
virtual void visit(AstAssignAlias* nodep) override { iterateNewStmt(nodep); }
|
||||
virtual void visit(AstAssignW* nodep) override { iterateNewStmt(nodep); }
|
||||
void visit(AstCFunc* nodep) override { iterateNewStmt(nodep); }
|
||||
void visit(AstAssignAlias* nodep) override { iterateNewStmt(nodep); }
|
||||
void visit(AstAssignW* nodep) override { iterateNewStmt(nodep); }
|
||||
|
||||
// Math that shouldn't cause us to clear hazard
|
||||
virtual void visit(AstConst*) override {}
|
||||
virtual void visit(AstReplicate* nodep) override { iterateChildren(nodep); }
|
||||
virtual void visit(AstConcat* nodep) override { iterateChildren(nodep); }
|
||||
virtual void visit(AstNot* nodep) override { iterateChildren(nodep); }
|
||||
virtual void visit(AstSel* nodep) override {
|
||||
void visit(AstConst*) override {}
|
||||
void visit(AstReplicate* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstConcat* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNot* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstSel* nodep) override {
|
||||
if (!VN_IS(nodep->lsbp(), Const)) setNodeHazard(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeSel* nodep) override {
|
||||
void visit(AstNodeSel* nodep) override {
|
||||
if (!VN_IS(nodep->bitp(), Const)) setNodeHazard(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
// Ignores
|
||||
virtual void visit(AstInitial*) override {}
|
||||
virtual void visit(AstInitialAutomatic*) override {}
|
||||
virtual void visit(AstInitialStatic*) override {}
|
||||
virtual void visit(AstTraceDecl*) override {}
|
||||
virtual void visit(AstCoverToggle*) override {}
|
||||
virtual void visit(AstNodeDType*) override {}
|
||||
void visit(AstInitial*) override {}
|
||||
void visit(AstInitialAutomatic*) override {}
|
||||
void visit(AstInitialStatic*) override {}
|
||||
void visit(AstTraceDecl*) override {}
|
||||
void visit(AstCoverToggle*) override {}
|
||||
void visit(AstNodeDType*) override {}
|
||||
|
||||
//--------------------
|
||||
// Default
|
||||
virtual void visit(AstNodeMath* nodep) override {
|
||||
void visit(AstNodeMath* nodep) override {
|
||||
setNodeHazard(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -753,7 +751,7 @@ public:
|
||||
*m_ofp << '\n';
|
||||
}
|
||||
}
|
||||
virtual ~CdcVisitor() override {
|
||||
~CdcVisitor() override {
|
||||
if (m_ofp) VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
+16
-13
@@ -29,12 +29,15 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3Changed.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
|
||||
class ChangedState final {
|
||||
@@ -66,7 +69,7 @@ public:
|
||||
m_tlChgFuncp->isStatic(false);
|
||||
m_tlChgFuncp->isLoose(true);
|
||||
m_tlChgFuncp->declPrivate(true);
|
||||
m_scopetopp->addActivep(m_tlChgFuncp);
|
||||
m_scopetopp->addBlocksp(m_tlChgFuncp);
|
||||
// Each change detection function needs at least one AstChangeDet
|
||||
// to ensure that V3EmitC outputs the necessary code.
|
||||
maybeCreateMidChg();
|
||||
@@ -85,7 +88,7 @@ public:
|
||||
m_chgFuncp->isStatic(false);
|
||||
m_chgFuncp->isLoose(true);
|
||||
m_chgFuncp->declPrivate(true);
|
||||
m_scopetopp->addActivep(m_chgFuncp);
|
||||
m_scopetopp->addBlocksp(m_chgFuncp);
|
||||
|
||||
// Add a top call to it
|
||||
AstCCall* const callp = new AstCCall{m_scopetopp->fileline(), m_chgFuncp};
|
||||
@@ -154,13 +157,13 @@ private:
|
||||
m_state.m_numStmts += initp->nodeCount() + m_varEqnp->widthWords();
|
||||
}
|
||||
|
||||
virtual void visit(AstBasicDType*) override { //
|
||||
void visit(AstBasicDType*) override { //
|
||||
newChangeDet();
|
||||
}
|
||||
virtual void visit(AstPackArrayDType*) override { //
|
||||
void visit(AstPackArrayDType*) override { //
|
||||
newChangeDet();
|
||||
}
|
||||
virtual void visit(AstUnpackArrayDType* nodep) override {
|
||||
void visit(AstUnpackArrayDType* nodep) override {
|
||||
for (int index = 0; index < nodep->elementsConst(); ++index) {
|
||||
VL_RESTORER(m_varEqnp);
|
||||
VL_RESTORER(m_newLvEqnp);
|
||||
@@ -180,7 +183,7 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeUOrStructDType* nodep) override {
|
||||
void visit(AstNodeUOrStructDType* nodep) override {
|
||||
if (nodep->packedUnsup()) {
|
||||
newChangeDet();
|
||||
} else {
|
||||
@@ -190,7 +193,7 @@ private:
|
||||
<< m_vscp->varp()->prettyNameQ());
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
void visit(AstNode* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (debug()) nodep->dumpTree(cout, "-DETECTARRAY-general-");
|
||||
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable"
|
||||
@@ -217,9 +220,9 @@ public:
|
||||
// CHANGEDET(VARREF(_last), VARREF(var))
|
||||
AstVar* const newvarp
|
||||
= new AstVar{varp->fileline(), VVarType::MODULETEMP, newvarname, varp};
|
||||
m_state.m_topModp->addStmtp(newvarp);
|
||||
m_state.m_topModp->addStmtsp(newvarp);
|
||||
m_newvscp = new AstVarScope{m_vscp->fileline(), m_state.m_scopetopp, newvarp};
|
||||
m_state.m_scopetopp->addVarp(m_newvscp);
|
||||
m_state.m_scopetopp->addVarsp(m_newvscp);
|
||||
|
||||
m_varEqnp = new AstVarRef{m_vscp->fileline(), m_vscp, VAccess::READ};
|
||||
m_newLvEqnp = new AstVarRef{m_vscp->fileline(), m_newvscp, VAccess::WRITE};
|
||||
@@ -230,7 +233,7 @@ public:
|
||||
m_newLvEqnp->deleteTree();
|
||||
m_newRvEqnp->deleteTree();
|
||||
}
|
||||
virtual ~ChangedInsertVisitor() override = default;
|
||||
~ChangedInsertVisitor() override = default;
|
||||
VL_UNCOPYABLE(ChangedInsertVisitor);
|
||||
};
|
||||
|
||||
@@ -251,5 +254,5 @@ void V3Changed::changedAll(AstNetlist* nodep) {
|
||||
}
|
||||
});
|
||||
|
||||
V3Global::dumpCheckGlobalTree("changed", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("changed", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+35
-29
@@ -23,9 +23,12 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Class.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
|
||||
@@ -46,14 +49,13 @@ private:
|
||||
std::vector<std::pair<AstNode*, AstNodeModule*>> m_toPackageMoves;
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
virtual void visit(AstClass* nodep) override {
|
||||
void visit(AstClass* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
// Move this class
|
||||
nodep->name(m_prefix + nodep->name());
|
||||
nodep->unlinkFrBack();
|
||||
v3Global.rootp()->addModulep(nodep);
|
||||
v3Global.rootp()->addModulesp(nodep);
|
||||
// Make containing package
|
||||
// Note origName is the same as the class origName so errors look correct
|
||||
AstClassPackage* const packagep
|
||||
@@ -61,7 +63,7 @@ private:
|
||||
packagep->name(nodep->name() + "__Vclpkg");
|
||||
nodep->classOrPackagep(packagep);
|
||||
packagep->classp(nodep);
|
||||
v3Global.rootp()->addModulep(packagep);
|
||||
v3Global.rootp()->addModulesp(packagep);
|
||||
// Add package to hierarchy
|
||||
AstCell* const cellp = new AstCell{packagep->fileline(),
|
||||
packagep->fileline(),
|
||||
@@ -71,7 +73,7 @@ private:
|
||||
nullptr,
|
||||
nullptr};
|
||||
cellp->modp(packagep);
|
||||
v3Global.rootp()->topModulep()->addStmtp(cellp);
|
||||
v3Global.rootp()->topModulep()->addStmtsp(cellp);
|
||||
// Find class's scope
|
||||
// Alternative would be to move this and related to V3Scope
|
||||
const AstScope* classScopep = nullptr;
|
||||
@@ -84,7 +86,7 @@ private:
|
||||
AstScope* const scopep
|
||||
= new AstScope{nodep->fileline(), packagep, classScopep->name(),
|
||||
classScopep->aboveScopep(), classScopep->aboveCellp()};
|
||||
packagep->addStmtp(scopep);
|
||||
packagep->addStmtsp(scopep);
|
||||
// Iterate
|
||||
VL_RESTORER(m_prefix);
|
||||
VL_RESTORER(m_classPackagep);
|
||||
@@ -99,7 +101,7 @@ private:
|
||||
}
|
||||
nodep->repairCache();
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
// Visit for NodeModules that are not AstClass (AstClass is-a AstNodeModule)
|
||||
VL_RESTORER(m_prefix);
|
||||
{
|
||||
@@ -108,40 +110,40 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstVar* nodep) override {
|
||||
void visit(AstVar* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (m_packageScopep) {
|
||||
if (m_ftaskp && m_ftaskp->lifetime().isStatic()) {
|
||||
// Move later, or we wouldn't keep interating the class
|
||||
// We're really moving the VarScope but we might not
|
||||
// have a pointer to it yet
|
||||
m_toScopeMoves.push_back(std::make_pair(nodep, m_packageScopep));
|
||||
m_toScopeMoves.emplace_back(std::make_pair(nodep, m_packageScopep));
|
||||
}
|
||||
if (!m_ftaskp && nodep->lifetime().isStatic()) {
|
||||
m_toPackageMoves.push_back(std::make_pair(nodep, m_classPackagep));
|
||||
m_toPackageMoves.emplace_back(std::make_pair(nodep, m_classPackagep));
|
||||
// We're really moving the VarScope but we might not
|
||||
// have a pointer to it yet
|
||||
m_toScopeMoves.push_back(std::make_pair(nodep, m_packageScopep));
|
||||
m_toScopeMoves.emplace_back(std::make_pair(nodep, m_packageScopep));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstVarScope* nodep) override {
|
||||
void visit(AstVarScope* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
nodep->varp()->user1p(nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeFTask* nodep) override {
|
||||
void visit(AstNodeFTask* nodep) override {
|
||||
VL_RESTORER(m_ftaskp);
|
||||
{
|
||||
m_ftaskp = nodep;
|
||||
iterateChildren(nodep);
|
||||
if (m_packageScopep && nodep->lifetime().isStatic()) {
|
||||
m_toScopeMoves.push_back(std::make_pair(nodep, m_packageScopep));
|
||||
m_toScopeMoves.emplace_back(std::make_pair(nodep, m_packageScopep));
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
void visit(AstCFunc* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
// Don't move now, or wouldn't keep interating the class
|
||||
// TODO move function statics only
|
||||
@@ -149,39 +151,43 @@ private:
|
||||
// m_toScopeMoves.push_back(std::make_pair(nodep, m_classScopep));
|
||||
//}
|
||||
}
|
||||
virtual void visit(AstInitial* nodep) override {
|
||||
void visit(AstInitial* nodep) override {
|
||||
// But not AstInitialAutomatic, which remains under the class
|
||||
iterateChildren(nodep);
|
||||
if (m_packageScopep) { m_toScopeMoves.push_back(std::make_pair(nodep, m_packageScopep)); }
|
||||
if (m_packageScopep) {
|
||||
m_toScopeMoves.emplace_back(std::make_pair(nodep, m_packageScopep));
|
||||
}
|
||||
}
|
||||
virtual void visit(AstInitialStatic* nodep) override {
|
||||
void visit(AstInitialStatic* nodep) override {
|
||||
// But not AstInitialAutomatic, which remains under the class
|
||||
iterateChildren(nodep);
|
||||
if (m_packageScopep) { m_toScopeMoves.push_back(std::make_pair(nodep, m_packageScopep)); }
|
||||
if (m_packageScopep) {
|
||||
m_toScopeMoves.emplace_back(std::make_pair(nodep, m_packageScopep));
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeMath* nodep) override {} // Short circuit
|
||||
virtual void visit(AstNodeStmt* nodep) override {} // Short circuit
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNodeMath* nodep) override {} // Short circuit
|
||||
void visit(AstNodeStmt* nodep) override {} // Short circuit
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit ClassVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~ClassVisitor() override {
|
||||
~ClassVisitor() override {
|
||||
for (auto moved : m_toScopeMoves) {
|
||||
AstNode* const nodep = moved.first;
|
||||
AstScope* const scopep = moved.second;
|
||||
UINFO(9, "moving " << nodep << " to " << scopep << endl);
|
||||
if (VN_IS(nodep, NodeFTask)) {
|
||||
scopep->addActivep(nodep->unlinkFrBack());
|
||||
scopep->addBlocksp(nodep->unlinkFrBack());
|
||||
} else if (VN_IS(nodep, Var)) {
|
||||
AstVarScope* const vscp = VN_AS(nodep->user1p(), VarScope);
|
||||
vscp->scopep(scopep);
|
||||
vscp->unlinkFrBack();
|
||||
scopep->addVarp(vscp);
|
||||
scopep->addVarsp(vscp);
|
||||
} else if (VN_IS(nodep, Initial) || VN_IS(nodep, InitialStatic)) {
|
||||
nodep->unlinkFrBack();
|
||||
scopep->addActivep(nodep);
|
||||
scopep->addBlocksp(nodep);
|
||||
} else {
|
||||
nodep->v3fatalSrc("Bad case");
|
||||
}
|
||||
@@ -191,7 +197,7 @@ public:
|
||||
AstNodeModule* const modp = moved.second;
|
||||
UINFO(9, "moving " << nodep << " to " << modp << endl);
|
||||
nodep->unlinkFrBack();
|
||||
modp->addStmtp(nodep);
|
||||
modp->addStmtsp(nodep);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -202,5 +208,5 @@ public:
|
||||
void V3Class::classAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ ClassVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("class", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("class", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+38
-35
@@ -26,12 +26,15 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Clean.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Clean state, as a visitor of each AstNode
|
||||
|
||||
@@ -53,7 +56,6 @@ private:
|
||||
const AstNodeModule* m_modp = nullptr;
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// Width resetting
|
||||
int cppWidth(AstNode* nodep) {
|
||||
@@ -88,6 +90,7 @@ private:
|
||||
if (VN_IS(nodep, Var)
|
||||
|| VN_IS(nodep, NodeDType) // Don't want to change variable widths!
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), AssocArrayDType) // Or arrays
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), WildcardArrayDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), DynArrayDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), ClassRefDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), QueueDType)
|
||||
@@ -171,134 +174,134 @@ private:
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
{
|
||||
m_modp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeUniop* nodep) override {
|
||||
void visit(AstNodeUniop* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
if (nodep->cleanLhs()) ensureClean(nodep->lhsp());
|
||||
setClean(nodep, nodep->cleanOut());
|
||||
}
|
||||
virtual void visit(AstNodeBiop* nodep) override {
|
||||
void visit(AstNodeBiop* nodep) override {
|
||||
operandBiop(nodep);
|
||||
setClean(nodep, nodep->cleanOut());
|
||||
}
|
||||
virtual void visit(AstAnd* nodep) override {
|
||||
void visit(AstAnd* nodep) override {
|
||||
operandBiop(nodep);
|
||||
setClean(nodep, isClean(nodep->lhsp()) || isClean(nodep->rhsp()));
|
||||
}
|
||||
virtual void visit(AstXor* nodep) override {
|
||||
void visit(AstXor* nodep) override {
|
||||
operandBiop(nodep);
|
||||
setClean(nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp()));
|
||||
}
|
||||
virtual void visit(AstOr* nodep) override {
|
||||
void visit(AstOr* nodep) override {
|
||||
operandBiop(nodep);
|
||||
setClean(nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp()));
|
||||
}
|
||||
virtual void visit(AstNodeQuadop* nodep) override {
|
||||
void visit(AstNodeQuadop* nodep) override {
|
||||
operandQuadop(nodep);
|
||||
setClean(nodep, nodep->cleanOut());
|
||||
}
|
||||
virtual void visit(AstNodeMath* nodep) override {
|
||||
void visit(AstNodeMath* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
setClean(nodep, nodep->cleanOut());
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep) override {
|
||||
void visit(AstNodeAssign* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
if (nodep->cleanRhs()) ensureClean(nodep->rhsp());
|
||||
}
|
||||
virtual void visit(AstText* nodep) override { //
|
||||
void visit(AstText* nodep) override { //
|
||||
setClean(nodep, true);
|
||||
}
|
||||
virtual void visit(AstScopeName* nodep) override { //
|
||||
void visit(AstScopeName* nodep) override { //
|
||||
setClean(nodep, true);
|
||||
}
|
||||
virtual void visit(AstCNew* nodep) override {
|
||||
void visit(AstCNew* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
setClean(nodep, true);
|
||||
}
|
||||
virtual void visit(AstSel* nodep) override {
|
||||
void visit(AstSel* nodep) override {
|
||||
operandTriop(nodep);
|
||||
setClean(nodep, nodep->cleanOut());
|
||||
}
|
||||
virtual void visit(AstUCFunc* nodep) override {
|
||||
void visit(AstUCFunc* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
setClean(nodep, false);
|
||||
// We always clean, as we don't trust those pesky users.
|
||||
if (!VN_IS(nodep->backp(), And)) insertClean(nodep);
|
||||
ensureCleanAndNext(nodep->bodysp());
|
||||
ensureCleanAndNext(nodep->exprsp());
|
||||
}
|
||||
virtual void visit(AstTraceDecl* nodep) override {
|
||||
void visit(AstTraceDecl* nodep) override {
|
||||
// No cleaning, or would loose pointer to enum
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstTraceInc* nodep) override {
|
||||
void visit(AstTraceInc* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureCleanAndNext(nodep->valuep());
|
||||
}
|
||||
virtual void visit(AstTypedef* nodep) override {
|
||||
void visit(AstTypedef* nodep) override {
|
||||
// No cleaning, or would loose pointer to enum
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstParamTypeDType* nodep) override {
|
||||
void visit(AstParamTypeDType* nodep) override {
|
||||
// No cleaning, or would loose pointer to enum
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
// Control flow operators
|
||||
virtual void visit(AstNodeCond* nodep) override {
|
||||
void visit(AstNodeCond* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureClean(nodep->condp());
|
||||
setClean(nodep, isClean(nodep->expr1p()) && isClean(nodep->expr2p()));
|
||||
setClean(nodep, isClean(nodep->thenp()) && isClean(nodep->elsep()));
|
||||
}
|
||||
virtual void visit(AstWhile* nodep) override {
|
||||
void visit(AstWhile* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureClean(nodep->condp());
|
||||
}
|
||||
virtual void visit(AstNodeIf* nodep) override {
|
||||
void visit(AstNodeIf* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureClean(nodep->condp());
|
||||
}
|
||||
virtual void visit(AstSFormatF* nodep) override {
|
||||
void visit(AstSFormatF* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureCleanAndNext(nodep->exprsp());
|
||||
setClean(nodep, true); // generates a string, so not relevant
|
||||
}
|
||||
virtual void visit(AstUCStmt* nodep) override {
|
||||
void visit(AstUCStmt* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureCleanAndNext(nodep->bodysp());
|
||||
ensureCleanAndNext(nodep->exprsp());
|
||||
}
|
||||
virtual void visit(AstNodeCCall* nodep) override {
|
||||
void visit(AstNodeCCall* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureCleanAndNext(nodep->argsp());
|
||||
setClean(nodep, true);
|
||||
}
|
||||
virtual void visit(AstCMethodHard* nodep) override {
|
||||
void visit(AstCMethodHard* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureCleanAndNext(nodep->pinsp());
|
||||
setClean(nodep, true);
|
||||
}
|
||||
virtual void visit(AstWith* nodep) override {
|
||||
void visit(AstWith* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureCleanAndNext(nodep->exprp());
|
||||
setClean(nodep, true);
|
||||
}
|
||||
virtual void visit(AstIntfRef* nodep) override {
|
||||
void visit(AstIntfRef* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
setClean(nodep, true); // generates a string, so not relevant
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
void visit(AstNode* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
}
|
||||
@@ -306,7 +309,7 @@ private:
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CleanVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~CleanVisitor() override = default;
|
||||
~CleanVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -315,5 +318,5 @@ public:
|
||||
void V3Clean::cleanAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CleanVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("clean", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("clean", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+25
-23
@@ -30,12 +30,15 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Clock.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Convert every WRITE AstVarRef to a READ ref
|
||||
|
||||
@@ -87,7 +90,6 @@ private:
|
||||
AstMTaskBody* m_mtaskBodyp = nullptr; // Current mtask body
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
AstVarScope* getCreateLastClk(AstVarScope* vscp) {
|
||||
if (vscp->user1p()) return static_cast<AstVarScope*>(vscp->user1p());
|
||||
@@ -99,12 +101,12 @@ private:
|
||||
const string newvarname
|
||||
= (string("__Vclklast__") + vscp->scopep()->nameDotless() + "__" + varp->name());
|
||||
AstVar* const newvarp = new AstVar(vscp->fileline(), VVarType::MODULETEMP, newvarname,
|
||||
VFlagLogicPacked(), 1);
|
||||
VFlagLogicPacked{}, 1);
|
||||
newvarp->noReset(true); // Reset by below assign
|
||||
m_modp->addStmtp(newvarp);
|
||||
m_modp->addStmtsp(newvarp);
|
||||
AstVarScope* const newvscp = new AstVarScope(vscp->fileline(), m_scopep, newvarp);
|
||||
vscp->user1p(newvscp);
|
||||
m_scopep->addVarp(newvscp);
|
||||
m_scopep->addVarsp(newvscp);
|
||||
// Add init
|
||||
AstNode* fromp = new AstVarRef(newvarp->fileline(), vscp, VAccess::READ);
|
||||
if (v3Global.opt.xInitialEdge()) fromp = new AstNot(fromp->fileline(), fromp);
|
||||
@@ -201,7 +203,7 @@ private:
|
||||
funcp->slow(slow);
|
||||
funcp->isConst(false);
|
||||
funcp->declPrivate(true);
|
||||
m_topScopep->scopep()->addActivep(funcp);
|
||||
m_topScopep->scopep()->addBlocksp(funcp);
|
||||
return funcp;
|
||||
}
|
||||
void splitCheck(AstCFunc* ofuncp) {
|
||||
@@ -228,7 +230,7 @@ private:
|
||||
funcp->isStatic(false);
|
||||
funcp->isLoose(true);
|
||||
funcp->slow(ofuncp->slow());
|
||||
m_topScopep->scopep()->addActivep(funcp);
|
||||
m_topScopep->scopep()->addBlocksp(funcp);
|
||||
//
|
||||
AstCCall* const callp = new AstCCall{funcp->fileline(), funcp};
|
||||
ofuncp->addStmtsp(callp);
|
||||
@@ -241,7 +243,7 @@ private:
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstTopScope* nodep) override {
|
||||
void visit(AstTopScope* nodep) override {
|
||||
UINFO(4, " TOPSCOPE " << nodep << endl);
|
||||
m_topScopep = nodep;
|
||||
m_scopep = nodep->scopep();
|
||||
@@ -276,7 +278,7 @@ private:
|
||||
m_topScopep = nullptr;
|
||||
m_scopep = nullptr;
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
// UINFO(4, " MOD " << nodep << endl);
|
||||
VL_RESTORER(m_modp);
|
||||
{
|
||||
@@ -284,7 +286,7 @@ private:
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstScope* nodep) override {
|
||||
void visit(AstScope* nodep) override {
|
||||
// UINFO(4, " SCOPE " << nodep << endl);
|
||||
m_scopep = nodep;
|
||||
iterateChildren(nodep);
|
||||
@@ -295,14 +297,14 @@ private:
|
||||
}
|
||||
m_scopep = nullptr;
|
||||
}
|
||||
virtual void visit(AstNodeProcedure* nodep) override {
|
||||
if (AstNode* const stmtsp = nodep->bodysp()) {
|
||||
void visit(AstNodeProcedure* nodep) override {
|
||||
if (AstNode* const stmtsp = nodep->stmtsp()) {
|
||||
stmtsp->unlinkFrBackWithNext();
|
||||
nodep->addNextHere(stmtsp);
|
||||
}
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
}
|
||||
virtual void visit(AstCoverToggle* nodep) override {
|
||||
void visit(AstCoverToggle* nodep) override {
|
||||
// nodep->dumpTree(cout, "ct:");
|
||||
// COVERTOGGLE(INC, ORIG, CHANGE) ->
|
||||
// IF(ORIG ^ CHANGE) { INC; CHANGE = ORIG; }
|
||||
@@ -315,11 +317,11 @@ private:
|
||||
// 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.
|
||||
newp->addIfsp(new AstAssign(nodep->fileline(), changeWrp, origp->cloneTree(false)));
|
||||
newp->addThensp(new AstAssign(nodep->fileline(), changeWrp, origp->cloneTree(false)));
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
void visit(AstCFunc* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
// Link to global function
|
||||
if (nodep->isFinal()) {
|
||||
@@ -328,7 +330,7 @@ private:
|
||||
m_finalFuncp->addStmtsp(callp);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstSenTree* nodep) override {
|
||||
void visit(AstSenTree* nodep) override {
|
||||
// Delete it later; Actives still pointing to it
|
||||
nodep->unlinkFrBack();
|
||||
pushDeletep(nodep);
|
||||
@@ -342,7 +344,7 @@ private:
|
||||
void addToInitial(AstNode* stmtsp) {
|
||||
m_initFuncp->addStmtsp(stmtsp); // add to top level function
|
||||
}
|
||||
virtual void visit(AstActive* nodep) override {
|
||||
void visit(AstActive* nodep) override {
|
||||
// Careful if adding variables here, ACTIVES can be under other ACTIVES
|
||||
// Need to save and restore any member state in AstUntilStable block
|
||||
if (!m_topScopep || !nodep->stmtsp()) {
|
||||
@@ -366,7 +368,7 @@ private:
|
||||
m_mtaskBodyp->addStmtsp(m_lastIfp);
|
||||
}
|
||||
// Move statements to if
|
||||
m_lastIfp->addIfsp(stmtsp);
|
||||
m_lastIfp->addThensp(stmtsp);
|
||||
} else if (nodep->hasInitial() || nodep->hasSettle()) {
|
||||
nodep->v3fatalSrc("MTask should not include initial/settle logic.");
|
||||
} else {
|
||||
@@ -392,7 +394,7 @@ private:
|
||||
addToEvalLoop(m_lastIfp);
|
||||
}
|
||||
// Move statements to if
|
||||
m_lastIfp->addIfsp(stmtsp);
|
||||
m_lastIfp->addThensp(stmtsp);
|
||||
} else if (nodep->hasInitial()) {
|
||||
// Don't need to: clearLastSen();, as we're adding it to different cfunc
|
||||
// Move statements to function
|
||||
@@ -410,7 +412,7 @@ private:
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstExecGraph* nodep) override {
|
||||
void visit(AstExecGraph* nodep) override {
|
||||
VL_RESTORER(m_mtaskBodyp);
|
||||
for (m_mtaskBodyp = nodep->mTaskBodiesp(); m_mtaskBodyp;
|
||||
m_mtaskBodyp = VN_AS(m_mtaskBodyp->nextp(), MTaskBody)) {
|
||||
@@ -426,7 +428,7 @@ private:
|
||||
}
|
||||
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -436,7 +438,7 @@ public:
|
||||
// easily without iterating through the tree.
|
||||
nodep->evalp(m_evalFuncp);
|
||||
}
|
||||
virtual ~ClockVisitor() override = default;
|
||||
~ClockVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -445,5 +447,5 @@ public:
|
||||
void V3Clock::clockAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ ClockVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("clock", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("clock", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+16
-12
@@ -22,16 +22,19 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Combine.h"
|
||||
#include "V3DupFinder.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3AstUserAllocator.h"
|
||||
#include "V3DupFinder.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
class CombineVisitor final : VNVisitor {
|
||||
// NODE STATE
|
||||
// AstNodeModule::user1() List of AstCFuncs in this module (via m_cfuncs)
|
||||
@@ -56,7 +59,6 @@ class CombineVisitor final : VNVisitor {
|
||||
VDouble0 m_cfuncsCombined; // Statistic tracking
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void removeEmptyFunctions(std::list<AstCFunc*>& funcps) {
|
||||
for (funcit_t it = funcps.begin(), nit; it != funcps.end(); it = nit) {
|
||||
@@ -179,32 +181,32 @@ class CombineVisitor final : VNVisitor {
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNetlist* nodep) override {
|
||||
void visit(AstNetlist* nodep) override {
|
||||
// Gather functions and references
|
||||
iterateChildrenConst(nodep);
|
||||
// Combine functions
|
||||
process(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
UASSERT_OBJ(!m_modp, nodep, "Should not nest");
|
||||
m_modp = nodep;
|
||||
iterateChildrenConst(nodep);
|
||||
m_modp = nullptr;
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
void visit(AstCFunc* nodep) override {
|
||||
iterateChildrenConst(nodep);
|
||||
if (nodep->dontCombine()) return;
|
||||
auto& coll = nodep->slow() ? m_cfuncs(m_modp).m_slow : m_cfuncs(m_modp).m_fast;
|
||||
coll.emplace_back(nodep);
|
||||
}
|
||||
virtual void visit(AstCCall* nodep) override {
|
||||
void visit(AstCCall* nodep) override {
|
||||
iterateChildrenConst(nodep);
|
||||
AstCFunc* const funcp = nodep->funcp();
|
||||
if (funcp->dontCombine()) return;
|
||||
m_callSites(funcp).emplace_back(nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstAddrOfCFunc* nodep) override {
|
||||
void visit(AstAddrOfCFunc* nodep) override {
|
||||
iterateChildrenConst(nodep);
|
||||
if (nodep->funcp()->dontCombine()) return;
|
||||
// LCOV_EXCL_START
|
||||
@@ -216,11 +218,13 @@ class CombineVisitor final : VNVisitor {
|
||||
}
|
||||
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
||||
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
||||
|
||||
// CONSTRUCTORS
|
||||
explicit CombineVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
~CombineVisitor() { V3Stats::addStat("Optimizations, Combined CFuncs", m_cfuncsCombined); }
|
||||
~CombineVisitor() override {
|
||||
V3Stats::addStat("Optimizations, Combined CFuncs", m_cfuncsCombined);
|
||||
}
|
||||
|
||||
public:
|
||||
static void apply(AstNetlist* netlistp) { CombineVisitor{netlistp}; }
|
||||
@@ -232,5 +236,5 @@ public:
|
||||
void V3Combine::combineAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
CombineVisitor::apply(nodep);
|
||||
V3Global::dumpCheckGlobalTree("combine", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("combine", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+16
-13
@@ -24,9 +24,12 @@
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Common.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Common component builders
|
||||
@@ -42,7 +45,7 @@ static void makeVlToString(AstClass* nodep) {
|
||||
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);
|
||||
nodep->addStmtsp(funcp);
|
||||
}
|
||||
static void makeToString(AstClass* nodep) {
|
||||
AstCFunc* const funcp = new AstCFunc{nodep->fileline(), "to_string", nullptr, "std::string"};
|
||||
@@ -50,10 +53,10 @@ static void makeToString(AstClass* nodep) {
|
||||
funcp->isStatic(false);
|
||||
funcp->protect(false);
|
||||
AstNode* const exprp
|
||||
= new AstCMath{nodep->fileline(), R"(std::string("'{") + to_string_middle() + "}")", 0};
|
||||
= new AstCMath{nodep->fileline(), R"(std::string{"'{"} + to_string_middle() + "}")", 0};
|
||||
exprp->dtypeSetString();
|
||||
funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp});
|
||||
nodep->addStmtp(funcp);
|
||||
nodep->addStmtsp(funcp);
|
||||
}
|
||||
static void makeToStringMiddle(AstClass* nodep) {
|
||||
AstCFunc* const funcp
|
||||
@@ -85,17 +88,17 @@ static void makeToStringMiddle(AstClass* nodep) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nodep->extendsp() && nodep->extendsp()->classp()->user1()) {
|
||||
string stmt = "out += \"";
|
||||
if (nodep->extendsp()) {
|
||||
string stmt = "out += ";
|
||||
if (!comma.empty()) stmt += "\", \"+ ";
|
||||
// comma = ", "; // Nothing further so not needed
|
||||
stmt += nodep->extendsp()->dtypep()->nameProtect();
|
||||
stmt += EmitCBaseVisitor::prefixNameProtect(nodep->extendsp()->dtypep());
|
||||
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);
|
||||
nodep->addStmtsp(funcp);
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
@@ -103,18 +106,18 @@ static void makeToStringMiddle(AstClass* nodep) {
|
||||
|
||||
void V3Common::commonAll() {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstClass::user1() -> bool. True if class needs to_string dumper
|
||||
const VNUser1InUse m_inuser1;
|
||||
// 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 VNUser1InUse m_inuser1;
|
||||
// Create ToString methods
|
||||
makeVlToString(classp);
|
||||
makeToString(classp);
|
||||
makeToStringMiddle(classp);
|
||||
}
|
||||
}
|
||||
V3Global::dumpCheckGlobalTree("common", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("common", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+9
-5
@@ -17,15 +17,18 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Config.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3String.h"
|
||||
#include "V3Config.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Resolve wildcards in files, modules, ftasks or variables
|
||||
|
||||
@@ -33,7 +36,8 @@
|
||||
// as wildcards and are accessed by a resolved name. It rebuilds a name lookup
|
||||
// cache of resolved entities. Entities stored in this container need an update
|
||||
// function that takes a reference of this type to join multiple entities into one.
|
||||
template <typename T> class V3ConfigWildcardResolver {
|
||||
template <typename T>
|
||||
class V3ConfigWildcardResolver final {
|
||||
using Map = std::map<const std::string, T>;
|
||||
|
||||
Map m_mapWildcard; // Wildcard strings to entities
|
||||
@@ -191,11 +195,11 @@ public:
|
||||
const VPragmaType type
|
||||
= m_inlineValue ? VPragmaType::INLINE_MODULE : VPragmaType::NO_INLINE_MODULE;
|
||||
AstNode* const nodep = new AstPragma(modp->fileline(), type);
|
||||
modp->addStmtp(nodep);
|
||||
modp->addStmtsp(nodep);
|
||||
}
|
||||
for (const auto& itr : m_modPragmas) {
|
||||
AstNode* const nodep = new AstPragma{modp->fileline(), itr};
|
||||
modp->addStmtp(nodep);
|
||||
modp->addStmtsp(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,7 +311,7 @@ public:
|
||||
if (lineMatch(lineno, VPragmaType::FULL_CASE)) nodep->fullPragma(true);
|
||||
if (lineMatch(lineno, VPragmaType::PARALLEL_CASE)) nodep->parallelPragma(true);
|
||||
}
|
||||
inline void applyIgnores(FileLine* filelinep) {
|
||||
void applyIgnores(FileLine* filelinep) {
|
||||
// HOT routine, called each parsed token line of this filename
|
||||
if (m_lastIgnore.lineno != filelinep->lineno()) {
|
||||
// UINFO(9, " ApplyIgnores for " << filelinep->ascii() << endl);
|
||||
|
||||
+1
-1
@@ -20,9 +20,9 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Error.h"
|
||||
#include "V3FileLine.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
//######################################################################
|
||||
|
||||
|
||||
+241
-174
@@ -23,19 +23,22 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3String.h"
|
||||
#include "V3Const.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Width.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Simulate.h"
|
||||
#include "V3Stats.h"
|
||||
#include "V3String.h"
|
||||
#include "V3UniqueNames.h"
|
||||
#include "V3Width.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Utilities
|
||||
|
||||
@@ -79,14 +82,50 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
|
||||
// bool indicating if the term is clean (0/1 value, or if the top bits might be dirty)
|
||||
using ResultTerm = std::tuple<AstNode*, unsigned, bool>;
|
||||
|
||||
struct LeafInfo final { // Leaf node (either AstConst or AstVarRef)
|
||||
class LeafInfo final { // Leaf node (either AstConst or AstVarRef)
|
||||
// MEMBERS
|
||||
bool m_polarity = true;
|
||||
int m_lsb = 0;
|
||||
int m_lsb = 0; // LSB of actually used bit of m_refp->varp()
|
||||
int m_msb = 0; // MSB of actually used bit of m_refp->varp()
|
||||
int m_wordIdx = -1; // -1 means AstWordSel is not used.
|
||||
AstVarRef* m_refp = nullptr;
|
||||
const AstConst* m_constp = nullptr;
|
||||
|
||||
int width() const {
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
LeafInfo() = default;
|
||||
LeafInfo(const LeafInfo& other) = default;
|
||||
explicit LeafInfo(int lsb)
|
||||
: m_lsb{lsb} {}
|
||||
|
||||
// METHODS
|
||||
void setLeaf(AstVarRef* refp) {
|
||||
UASSERT(!m_refp && !m_constp, "Must be called just once");
|
||||
m_refp = refp;
|
||||
m_msb = refp->varp()->widthMin() - 1;
|
||||
}
|
||||
void setLeaf(const AstConst* constp) {
|
||||
UASSERT(!m_refp && !m_constp, "Must be called just once");
|
||||
m_constp = constp;
|
||||
m_msb = constp->widthMin() - 1;
|
||||
}
|
||||
void updateBitRange(const AstCCast* castp) {
|
||||
m_msb = std::min(m_msb, m_lsb + castp->width() - 1);
|
||||
}
|
||||
void updateBitRange(const AstShiftR* shiftp) {
|
||||
m_lsb += VN_AS(shiftp->rhsp(), Const)->toUInt();
|
||||
}
|
||||
void wordIdx(int i) { m_wordIdx = i; }
|
||||
void polarity(bool p) { m_polarity = p; }
|
||||
|
||||
AstVarRef* refp() const { return m_refp; }
|
||||
const AstConst* constp() const { return m_constp; }
|
||||
int wordIdx() const { return m_wordIdx; }
|
||||
bool polarity() const { return m_polarity; }
|
||||
int lsb() const { return m_lsb; }
|
||||
|
||||
int msb() const { return std::min(m_msb, varWidth() - 1); }
|
||||
int varWidth() const {
|
||||
UASSERT(m_refp, "m_refp should be set");
|
||||
const int width = m_refp->varp()->widthMin();
|
||||
if (!m_refp->isWide()) {
|
||||
@@ -289,7 +328,6 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
|
||||
return ResultTerm{resultp, ops, clean};
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VarInfo(ConstBitOpTreeVisitor* parent, AstVarRef* refp, int width)
|
||||
: m_parentp{parent}
|
||||
@@ -314,7 +352,6 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
|
||||
std::vector<std::unique_ptr<VarInfo>> m_varInfos; // VarInfo for each variable, [0] is nullptr
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
bool isAndTree() const { return VN_IS(m_rootp, And); }
|
||||
bool isOrTree() const { return VN_IS(m_rootp, Or); }
|
||||
@@ -339,32 +376,32 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
|
||||
UINFO(9, "Increment to " << m_ops << " " << nodep << " called from line " << line << "\n");
|
||||
}
|
||||
VarInfo& getVarInfo(const LeafInfo& ref) {
|
||||
UASSERT_OBJ(ref.m_refp, m_rootp, "null varref in And/Or/Xor optimization");
|
||||
AstNode* nodep = ref.m_refp->varScopep();
|
||||
if (!nodep) nodep = ref.m_refp->varp(); // Not scoped
|
||||
UASSERT_OBJ(ref.refp(), m_rootp, "null varref in And/Or/Xor optimization");
|
||||
AstNode* nodep = ref.refp()->varScopep();
|
||||
if (!nodep) nodep = ref.refp()->varp(); // Not scoped
|
||||
int baseIdx = nodep->user4();
|
||||
if (baseIdx == 0) { // Not set yet
|
||||
baseIdx = m_varInfos.size();
|
||||
const int numWords
|
||||
= ref.m_refp->dtypep()->isWide() ? ref.m_refp->dtypep()->widthWords() : 1;
|
||||
= ref.refp()->dtypep()->isWide() ? ref.refp()->dtypep()->widthWords() : 1;
|
||||
m_varInfos.resize(m_varInfos.size() + numWords);
|
||||
nodep->user4(baseIdx);
|
||||
}
|
||||
const size_t idx = baseIdx + std::max(0, ref.m_wordIdx);
|
||||
const size_t idx = baseIdx + std::max(0, ref.wordIdx());
|
||||
VarInfo* varInfop = m_varInfos[idx].get();
|
||||
if (!varInfop) {
|
||||
varInfop = new VarInfo{this, ref.m_refp, ref.width()};
|
||||
varInfop = new VarInfo{this, ref.refp(), ref.varWidth()};
|
||||
m_varInfos[idx].reset(varInfop);
|
||||
} else {
|
||||
if (!varInfop->sameVarAs(ref.m_refp))
|
||||
CONST_BITOP_SET_FAILED("different var (scope?)", ref.m_refp);
|
||||
if (!varInfop->sameVarAs(ref.refp()))
|
||||
CONST_BITOP_SET_FAILED("different var (scope?)", ref.refp());
|
||||
}
|
||||
return *varInfop;
|
||||
}
|
||||
|
||||
// Traverse down to see AstConst or AstVarRef
|
||||
LeafInfo findLeaf(AstNode* nodep, bool expectConst) {
|
||||
LeafInfo info;
|
||||
LeafInfo info{m_lsb};
|
||||
{
|
||||
VL_RESTORER(m_leafp);
|
||||
m_leafp = &info;
|
||||
@@ -373,31 +410,34 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
|
||||
|
||||
bool ok = !m_failed;
|
||||
if (expectConst) {
|
||||
ok &= !info.m_refp && info.m_constp;
|
||||
ok &= !info.refp() && info.constp();
|
||||
} else {
|
||||
ok &= info.m_refp && !info.m_constp;
|
||||
ok &= info.refp() && !info.constp();
|
||||
}
|
||||
return ok ? info : LeafInfo{};
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
CONST_BITOP_SET_FAILED("Hit unexpected op", nodep);
|
||||
void visit(AstNode* nodep) override { CONST_BITOP_SET_FAILED("Hit unexpected op", nodep); }
|
||||
void visit(AstCCast* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (m_leafp) m_leafp->updateBitRange(nodep);
|
||||
}
|
||||
virtual void visit(AstCCast* nodep) override { iterateChildren(nodep); }
|
||||
virtual void visit(AstShiftR* nodep) override {
|
||||
void visit(AstShiftR* nodep) override {
|
||||
CONST_BITOP_RETURN_IF(!m_leafp, nodep);
|
||||
AstConst* const constp = VN_CAST(nodep->rhsp(), Const);
|
||||
CONST_BITOP_RETURN_IF(!constp, nodep->rhsp());
|
||||
m_lsb += constp->toUInt();
|
||||
incrOps(nodep, __LINE__);
|
||||
iterate(nodep->lhsp());
|
||||
m_leafp->updateBitRange(nodep);
|
||||
m_lsb -= constp->toUInt();
|
||||
}
|
||||
virtual void visit(AstNot* nodep) override {
|
||||
void visit(AstNot* nodep) override {
|
||||
CONST_BITOP_RETURN_IF(nodep->widthMin() != 1, nodep);
|
||||
AstNode* lhsp = nodep->lhsp();
|
||||
if (AstCCast* const castp = VN_CAST(lhsp, CCast)) lhsp = castp->lhsp();
|
||||
AstCCast* const castp = VN_CAST(lhsp, CCast);
|
||||
if (castp) lhsp = castp->lhsp();
|
||||
CONST_BITOP_RETURN_IF(!VN_IS(lhsp, VarRef) && !VN_IS(lhsp, Xor) && !VN_IS(lhsp, RedXor)
|
||||
&& !VN_IS(lhsp, ShiftR),
|
||||
lhsp);
|
||||
@@ -406,74 +446,74 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
|
||||
iterateChildren(nodep);
|
||||
// Don't restore m_polarity for Xor as it counts parity of the entire tree
|
||||
if (!isXorTree()) m_polarity = !m_polarity;
|
||||
if (m_leafp && castp) m_leafp->updateBitRange(castp);
|
||||
}
|
||||
virtual void visit(AstWordSel* nodep) override {
|
||||
void visit(AstWordSel* nodep) override {
|
||||
CONST_BITOP_RETURN_IF(!m_leafp, nodep);
|
||||
AstConst* const constp = VN_CAST(nodep->bitp(), Const);
|
||||
CONST_BITOP_RETURN_IF(!constp, nodep->rhsp());
|
||||
UASSERT_OBJ(m_leafp->m_wordIdx == -1, nodep, "Unexpected nested WordSel");
|
||||
m_leafp->m_wordIdx = constp->toSInt();
|
||||
UASSERT_OBJ(m_leafp->wordIdx() == -1, nodep, "Unexpected nested WordSel");
|
||||
m_leafp->wordIdx(constp->toSInt());
|
||||
iterate(nodep->fromp());
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep) override {
|
||||
void visit(AstVarRef* nodep) override {
|
||||
CONST_BITOP_RETURN_IF(!m_leafp, nodep);
|
||||
UASSERT_OBJ(!m_leafp->m_refp, nodep, m_leafp->m_refp << " is already set");
|
||||
m_leafp->m_refp = nodep;
|
||||
m_leafp->m_polarity = m_polarity;
|
||||
m_leafp->m_lsb = m_lsb;
|
||||
m_leafp->setLeaf(nodep);
|
||||
m_leafp->polarity(m_polarity);
|
||||
}
|
||||
virtual void visit(AstConst* nodep) override {
|
||||
void visit(AstConst* nodep) override {
|
||||
CONST_BITOP_RETURN_IF(!m_leafp, nodep);
|
||||
UASSERT_OBJ(!m_leafp->m_constp, nodep, m_leafp->m_constp << " is already set");
|
||||
m_leafp->m_constp = nodep;
|
||||
m_leafp->m_lsb = m_lsb;
|
||||
m_leafp->setLeaf(nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstRedXor* nodep) override {
|
||||
void visit(AstRedXor* nodep) override {
|
||||
Restorer restorer{*this};
|
||||
CONST_BITOP_RETURN_IF(!VN_IS(m_rootp, Xor), nodep);
|
||||
AstNode* lhsp = nodep->lhsp();
|
||||
if (const AstCCast* const castp = VN_CAST(lhsp, CCast)) lhsp = castp->lhsp();
|
||||
const AstCCast* const castp = VN_CAST(lhsp, CCast);
|
||||
if (castp) lhsp = castp->lhsp();
|
||||
if (const AstAnd* const andp = VN_CAST(lhsp, And)) { // '^(mask & leaf)'
|
||||
CONST_BITOP_RETURN_IF(!andp, lhsp);
|
||||
|
||||
const LeafInfo& mask = findLeaf(andp->lhsp(), true);
|
||||
CONST_BITOP_RETURN_IF(!mask.m_constp || mask.m_lsb != 0, andp->lhsp());
|
||||
CONST_BITOP_RETURN_IF(!mask.constp() || mask.lsb() != 0, andp->lhsp());
|
||||
|
||||
const LeafInfo& ref = findLeaf(andp->rhsp(), false);
|
||||
CONST_BITOP_RETURN_IF(!ref.m_refp, andp->rhsp());
|
||||
LeafInfo ref = findLeaf(andp->rhsp(), false);
|
||||
CONST_BITOP_RETURN_IF(!ref.refp(), andp->rhsp());
|
||||
if (castp) ref.updateBitRange(castp);
|
||||
|
||||
restorer.disableRestore(); // Now all subtree succeeded
|
||||
|
||||
const V3Number& maskNum = mask.m_constp->num();
|
||||
const V3Number& maskNum = mask.constp()->num();
|
||||
|
||||
incrOps(nodep, __LINE__);
|
||||
incrOps(andp, __LINE__);
|
||||
|
||||
// Mark all bits checked in this reduction
|
||||
const int maxBitIdx = std::min(ref.m_lsb + maskNum.width(), ref.width());
|
||||
for (int bitIdx = ref.m_lsb; bitIdx < maxBitIdx; ++bitIdx) {
|
||||
const int maskIdx = bitIdx - ref.m_lsb;
|
||||
const int maxBitIdx = std::min(ref.lsb() + maskNum.width(), ref.msb() + 1);
|
||||
for (int bitIdx = ref.lsb(); bitIdx < maxBitIdx; ++bitIdx) {
|
||||
const int maskIdx = bitIdx - ref.lsb();
|
||||
if (maskNum.bitIs0(maskIdx)) continue;
|
||||
// Set true, m_polarity takes care of the entire parity
|
||||
m_bitPolarities.emplace_back(ref, true, bitIdx);
|
||||
}
|
||||
} else { // '^leaf'
|
||||
const LeafInfo& ref = findLeaf(lhsp, false);
|
||||
CONST_BITOP_RETURN_IF(!ref.m_refp, lhsp);
|
||||
LeafInfo ref = findLeaf(lhsp, false);
|
||||
CONST_BITOP_RETURN_IF(!ref.refp(), lhsp);
|
||||
if (castp) ref.updateBitRange(castp);
|
||||
|
||||
restorer.disableRestore(); // Now all checks passed
|
||||
|
||||
incrOps(nodep, __LINE__);
|
||||
|
||||
// Mark all bits checked by this comparison
|
||||
for (int bitIdx = ref.m_lsb; bitIdx < ref.width(); ++bitIdx) {
|
||||
for (int bitIdx = ref.lsb(); bitIdx <= ref.msb(); ++bitIdx) {
|
||||
m_bitPolarities.emplace_back(ref, true, bitIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeBiop* nodep) override {
|
||||
void visit(AstNodeBiop* nodep) override {
|
||||
if (VN_IS(nodep, And) && isConst(nodep->lhsp(), 1)) { // 1 & _
|
||||
// Always reach past a plain making AND
|
||||
Restorer restorer{*this};
|
||||
@@ -487,12 +527,12 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
|
||||
|
||||
for (const bool right : {false, true}) {
|
||||
Restorer restorer{*this};
|
||||
LeafInfo leafInfo;
|
||||
LeafInfo leafInfo{m_lsb};
|
||||
m_leafp = &leafInfo;
|
||||
AstNode* opp = right ? nodep->rhsp() : nodep->lhsp();
|
||||
const bool origFailed = m_failed;
|
||||
iterate(opp);
|
||||
if (leafInfo.m_constp || m_failed) {
|
||||
if (leafInfo.constp() || m_failed) {
|
||||
// Revert changes in leaf
|
||||
restorer.restoreNow();
|
||||
// Reach past a cast then add to frozen nodes to be added to final reduction
|
||||
@@ -502,14 +542,14 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
|
||||
continue;
|
||||
}
|
||||
restorer.disableRestore(); // Now all checks passed
|
||||
if (leafInfo.m_refp) {
|
||||
if (leafInfo.refp()) {
|
||||
// The conditional on the lsb being in range is necessary for some degenerate
|
||||
// case, e.g.: (IData)((QData)wide[0] >> 32), or <1-bit-var> >> 1, which is
|
||||
// just zero
|
||||
if (leafInfo.m_lsb < leafInfo.width()) {
|
||||
m_bitPolarities.emplace_back(leafInfo, isXorTree() || leafInfo.m_polarity,
|
||||
leafInfo.m_lsb);
|
||||
} else if (isAndTree() && leafInfo.m_polarity) {
|
||||
if (leafInfo.lsb() <= leafInfo.msb()) {
|
||||
m_bitPolarities.emplace_back(leafInfo, isXorTree() || leafInfo.polarity(),
|
||||
leafInfo.lsb());
|
||||
} else if (isAndTree() && leafInfo.polarity()) {
|
||||
// If there is a constant 0 term in an And tree, we must include it. Fudge
|
||||
// this by adding a bit with both polarities, which will simplify to zero
|
||||
m_bitPolarities.emplace_back(leafInfo, true, 0);
|
||||
@@ -525,46 +565,59 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
|
||||
const AstConst* const constp = VN_CAST(lhsp, Const);
|
||||
CONST_BITOP_RETURN_IF(!constp, nodep->lhsp());
|
||||
|
||||
const bool maskFlip = isOrTree();
|
||||
const V3Number& compNum = constp->num();
|
||||
|
||||
auto setPolarities = [this, &compNum](const LeafInfo& ref, const V3Number* maskp) {
|
||||
const bool maskFlip = isOrTree();
|
||||
int constantWidth = compNum.width();
|
||||
if (maskp) constantWidth = std::max(constantWidth, maskp->width());
|
||||
const int maxBitIdx = std::max(ref.lsb() + constantWidth, ref.msb() + 1);
|
||||
// Mark all bits checked by this comparison
|
||||
for (int bitIdx = ref.lsb(); bitIdx < maxBitIdx; ++bitIdx) {
|
||||
const int maskIdx = bitIdx - ref.lsb();
|
||||
const bool mask0 = maskp && maskp->bitIs0(maskIdx);
|
||||
const bool outOfRange = bitIdx > ref.msb();
|
||||
if (mask0 || outOfRange) { // RHS is 0
|
||||
if (compNum.bitIs1(maskIdx)) {
|
||||
// LHS is 1
|
||||
// And tree: 1 == 0 => always false, set v && !v
|
||||
// Or tree : 1 != 0 => always true, set v || !v
|
||||
m_bitPolarities.emplace_back(ref, true, 0);
|
||||
m_bitPolarities.emplace_back(ref, false, 0);
|
||||
break;
|
||||
} else { // This bitIdx is irrelevant
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const bool polarity = compNum.bitIs1(maskIdx) != maskFlip;
|
||||
m_bitPolarities.emplace_back(ref, polarity, bitIdx);
|
||||
}
|
||||
};
|
||||
|
||||
if (const AstAnd* const andp = VN_CAST(nodep->rhsp(), And)) { // comp == (mask & v)
|
||||
const LeafInfo& mask = findLeaf(andp->lhsp(), true);
|
||||
CONST_BITOP_RETURN_IF(!mask.m_constp || mask.m_lsb != 0, andp->lhsp());
|
||||
CONST_BITOP_RETURN_IF(!mask.constp() || mask.lsb() != 0, andp->lhsp());
|
||||
|
||||
const LeafInfo& ref = findLeaf(andp->rhsp(), false);
|
||||
CONST_BITOP_RETURN_IF(!ref.m_refp, andp->rhsp());
|
||||
CONST_BITOP_RETURN_IF(!ref.refp(), andp->rhsp());
|
||||
|
||||
restorer.disableRestore(); // Now all checks passed
|
||||
|
||||
const V3Number& maskNum = mask.m_constp->num();
|
||||
const V3Number& maskNum = mask.constp()->num();
|
||||
|
||||
incrOps(nodep, __LINE__);
|
||||
incrOps(andp, __LINE__);
|
||||
|
||||
// Mark all bits checked by this comparison
|
||||
const int maxBitIdx = std::min(ref.m_lsb + maskNum.width(), ref.width());
|
||||
for (int bitIdx = ref.m_lsb; bitIdx < maxBitIdx; ++bitIdx) {
|
||||
const int maskIdx = bitIdx - ref.m_lsb;
|
||||
if (maskNum.bitIs0(maskIdx)) continue;
|
||||
const bool polarity = compNum.bitIs1(maskIdx) != maskFlip;
|
||||
m_bitPolarities.emplace_back(ref, polarity, bitIdx);
|
||||
}
|
||||
setPolarities(ref, &maskNum);
|
||||
} else { // comp == v
|
||||
const LeafInfo& ref = findLeaf(nodep->rhsp(), false);
|
||||
CONST_BITOP_RETURN_IF(!ref.m_refp, nodep->rhsp());
|
||||
CONST_BITOP_RETURN_IF(!ref.refp(), nodep->rhsp());
|
||||
|
||||
restorer.disableRestore(); // Now all checks passed
|
||||
|
||||
incrOps(nodep, __LINE__);
|
||||
|
||||
// Mark all bits checked by this comparison
|
||||
const int maxBitIdx = std::min(ref.m_lsb + compNum.width(), ref.width());
|
||||
for (int bitIdx = ref.m_lsb; bitIdx < maxBitIdx; ++bitIdx) {
|
||||
const int maskIdx = bitIdx - ref.m_lsb;
|
||||
const bool polarity = compNum.bitIs1(maskIdx) != maskFlip;
|
||||
m_bitPolarities.emplace_back(ref, polarity, bitIdx);
|
||||
}
|
||||
setPolarities(ref, nullptr);
|
||||
}
|
||||
} else {
|
||||
CONST_BITOP_SET_FAILED("Mixture of different ops cannot be optimized", nodep);
|
||||
@@ -700,10 +753,11 @@ public:
|
||||
if (debug() >= 9) { // LCOV_EXCL_START
|
||||
cout << "Bitop tree considered: " << endl;
|
||||
for (AstNode* const termp : termps) termp->dumpTree("Reduced term: ");
|
||||
for (const std::pair<AstNode*, FrozenNodeInfo>& termp : visitor.m_frozenNodes)
|
||||
for (const std::pair<AstNode*, FrozenNodeInfo>& termp : visitor.m_frozenNodes) {
|
||||
termp.first->dumpTree("Frozen term with lsb " + std::to_string(termp.second.m_lsb)
|
||||
+ " polarity " + std::to_string(termp.second.m_polarity)
|
||||
+ ": ");
|
||||
}
|
||||
cout << "Needs flipping: " << needsFlip << endl;
|
||||
cout << "Needs cleaning: " << needsCleaning << endl;
|
||||
cout << "Size: " << resultOps << " input size: " << visitor.m_ops << endl;
|
||||
@@ -840,7 +894,6 @@ private:
|
||||
V3UniqueNames m_concswapNames; // For generating unique temporary variable names
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
bool operandConst(AstNode* nodep) { return VN_IS(nodep, Const); }
|
||||
bool operandAsvConst(const AstNode* nodep) {
|
||||
@@ -989,20 +1042,19 @@ private:
|
||||
// as high as possible, which is usally the right choice, except for this.
|
||||
AstNodeCond* const condp = VN_CAST(nodep->rhsp(), NodeCond);
|
||||
if (!condp) return false;
|
||||
if (!VN_IS(condp->expr1p(), Const) && !VN_IS(condp->expr2p(), Const)) return false;
|
||||
if (!VN_IS(condp->thenp(), Const) && !VN_IS(condp->elsep(), Const)) return false;
|
||||
AstConst* const maskp = VN_CAST(nodep->lhsp(), Const);
|
||||
if (!maskp) return false;
|
||||
UINFO(4, "AND(CONSTm, CONDcond(c, i, e))->CONDcond(c, AND(m,i), AND(m, e)) " << nodep
|
||||
<< endl);
|
||||
AstNodeCond* const newp = static_cast<AstNodeCond*>(
|
||||
condp->cloneType(condp->condp()->unlinkFrBack(),
|
||||
new AstAnd(nodep->fileline(), maskp->cloneTree(false),
|
||||
condp->expr1p()->unlinkFrBack()),
|
||||
new AstAnd(nodep->fileline(), maskp->cloneTree(false),
|
||||
condp->expr2p()->unlinkFrBack())));
|
||||
AstNodeCond* const newp = static_cast<AstNodeCond*>(condp->cloneType(
|
||||
condp->condp()->unlinkFrBack(),
|
||||
new AstAnd(nodep->fileline(), maskp->cloneTree(false), condp->thenp()->unlinkFrBack()),
|
||||
new AstAnd(nodep->fileline(), maskp->cloneTree(false),
|
||||
condp->elsep()->unlinkFrBack())));
|
||||
newp->dtypeFrom(nodep);
|
||||
newp->expr1p()->dtypeFrom(nodep); // As And might have been to change widths
|
||||
newp->expr2p()->dtypeFrom(nodep);
|
||||
newp->thenp()->dtypeFrom(nodep); // As And might have been to change widths
|
||||
newp->elsep()->dtypeFrom(nodep);
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
return true;
|
||||
@@ -1034,7 +1086,7 @@ private:
|
||||
|
||||
if (orLIsRedundant && orRIsRedundant) {
|
||||
nodep->replaceWith(
|
||||
new AstConst(nodep->fileline(), AstConst::DtypedValue(), nodep->dtypep(), 0));
|
||||
new AstConst(nodep->fileline(), AstConst::DTyped{}, nodep->dtypep()));
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
return true;
|
||||
} else if (orLIsRedundant) {
|
||||
@@ -1390,19 +1442,19 @@ private:
|
||||
}
|
||||
}
|
||||
bool ifSameAssign(const AstNodeIf* nodep) {
|
||||
const AstNodeAssign* const ifp = VN_CAST(nodep->ifsp(), NodeAssign);
|
||||
const AstNodeAssign* const elsep = VN_CAST(nodep->elsesp(), NodeAssign);
|
||||
if (!ifp || ifp->nextp()) return false; // Must be SINGLE statement
|
||||
if (!elsep || elsep->nextp()) return false;
|
||||
if (ifp->type() != elsep->type()) return false; // Can't mix an assigndly and an assign
|
||||
if (!ifp->lhsp()->sameGateTree(elsep->lhsp())) return false;
|
||||
if (!ifp->rhsp()->gateTree()) return false;
|
||||
if (!elsep->rhsp()->gateTree()) return false;
|
||||
const AstNodeAssign* const thensp = VN_CAST(nodep->thensp(), NodeAssign);
|
||||
const AstNodeAssign* const elsesp = VN_CAST(nodep->elsesp(), NodeAssign);
|
||||
if (!thensp || thensp->nextp()) return false; // Must be SINGLE statement
|
||||
if (!elsesp || elsesp->nextp()) return false;
|
||||
if (thensp->type() != elsesp->type()) return false; // Can't mix an assigndly with assign
|
||||
if (!thensp->lhsp()->sameGateTree(elsesp->lhsp())) return false;
|
||||
if (!thensp->rhsp()->gateTree()) return false;
|
||||
if (!elsesp->rhsp()->gateTree()) return false;
|
||||
return true;
|
||||
}
|
||||
bool operandIfIf(const AstNodeIf* nodep) {
|
||||
if (nodep->elsesp()) return false;
|
||||
const AstNodeIf* const lowerIfp = VN_CAST(nodep->ifsp(), NodeIf);
|
||||
const AstNodeIf* const lowerIfp = VN_CAST(nodep->thensp(), NodeIf);
|
||||
if (!lowerIfp || lowerIfp->nextp()) return false;
|
||||
if (nodep->type() != lowerIfp->type()) return false;
|
||||
if (afterComment(lowerIfp->elsesp())) return false;
|
||||
@@ -1710,6 +1762,7 @@ private:
|
||||
lp->rhsp(lrp);
|
||||
nodep->lhsp(llp);
|
||||
nodep->rhsp(rlp);
|
||||
nodep->dtypep(llp->dtypep()); // dtype of Biop is before shift.
|
||||
VL_DO_DANGLING(rp->deleteTree(), rp);
|
||||
VL_DO_DANGLING(rrp->deleteTree(), rrp);
|
||||
// nodep->dumpTree(cout, " repShiftSame_new: ");
|
||||
@@ -2023,7 +2076,7 @@ private:
|
||||
AstSel* const sel2p = new AstSel(conp->fileline(), rhs2p, lsb2, msb2 - lsb2 + 1);
|
||||
// Make new assigns of same flavor as old one
|
||||
//*** Not cloneTree; just one node.
|
||||
AstNode* newp = nullptr;
|
||||
AstNodeAssign* newp = nullptr;
|
||||
if (!need_temp) {
|
||||
AstNodeAssign* const asn1ap = VN_AS(nodep->cloneType(lc1p, sel1p), NodeAssign);
|
||||
AstNodeAssign* const asn2ap = VN_AS(nodep->cloneType(lc2p, sel2p), NodeAssign);
|
||||
@@ -2043,8 +2096,8 @@ private:
|
||||
AstVar* const temp2p
|
||||
= new AstVar(sel2p->fileline(), VVarType::BLOCKTEMP,
|
||||
m_concswapNames.get(sel2p), VFlagLogicPacked(), msb2 - lsb2 + 1);
|
||||
m_modp->addStmtp(temp1p);
|
||||
m_modp->addStmtp(temp2p);
|
||||
m_modp->addStmtsp(temp1p);
|
||||
m_modp->addStmtsp(temp2p);
|
||||
AstNodeAssign* const asn1ap
|
||||
= VN_AS(nodep->cloneType(
|
||||
new AstVarRef(sel1p->fileline(), temp1p, VAccess::WRITE), sel1p),
|
||||
@@ -2196,11 +2249,11 @@ private:
|
||||
//----------------------------------------
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNetlist* nodep) override {
|
||||
void visit(AstNetlist* nodep) override {
|
||||
// Iterate modules backwards, in bottom-up order. That's faster
|
||||
iterateChildrenBackwards(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
{
|
||||
m_modp = nodep;
|
||||
@@ -2208,7 +2261,7 @@ private:
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
void visit(AstCFunc* nodep) override {
|
||||
// No ASSIGNW removals under funcs, we've long eliminated INITIALs
|
||||
// (We should perhaps rename the assignw's to just assigns)
|
||||
VL_RESTORER(m_wremove);
|
||||
@@ -2217,7 +2270,7 @@ private:
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstScope* nodep) override {
|
||||
void visit(AstScope* nodep) override {
|
||||
// No ASSIGNW removals under scope, we've long eliminated INITIALs
|
||||
VL_RESTORER(m_wremove);
|
||||
VL_RESTORER(m_scopep);
|
||||
@@ -2314,16 +2367,17 @@ private:
|
||||
}
|
||||
|
||||
// Special cases
|
||||
virtual void visit(AstConst*) override {} // Already constant
|
||||
void visit(AstConst*) override {} // Already constant
|
||||
|
||||
virtual void visit(AstCell* nodep) override {
|
||||
void visit(AstCell* nodep) override {
|
||||
if (m_params) {
|
||||
iterateAndNextNull(nodep->paramsp());
|
||||
} else {
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstPin* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstClassOrPackageRef* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstPin* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
void replaceLogEq(AstLogEq* nodep) {
|
||||
// LOGEQ(a,b) => AstLogAnd{AstLogOr{AstLogNot{a},b},AstLogOr{AstLogNot{b},a}}
|
||||
@@ -2505,7 +2559,7 @@ private:
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstAttrOf* nodep) override {
|
||||
void visit(AstAttrOf* nodep) override {
|
||||
VL_RESTORER(m_attrp);
|
||||
{
|
||||
m_attrp = nodep;
|
||||
@@ -2513,7 +2567,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstArraySel* nodep) override {
|
||||
void visit(AstArraySel* nodep) override {
|
||||
iterateAndNextNull(nodep->bitp());
|
||||
if (VN_IS(nodep->bitp(), Const)
|
||||
&& VN_IS(nodep->fromp(), VarRef)
|
||||
@@ -2539,7 +2593,7 @@ private:
|
||||
}
|
||||
m_selp = nullptr;
|
||||
}
|
||||
virtual void visit(AstNodeVarRef* nodep) override {
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
UASSERT_OBJ(nodep->varp(), nodep, "Not linked");
|
||||
bool did = false;
|
||||
@@ -2593,7 +2647,7 @@ private:
|
||||
<< nodep->varp()->prettyNameQ());
|
||||
}
|
||||
}
|
||||
virtual void visit(AstEnumItemRef* nodep) override {
|
||||
void visit(AstEnumItemRef* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
UASSERT_OBJ(nodep->itemp(), nodep, "Not linked");
|
||||
bool did = false;
|
||||
@@ -2618,7 +2672,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
// virtual void visit(AstCvtPackString* nodep) override {
|
||||
// void visit(AstCvtPackString* nodep) override {
|
||||
// Not constant propagated (for today) because AstNodeMath::isOpaque is set
|
||||
// Someday if lower is constant, convert to quoted "string".
|
||||
|
||||
@@ -2626,7 +2680,7 @@ private:
|
||||
// Only one if it's not in a list
|
||||
return (!nodep->nextp() && nodep->backp()->nextp() != nodep);
|
||||
}
|
||||
virtual void visit(AstSenItem* nodep) override {
|
||||
void visit(AstSenItem* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (m_doNConst
|
||||
&& (VN_IS(nodep->sensp(), Const) || VN_IS(nodep->sensp(), EnumItemRef)
|
||||
@@ -2693,7 +2747,7 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
virtual void visit(AstSenTree* nodep) override {
|
||||
void visit(AstSenTree* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (m_doExpensive) {
|
||||
// cout<<endl; nodep->dumpTree(cout, "ssin: ");
|
||||
@@ -2777,17 +2831,17 @@ private:
|
||||
|
||||
//-----
|
||||
// Zero elimination
|
||||
virtual void visit(AstNodeAssign* nodep) override {
|
||||
void visit(AstNodeAssign* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (m_doNConst && replaceNodeAssign(nodep)) return;
|
||||
}
|
||||
virtual void visit(AstAssignAlias* nodep) override {
|
||||
void visit(AstAssignAlias* nodep) override {
|
||||
// Don't perform any optimizations, keep the alias around
|
||||
}
|
||||
virtual void visit(AstAssignVarScope* nodep) override {
|
||||
void visit(AstAssignVarScope* nodep) override {
|
||||
// Don't perform any optimizations, the node won't be linked yet
|
||||
}
|
||||
virtual void visit(AstAssignW* nodep) override {
|
||||
void visit(AstAssignW* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (m_doNConst && replaceNodeAssign(nodep)) return;
|
||||
AstNodeVarRef* const varrefp = VN_CAST(
|
||||
@@ -2796,6 +2850,7 @@ private:
|
||||
if (m_wremove && !m_params && m_doNConst && m_modp && operandConst(nodep->rhsp())
|
||||
&& !VN_AS(nodep->rhsp(), Const)->num().isFourState()
|
||||
&& varrefp // Don't do messes with BITREFs/ARRAYREFs
|
||||
&& !varrefp->varp()->hasStrengthAssignment() // Strengths are resolved in V3Tristate
|
||||
&& !varrefp->varp()->valuep() // Not already constified
|
||||
&& !varrefp->varScopep()) { // Not scoped (or each scope may have different initial
|
||||
// value)
|
||||
@@ -2806,14 +2861,14 @@ private:
|
||||
varrefp->unlinkFrBack();
|
||||
AstInitial* const newinitp = new AstInitial(
|
||||
nodep->fileline(), new AstAssign(nodep->fileline(), varrefp, exprp));
|
||||
m_modp->addStmtp(newinitp);
|
||||
m_modp->addStmtsp(newinitp);
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
// Set the initial value right in the variable so we can constant propagate
|
||||
AstNode* const initvaluep = exprp->cloneTree(false);
|
||||
varrefp->varp()->valuep(initvaluep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstRelease* nodep) override {
|
||||
void visit(AstRelease* nodep) override {
|
||||
if (AstConcat* const concatp = VN_CAST(nodep->lhsp(), Concat)) {
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstRelease* const newLp = new AstRelease{flp, concatp->lhsp()->unlinkFrBack()};
|
||||
@@ -2826,7 +2881,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeIf* nodep) override {
|
||||
void visit(AstNodeIf* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (m_doNConst) {
|
||||
if (const AstConst* const constp = VN_CAST(nodep->condp(), Const)) {
|
||||
@@ -2836,7 +2891,7 @@ private:
|
||||
keepp = nodep->elsesp();
|
||||
} else if (!m_doV || constp->isNeqZero()) { // Might be X in Verilog
|
||||
UINFO(4, "IF(!0,{x},{any}) => {x}: " << nodep << endl);
|
||||
keepp = nodep->ifsp();
|
||||
keepp = nodep->thensp();
|
||||
} else {
|
||||
UINFO(4, "IF condition is X, retaining: " << nodep << endl);
|
||||
return;
|
||||
@@ -2848,7 +2903,7 @@ private:
|
||||
nodep->unlinkFrBack();
|
||||
}
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
} else if (!afterComment(nodep->ifsp()) && !afterComment(nodep->elsesp())) {
|
||||
} else if (!afterComment(nodep->thensp()) && !afterComment(nodep->elsesp())) {
|
||||
if (!isTreePureRecurse(nodep->condp())) {
|
||||
// Condition has side effect - leave - perhaps in
|
||||
// future simplify to remove all but side effect terms
|
||||
@@ -2856,27 +2911,27 @@ private:
|
||||
// Empty block, remove it
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
}
|
||||
} else if (!afterComment(nodep->ifsp())) {
|
||||
} else if (!afterComment(nodep->thensp())) {
|
||||
UINFO(4, "IF({x}) nullptr {...} => IF(NOT{x}}: " << nodep << endl);
|
||||
AstNode* const condp = nodep->condp();
|
||||
AstNode* const elsesp = nodep->elsesp();
|
||||
condp->unlinkFrBackWithNext();
|
||||
elsesp->unlinkFrBackWithNext();
|
||||
if (nodep->ifsp()) { // Must have been comment
|
||||
nodep->ifsp()->unlinkFrBackWithNext()->deleteTree();
|
||||
if (nodep->thensp()) { // Must have been comment
|
||||
nodep->thensp()->unlinkFrBackWithNext()->deleteTree();
|
||||
}
|
||||
nodep->condp(new AstLogNot(condp->fileline(),
|
||||
condp)); // LogNot, as C++ optimization also possible
|
||||
nodep->addIfsp(elsesp);
|
||||
nodep->addThensp(elsesp);
|
||||
} else if (((VN_IS(nodep->condp(), Not) && nodep->condp()->width() == 1)
|
||||
|| VN_IS(nodep->condp(), LogNot))
|
||||
&& nodep->ifsp() && nodep->elsesp()) {
|
||||
&& nodep->thensp() && nodep->elsesp()) {
|
||||
UINFO(4, "IF(NOT {x}) => IF(x) swapped if/else" << nodep << endl);
|
||||
AstNode* const condp
|
||||
= VN_AS(nodep->condp(), NodeUniop)->lhsp()->unlinkFrBackWithNext();
|
||||
AstNode* const ifsp = nodep->ifsp()->unlinkFrBackWithNext();
|
||||
AstNode* const thensp = nodep->thensp()->unlinkFrBackWithNext();
|
||||
AstNode* const elsesp = nodep->elsesp()->unlinkFrBackWithNext();
|
||||
AstIf* const ifp = new AstIf(nodep->fileline(), condp, elsesp, ifsp);
|
||||
AstIf* const ifp = new AstIf(nodep->fileline(), condp, elsesp, thensp);
|
||||
ifp->branchPred(nodep->branchPred().invert());
|
||||
nodep->replaceWith(ifp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
@@ -2884,25 +2939,25 @@ private:
|
||||
UINFO(
|
||||
4,
|
||||
"IF({a}) ASSIGN({b},{c}) else ASSIGN({b},{d}) => ASSIGN({b}, {a}?{c}:{d})\n");
|
||||
AstNodeAssign* const ifp = VN_AS(nodep->ifsp(), NodeAssign);
|
||||
AstNodeAssign* const elsep = VN_AS(nodep->elsesp(), NodeAssign);
|
||||
ifp->unlinkFrBack();
|
||||
AstNodeAssign* const thensp = VN_AS(nodep->thensp(), NodeAssign);
|
||||
AstNodeAssign* const elsesp = VN_AS(nodep->elsesp(), NodeAssign);
|
||||
thensp->unlinkFrBack();
|
||||
AstNode* const condp = nodep->condp()->unlinkFrBack();
|
||||
AstNode* const truep = ifp->rhsp()->unlinkFrBack();
|
||||
AstNode* const falsep = elsep->rhsp()->unlinkFrBack();
|
||||
ifp->rhsp(new AstCond(truep->fileline(), condp, truep, falsep));
|
||||
nodep->replaceWith(ifp);
|
||||
AstNode* const truep = thensp->rhsp()->unlinkFrBack();
|
||||
AstNode* const falsep = elsesp->rhsp()->unlinkFrBack();
|
||||
thensp->rhsp(new AstCond(truep->fileline(), condp, truep, falsep));
|
||||
nodep->replaceWith(thensp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
} else if (false // Disabled, as vpm assertions are faster
|
||||
// without due to short-circuiting
|
||||
&& operandIfIf(nodep)) {
|
||||
UINFO(9, "IF({a}) IF({b}) => IF({a} && {b})" << endl);
|
||||
AstNodeIf* const lowerIfp = VN_AS(nodep->ifsp(), NodeIf);
|
||||
AstNodeIf* const lowerIfp = VN_AS(nodep->thensp(), NodeIf);
|
||||
AstNode* const condp = nodep->condp()->unlinkFrBack();
|
||||
AstNode* const lowerIfsp = lowerIfp->ifsp()->unlinkFrBackWithNext();
|
||||
AstNode* const lowerThensp = lowerIfp->thensp()->unlinkFrBackWithNext();
|
||||
AstNode* const lowerCondp = lowerIfp->condp()->unlinkFrBackWithNext();
|
||||
nodep->condp(new AstLogAnd(lowerIfp->fileline(), condp, lowerCondp));
|
||||
lowerIfp->replaceWith(lowerIfsp);
|
||||
lowerIfp->replaceWith(lowerThensp);
|
||||
VL_DO_DANGLING(lowerIfp->deleteTree(), lowerIfp);
|
||||
} else if (operandBoolShift(nodep->condp())) {
|
||||
replaceBoolShift(nodep->condp());
|
||||
@@ -2910,7 +2965,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstDisplay* nodep) override {
|
||||
void visit(AstDisplay* nodep) override {
|
||||
// DISPLAY(SFORMAT(text1)),DISPLAY(SFORMAT(text2)) -> DISPLAY(SFORMAT(text1+text2))
|
||||
iterateChildren(nodep);
|
||||
if (stmtDisplayDisplay(nodep)) return;
|
||||
@@ -2959,12 +3014,14 @@ private:
|
||||
pformatp->text(pformatp->text() + nformatp->text());
|
||||
if (!prevp->addNewline() && nodep->addNewline()) pformatp->text(pformatp->text() + "\n");
|
||||
if (nformatp->exprsp()) pformatp->addExprsp(nformatp->exprsp()->unlinkFrBackWithNext());
|
||||
if (nformatp->scopeNamep())
|
||||
pformatp->scopeNamep(nformatp->scopeNamep()->unlinkFrBackWithNext());
|
||||
if (AstScopeName* const scopeNamep = nformatp->scopeNamep()) {
|
||||
scopeNamep->unlinkFrBackWithNext();
|
||||
pformatp->scopeNamep(scopeNamep);
|
||||
}
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return true;
|
||||
}
|
||||
virtual void visit(AstSFormatF* nodep) override {
|
||||
void visit(AstSFormatF* nodep) override {
|
||||
// Substitute constants into displays. The main point of this is to
|
||||
// simplify assertion methodologies which call functions with display's.
|
||||
// This eliminates a pile of wide temps, and makes the C a whole lot more readable.
|
||||
@@ -3031,17 +3088,17 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstFuncRef* nodep) override {
|
||||
void visit(AstFuncRef* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (m_params) { // Only parameters force us to do constant function call propagation
|
||||
replaceWithSimulation(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstArg* nodep) override {
|
||||
void visit(AstArg* nodep) override {
|
||||
// replaceWithSimulation on the Arg's parent FuncRef replaces these
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstWhile* nodep) override {
|
||||
void visit(AstWhile* nodep) override {
|
||||
const bool oldHasJumpDelay = m_hasJumpDelay;
|
||||
m_hasJumpDelay = false;
|
||||
{ iterateChildren(nodep); }
|
||||
@@ -3067,22 +3124,22 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstInitArray* nodep) override { iterateChildren(nodep); }
|
||||
virtual void visit(AstInitItem* nodep) override { iterateChildren(nodep); }
|
||||
virtual void visit(AstUnbounded* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstInitArray* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstInitItem* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstUnbounded* nodep) override { iterateChildren(nodep); }
|
||||
// These are converted by V3Param. Don't constify as we don't want the
|
||||
// from() VARREF to disappear, if any.
|
||||
// If output of a presel didn't get consted, chances are V3Param didn't visit properly
|
||||
virtual void visit(AstNodePreSel*) override {}
|
||||
void visit(AstNodePreSel*) override {}
|
||||
|
||||
// Ignored, can eliminate early
|
||||
virtual void visit(AstSysIgnore* nodep) override {
|
||||
void visit(AstSysIgnore* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (m_doNConst) VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
}
|
||||
|
||||
// Simplify
|
||||
virtual void visit(AstBasicDType* nodep) override {
|
||||
void visit(AstBasicDType* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
nodep->cvtRangeConst();
|
||||
}
|
||||
@@ -3090,11 +3147,11 @@ private:
|
||||
//-----
|
||||
// Jump elimination
|
||||
|
||||
virtual void visit(AstDelay* nodep) override {
|
||||
void visit(AstDelay* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
m_hasJumpDelay = true;
|
||||
}
|
||||
virtual void visit(AstJumpGo* nodep) override {
|
||||
void visit(AstJumpGo* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
// Jump to label where label immediately follows label is not useful
|
||||
if (nodep->labelp() == VN_CAST(nodep->nextp(), JumpLabel)) {
|
||||
@@ -3125,7 +3182,7 @@ private:
|
||||
m_hasJumpDelay = true;
|
||||
}
|
||||
|
||||
virtual void visit(AstJumpBlock* nodep) override {
|
||||
void visit(AstJumpBlock* nodep) override {
|
||||
// Because JumpLabels disable many optimizations,
|
||||
// remove JumpLabels that are not pointed to by any AstJumpGos
|
||||
// Note this assumes all AstJumpGos are underneath the given label; V3Broken asserts this
|
||||
@@ -3244,19 +3301,19 @@ private:
|
||||
TREEOPC("AstAnd {$lhsp.isOne, matchRedundantClean(nodep)}", "DONE") // 1 & (a == b) -> (IData)(a == b)
|
||||
// Trinary ops
|
||||
// Note V3Case::Sel requires Cond to always be conditionally executed in C to prevent core dump!
|
||||
TREEOP ("AstNodeCond{$condp.isZero, $expr1p, $expr2p}", "replaceWChild(nodep,$expr2p)");
|
||||
TREEOP ("AstNodeCond{$condp.isNeqZero, $expr1p, $expr2p}", "replaceWChild(nodep,$expr1p)");
|
||||
TREEOPA("AstNodeCond{$condp.isZero, $expr1p.castConst, $expr2p.castConst}", "replaceWChild(nodep,$expr2p)");
|
||||
TREEOPA("AstNodeCond{$condp.isNeqZero, $expr1p.castConst, $expr2p.castConst}", "replaceWChild(nodep,$expr1p)");
|
||||
TREEOP ("AstNodeCond{$condp, operandsSame($expr1p,,$expr2p)}","replaceWChild(nodep,$expr1p)");
|
||||
TREEOP ("AstNodeCond{$condp.isZero, $thenp, $elsep}", "replaceWChild(nodep,$elsep)");
|
||||
TREEOP ("AstNodeCond{$condp.isNeqZero, $thenp, $elsep}", "replaceWChild(nodep,$thenp)");
|
||||
TREEOPA("AstNodeCond{$condp.isZero, $thenp.castConst, $elsep.castConst}", "replaceWChild(nodep,$elsep)");
|
||||
TREEOPA("AstNodeCond{$condp.isNeqZero, $thenp.castConst, $elsep.castConst}", "replaceWChild(nodep,$thenp)");
|
||||
TREEOP ("AstNodeCond{$condp, operandsSame($thenp,,$elsep)}","replaceWChild(nodep,$thenp)");
|
||||
// This visit function here must allow for short-circuiting.
|
||||
TREEOPS("AstCond {$lhsp.isZero}", "replaceWIteratedThs(nodep)");
|
||||
TREEOPS("AstCond {$lhsp.isNeqZero}", "replaceWIteratedRhs(nodep)");
|
||||
TREEOP ("AstCond{$condp.castNot, $expr1p, $expr2p}", "AstCond{$condp->op1p(), $expr2p, $expr1p}");
|
||||
TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p.isAllOnes, $expr2p}", "AstLogOr {$condp, $expr2p}"); // a?1:b == a||b
|
||||
TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p, $expr2p.isZero}", "AstLogAnd{$condp, $expr1p}"); // a?b:0 == a&&b
|
||||
TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p, $expr2p.isAllOnes}", "AstLogOr {AstNot{$condp}, $expr1p}"); // a?b:1 == ~a||b
|
||||
TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p.isZero, $expr2p}", "AstLogAnd{AstNot{$condp}, $expr2p}"); // a?0:b == ~a&&b
|
||||
TREEOP ("AstCond{$condp.castNot, $thenp, $elsep}", "AstCond{$condp->op1p(), $elsep, $thenp}");
|
||||
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isAllOnes, $elsep}", "AstLogOr {$condp, $elsep}"); // a?1:b == a||b
|
||||
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isZero}", "AstLogAnd{$condp, $thenp}"); // a?b:0 == a&&b
|
||||
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isAllOnes}", "AstLogOr {AstNot{$condp}, $thenp}"); // a?b:1 == ~a||b
|
||||
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isZero, $elsep}", "AstLogAnd{AstNot{$condp}, $elsep}"); // a?0:b == ~a&&b
|
||||
TREEOP ("AstNodeCond{!$condp.width1, operandBoolShift(nodep->condp())}", "replaceBoolShift(nodep->condp())");
|
||||
// Prefer constants on left, since that often needs a shift, it lets
|
||||
// constant red remove the shift
|
||||
@@ -3479,7 +3536,7 @@ private:
|
||||
// Note we can't convert EqCase/NeqCase to Eq/Neq here because that would break 3'b1x1==3'b101
|
||||
|
||||
//-----
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
void visit(AstNode* nodep) override {
|
||||
// Default: Just iterate
|
||||
if (m_required) {
|
||||
if (VN_IS(nodep, NodeDType) || VN_IS(nodep, Range) || VN_IS(nodep, SliceSel)) {
|
||||
@@ -3526,9 +3583,13 @@ public:
|
||||
}
|
||||
// clang-format on
|
||||
}
|
||||
virtual ~ConstVisitor() override {
|
||||
~ConstVisitor() override {
|
||||
if (m_doCpp) {
|
||||
V3Stats::addStat("Optimizations, Const bit op reduction", m_statBitOpReduction);
|
||||
if (m_globalPass) {
|
||||
V3Stats::addStat("Optimizations, Const bit op reduction", m_statBitOpReduction);
|
||||
} else {
|
||||
V3Stats::addStatSum("Optimizations, Const bit op reduction", m_statBitOpReduction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3603,7 +3664,7 @@ void V3Const::constifyAllLint(AstNetlist* nodep) {
|
||||
ConstVisitor visitor{ConstVisitor::PROC_V_WARN, /* globalPass: */ true};
|
||||
(void)visitor.mainAcceptEdit(nodep);
|
||||
} // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("const", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("const", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
void V3Const::constifyCpp(AstNetlist* nodep) {
|
||||
@@ -3612,7 +3673,7 @@ void V3Const::constifyCpp(AstNetlist* nodep) {
|
||||
ConstVisitor visitor{ConstVisitor::PROC_CPP, /* globalPass: */ true};
|
||||
(void)visitor.mainAcceptEdit(nodep);
|
||||
} // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("const_cpp", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("const_cpp", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
AstNode* V3Const::constifyEdit(AstNode* nodep) {
|
||||
@@ -3621,6 +3682,12 @@ AstNode* V3Const::constifyEdit(AstNode* nodep) {
|
||||
return nodep;
|
||||
}
|
||||
|
||||
AstNode* V3Const::constifyEditCpp(AstNode* nodep) {
|
||||
ConstVisitor visitor{ConstVisitor::PROC_CPP, /* globalPass: */ false};
|
||||
nodep = visitor.mainAcceptEdit(nodep);
|
||||
return nodep;
|
||||
}
|
||||
|
||||
void V3Const::constifyAllLive(AstNetlist* nodep) {
|
||||
// Only call from Verilator.cpp, as it uses user#'s
|
||||
// This only pushes constants up, doesn't make any other edits
|
||||
@@ -3630,7 +3697,7 @@ void V3Const::constifyAllLive(AstNetlist* nodep) {
|
||||
ConstVisitor visitor{ConstVisitor::PROC_LIVE, /* globalPass: */ true};
|
||||
(void)visitor.mainAcceptEdit(nodep);
|
||||
} // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("const", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("const", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
void V3Const::constifyAll(AstNetlist* nodep) {
|
||||
@@ -3640,7 +3707,7 @@ void V3Const::constifyAll(AstNetlist* nodep) {
|
||||
ConstVisitor visitor{ConstVisitor::PROC_V_EXPENSIVE, /* globalPass: */ true};
|
||||
(void)visitor.mainAcceptEdit(nodep);
|
||||
} // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("const", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("const", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
AstNode* V3Const::constifyExpensiveEdit(AstNode* nodep) {
|
||||
|
||||
@@ -39,6 +39,9 @@ public:
|
||||
static void constifyCpp(AstNetlist* nodep);
|
||||
// Only the current node and lower
|
||||
// Return new node that may have replaced nodep
|
||||
static AstNode* constifyEditCpp(AstNode* nodep);
|
||||
// Only the current node and lower
|
||||
// Return new node that may have replaced nodep
|
||||
static AstNode* constifyEdit(AstNode* nodep);
|
||||
// Only the current node and lower, with special SenTree optimization
|
||||
// Return new node that may have replaced nodep
|
||||
|
||||
+69
-66
@@ -27,13 +27,16 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Coverage.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Coverage state, as a visitor of each AstNode
|
||||
|
||||
@@ -85,7 +88,6 @@ private:
|
||||
m_handleLines; // All line numbers for a given m_stateHandle
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
const char* varIgnoreToggle(AstVar* nodep) {
|
||||
// Return true if this shouldn't be traced
|
||||
@@ -118,7 +120,7 @@ private:
|
||||
|
||||
AstCoverDecl* const declp = new AstCoverDecl(fl, page, comment, linescov, offset);
|
||||
declp->hier(hier);
|
||||
m_modp->addStmtp(declp);
|
||||
m_modp->addStmtsp(declp);
|
||||
UINFO(9, "new " << declp << endl);
|
||||
|
||||
AstCoverInc* const incp = new AstCoverInc(fl, declp);
|
||||
@@ -127,13 +129,13 @@ private:
|
||||
incp->findUInt32DType());
|
||||
varp->trace(true);
|
||||
varp->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true);
|
||||
m_modp->addStmtp(varp);
|
||||
m_modp->addStmtsp(varp);
|
||||
UINFO(5, "New coverage trace: " << varp << endl);
|
||||
AstAssign* const assp = new AstAssign(
|
||||
incp->fileline(), new AstVarRef(incp->fileline(), varp, VAccess::WRITE),
|
||||
new AstAdd(incp->fileline(), new AstVarRef(incp->fileline(), varp, VAccess::READ),
|
||||
new AstConst(incp->fileline(), AstConst::WidthedValue(), 32, 1)));
|
||||
incp->addNext(assp);
|
||||
AstNode::addNext<AstNode, AstNode>(incp, assp);
|
||||
}
|
||||
return incp;
|
||||
}
|
||||
@@ -208,7 +210,7 @@ private:
|
||||
}
|
||||
|
||||
// VISITORS - BOTH
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
const AstNodeModule* const origModp = m_modp;
|
||||
VL_RESTORER(m_modp);
|
||||
VL_RESTORER(m_state);
|
||||
@@ -226,9 +228,9 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeProcedure* nodep) override { iterateProcedure(nodep); }
|
||||
virtual void visit(AstWhile* nodep) override { iterateProcedure(nodep); }
|
||||
virtual void visit(AstNodeFTask* nodep) override {
|
||||
void visit(AstNodeProcedure* nodep) override { iterateProcedure(nodep); }
|
||||
void visit(AstWhile* nodep) override { iterateProcedure(nodep); }
|
||||
void visit(AstNodeFTask* nodep) override {
|
||||
if (!nodep->dpiImport()) iterateProcedure(nodep);
|
||||
}
|
||||
void iterateProcedure(AstNode* nodep) {
|
||||
@@ -244,11 +246,11 @@ private:
|
||||
= newCoverInc(nodep->fileline(), "", "v_line", "block",
|
||||
linesCov(m_state, nodep), 0, traceNameForLine(nodep, "block"));
|
||||
if (AstNodeProcedure* const itemp = VN_CAST(nodep, NodeProcedure)) {
|
||||
itemp->addStmtp(newp);
|
||||
itemp->addStmtsp(newp);
|
||||
} else if (AstNodeFTask* const itemp = VN_CAST(nodep, NodeFTask)) {
|
||||
itemp->addStmtsp(newp);
|
||||
} else if (AstWhile* const itemp = VN_CAST(nodep, While)) {
|
||||
itemp->addBodysp(newp);
|
||||
itemp->addStmtsp(newp);
|
||||
} else {
|
||||
nodep->v3fatalSrc("Bad node type");
|
||||
}
|
||||
@@ -257,7 +259,7 @@ private:
|
||||
}
|
||||
|
||||
// VISITORS - TOGGLE COVERAGE
|
||||
virtual void visit(AstVar* nodep) override {
|
||||
void visit(AstVar* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (m_modp && !m_inToggleOff && !m_state.m_inModOff && nodep->fileline()->coverageOn()
|
||||
&& v3Global.opt.coverageToggle()) {
|
||||
@@ -278,19 +280,19 @@ private:
|
||||
// We'll do this, and make the if(...) coverinc later.
|
||||
|
||||
// Add signal to hold the old value
|
||||
const string newvarname = string("__Vtogcov__") + nodep->shortName();
|
||||
const string newvarname = std::string{"__Vtogcov__"} + nodep->shortName();
|
||||
AstVar* const chgVarp
|
||||
= new AstVar(nodep->fileline(), VVarType::MODULETEMP, newvarname, nodep);
|
||||
chgVarp->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true);
|
||||
m_modp->addStmtp(chgVarp);
|
||||
m_modp->addStmtsp(chgVarp);
|
||||
|
||||
// Create bucket for each dimension * bit.
|
||||
// This is necessarily an O(n^2) expansion, which is why
|
||||
// we limit coverage to signals with < 256 bits.
|
||||
|
||||
ToggleEnt newvec(string(""),
|
||||
new AstVarRef(nodep->fileline(), nodep, VAccess::READ),
|
||||
new AstVarRef(nodep->fileline(), chgVarp, VAccess::WRITE));
|
||||
ToggleEnt newvec{std::string{""},
|
||||
new AstVarRef{nodep->fileline(), nodep, VAccess::READ},
|
||||
new AstVarRef{nodep->fileline(), chgVarp, VAccess::WRITE}};
|
||||
toggleVarRecurse(nodep->dtypeSkipRefp(), 0, newvec, nodep, chgVarp);
|
||||
newvec.cleanup();
|
||||
}
|
||||
@@ -303,7 +305,7 @@ private:
|
||||
newCoverInc(varp->fileline(), "", "v_toggle", varp->name() + above.m_comment, "", 0,
|
||||
""),
|
||||
above.m_varRefp->cloneTree(true), above.m_chgRefp->cloneTree(true));
|
||||
m_modp->addStmtp(newp);
|
||||
m_modp->addStmtsp(newp);
|
||||
}
|
||||
|
||||
void toggleVarRecurse(AstNodeDType* dtypep, int depth, // per-iteration
|
||||
@@ -313,11 +315,12 @@ private:
|
||||
for (int index_docs = bdtypep->lo(); index_docs < bdtypep->hi() + 1;
|
||||
++index_docs) {
|
||||
const int index_code = index_docs - bdtypep->lo();
|
||||
ToggleEnt newent(above.m_comment + string("[") + cvtToStr(index_docs) + "]",
|
||||
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
|
||||
index_code, 1),
|
||||
new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true),
|
||||
index_code, 1));
|
||||
ToggleEnt newent{above.m_comment + std::string{"["} + cvtToStr(index_docs)
|
||||
+ "]",
|
||||
new AstSel{varp->fileline(), above.m_varRefp->cloneTree(true),
|
||||
index_code, 1},
|
||||
new AstSel{varp->fileline(), above.m_chgRefp->cloneTree(true),
|
||||
index_code, 1}};
|
||||
toggleVarBottom(newent, varp);
|
||||
newent.cleanup();
|
||||
}
|
||||
@@ -327,11 +330,11 @@ private:
|
||||
} else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
for (int index_docs = adtypep->lo(); index_docs <= adtypep->hi(); ++index_docs) {
|
||||
const int index_code = index_docs - adtypep->lo();
|
||||
ToggleEnt newent(above.m_comment + string("[") + cvtToStr(index_docs) + "]",
|
||||
new AstArraySel(varp->fileline(),
|
||||
above.m_varRefp->cloneTree(true), index_code),
|
||||
new AstArraySel(varp->fileline(),
|
||||
above.m_chgRefp->cloneTree(true), index_code));
|
||||
ToggleEnt newent{above.m_comment + std::string{"["} + cvtToStr(index_docs) + "]",
|
||||
new AstArraySel{varp->fileline(),
|
||||
above.m_varRefp->cloneTree(true), index_code},
|
||||
new AstArraySel{varp->fileline(),
|
||||
above.m_chgRefp->cloneTree(true), index_code}};
|
||||
toggleVarRecurse(adtypep->subDTypep()->skipRefp(), depth + 1, newent, varp,
|
||||
chgVarp);
|
||||
newent.cleanup();
|
||||
@@ -340,11 +343,11 @@ private:
|
||||
for (int index_docs = adtypep->lo(); index_docs <= adtypep->hi(); ++index_docs) {
|
||||
const AstNodeDType* const subtypep = adtypep->subDTypep()->skipRefp();
|
||||
const int index_code = index_docs - adtypep->lo();
|
||||
ToggleEnt newent(above.m_comment + string("[") + cvtToStr(index_docs) + "]",
|
||||
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
|
||||
index_code * subtypep->width(), subtypep->width()),
|
||||
new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true),
|
||||
index_code * subtypep->width(), subtypep->width()));
|
||||
ToggleEnt newent{above.m_comment + std::string{"["} + cvtToStr(index_docs) + "]",
|
||||
new AstSel{varp->fileline(), above.m_varRefp->cloneTree(true),
|
||||
index_code * subtypep->width(), subtypep->width()},
|
||||
new AstSel{varp->fileline(), above.m_chgRefp->cloneTree(true),
|
||||
index_code * subtypep->width(), subtypep->width()}};
|
||||
toggleVarRecurse(adtypep->subDTypep()->skipRefp(), depth + 1, newent, varp,
|
||||
chgVarp);
|
||||
newent.cleanup();
|
||||
@@ -355,11 +358,11 @@ private:
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
AstNodeDType* const subtypep = itemp->subDTypep()->skipRefp();
|
||||
const int index_code = itemp->lsb();
|
||||
ToggleEnt newent(above.m_comment + string(".") + itemp->name(),
|
||||
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
|
||||
index_code, subtypep->width()),
|
||||
new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true),
|
||||
index_code, subtypep->width()));
|
||||
ToggleEnt newent{above.m_comment + std::string{"."} + itemp->name(),
|
||||
new AstSel{varp->fileline(), above.m_varRefp->cloneTree(true),
|
||||
index_code, subtypep->width()},
|
||||
new AstSel{varp->fileline(), above.m_chgRefp->cloneTree(true),
|
||||
index_code, subtypep->width()}};
|
||||
toggleVarRecurse(subtypep, depth + 1, newent, varp, chgVarp);
|
||||
newent.cleanup();
|
||||
}
|
||||
@@ -367,9 +370,9 @@ private:
|
||||
// Arbitrarily handle only the first member of the union
|
||||
if (const AstMemberDType* const itemp = adtypep->membersp()) {
|
||||
AstNodeDType* const subtypep = itemp->subDTypep()->skipRefp();
|
||||
ToggleEnt newent(above.m_comment + string(".") + itemp->name(),
|
||||
ToggleEnt newent{above.m_comment + std::string{"."} + itemp->name(),
|
||||
above.m_varRefp->cloneTree(true),
|
||||
above.m_chgRefp->cloneTree(true));
|
||||
above.m_chgRefp->cloneTree(true)};
|
||||
toggleVarRecurse(subtypep, depth + 1, newent, varp, chgVarp);
|
||||
newent.cleanup();
|
||||
}
|
||||
@@ -381,12 +384,12 @@ private:
|
||||
|
||||
// VISITORS - LINE COVERAGE
|
||||
// Note not AstNodeIf; other types don't get covered
|
||||
virtual void visit(AstIf* nodep) override {
|
||||
void visit(AstIf* nodep) override {
|
||||
UINFO(4, " IF: " << nodep << endl);
|
||||
if (m_state.m_on) {
|
||||
// An else-if. When we iterate the if, use "elsif" marking
|
||||
const bool elsif
|
||||
= nodep->ifsp() && VN_IS(nodep->elsesp(), If) && !nodep->elsesp()->nextp();
|
||||
= nodep->thensp() && VN_IS(nodep->elsesp(), If) && !nodep->elsesp()->nextp();
|
||||
if (elsif) VN_AS(nodep->elsesp(), If)->user1(true);
|
||||
const bool first_elsif = !nodep->user1() && elsif;
|
||||
const bool cont_elsif = nodep->user1() && elsif;
|
||||
@@ -401,7 +404,7 @@ private:
|
||||
CheckState elseState;
|
||||
{
|
||||
createHandle(nodep);
|
||||
iterateAndNextNull(nodep->ifsp());
|
||||
iterateAndNextNull(nodep->thensp());
|
||||
lineTrack(nodep);
|
||||
ifState = m_state;
|
||||
}
|
||||
@@ -420,9 +423,9 @@ private:
|
||||
// Normal if. Linecov shows what's inside the if (not condition that is
|
||||
// always executed)
|
||||
UINFO(4, " COVER-branch: " << nodep << endl);
|
||||
nodep->addIfsp(newCoverInc(nodep->fileline(), "", "v_branch", "if",
|
||||
linesCov(ifState, nodep), 0,
|
||||
traceNameForLine(nodep, "if")));
|
||||
nodep->addThensp(newCoverInc(nodep->fileline(), "", "v_branch", "if",
|
||||
linesCov(ifState, nodep), 0,
|
||||
traceNameForLine(nodep, "if")));
|
||||
// The else has a column offset of 1 to uniquify it relative to the if
|
||||
// As "if" and "else" are more than one character wide, this won't overlap
|
||||
// another token
|
||||
@@ -434,18 +437,18 @@ private:
|
||||
else if (first_elsif || cont_elsif) {
|
||||
UINFO(4, " COVER-elsif: " << nodep << endl);
|
||||
if (ifState.lineCoverageOn(nodep)) {
|
||||
nodep->addIfsp(newCoverInc(nodep->fileline(), "", "v_line", "elsif",
|
||||
linesCov(ifState, nodep), 0,
|
||||
traceNameForLine(nodep, "elsif")));
|
||||
nodep->addThensp(newCoverInc(nodep->fileline(), "", "v_line", "elsif",
|
||||
linesCov(ifState, nodep), 0,
|
||||
traceNameForLine(nodep, "elsif")));
|
||||
}
|
||||
// and we don't insert the else as the child if-else will do so
|
||||
} else {
|
||||
// Cover as separate blocks (not a branch as is not two-legged)
|
||||
if (ifState.lineCoverageOn(nodep)) {
|
||||
UINFO(4, " COVER-half-if: " << nodep << endl);
|
||||
nodep->addIfsp(newCoverInc(nodep->fileline(), "", "v_line", "if",
|
||||
linesCov(ifState, nodep), 0,
|
||||
traceNameForLine(nodep, "if")));
|
||||
nodep->addThensp(newCoverInc(nodep->fileline(), "", "v_line", "if",
|
||||
linesCov(ifState, nodep), 0,
|
||||
traceNameForLine(nodep, "if")));
|
||||
}
|
||||
if (elseState.lineCoverageOn(nodep)) {
|
||||
UINFO(4, " COVER-half-el: " << nodep << endl);
|
||||
@@ -458,7 +461,7 @@ private:
|
||||
}
|
||||
UINFO(9, " done HANDLE " << m_state.m_handle << " for " << nodep << endl);
|
||||
}
|
||||
virtual void visit(AstCaseItem* nodep) override {
|
||||
void visit(AstCaseItem* nodep) override {
|
||||
// We don't add an explicit "default" coverage if not provided,
|
||||
// as we already have a warning when there is no default.
|
||||
UINFO(4, " CASEI: " << nodep << endl);
|
||||
@@ -466,38 +469,38 @@ private:
|
||||
VL_RESTORER(m_state);
|
||||
{
|
||||
createHandle(nodep);
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
if (m_state.lineCoverageOn(nodep)) { // if the case body didn't disable it
|
||||
lineTrack(nodep);
|
||||
UINFO(4, " COVER: " << nodep << endl);
|
||||
nodep->addBodysp(newCoverInc(nodep->fileline(), "", "v_line", "case",
|
||||
nodep->addStmtsp(newCoverInc(nodep->fileline(), "", "v_line", "case",
|
||||
linesCov(m_state, nodep), 0,
|
||||
traceNameForLine(nodep, "case")));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCover* nodep) override {
|
||||
void visit(AstCover* nodep) override {
|
||||
UINFO(4, " COVER: " << nodep << endl);
|
||||
VL_RESTORER(m_state);
|
||||
{
|
||||
m_state.m_on = true; // Always do cover blocks, even if there's a $stop
|
||||
createHandle(nodep);
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->coverincp() && v3Global.opt.coverageUser()) {
|
||||
if (!nodep->coverincsp() && v3Global.opt.coverageUser()) {
|
||||
// Note the name may be overridden by V3Assert processing
|
||||
lineTrack(nodep);
|
||||
nodep->coverincp(newCoverInc(nodep->fileline(), m_beginHier, "v_user", "cover",
|
||||
linesCov(m_state, nodep), 0,
|
||||
m_beginHier + "_vlCoverageUserTrace"));
|
||||
nodep->addCoverincsp(newCoverInc(nodep->fileline(), m_beginHier, "v_user", "cover",
|
||||
linesCov(m_state, nodep), 0,
|
||||
m_beginHier + "_vlCoverageUserTrace"));
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstStop* nodep) override {
|
||||
void visit(AstStop* nodep) override {
|
||||
UINFO(4, " STOP: " << nodep << endl);
|
||||
m_state.m_on = false;
|
||||
}
|
||||
virtual void visit(AstPragma* nodep) override {
|
||||
void visit(AstPragma* nodep) override {
|
||||
if (nodep->pragType() == VPragmaType::COVERAGE_BLOCK_OFF) {
|
||||
// Skip all NEXT nodes under this block, and skip this if/case branch
|
||||
UINFO(4, " OFF: h" << m_state.m_handle << " " << nodep << endl);
|
||||
@@ -508,7 +511,7 @@ private:
|
||||
lineTrack(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstBegin* nodep) override {
|
||||
void visit(AstBegin* nodep) override {
|
||||
// Record the hierarchy of any named begins, so we can apply to user
|
||||
// coverage points. This is because there may be cov points inside
|
||||
// generate blocks; each point should get separate consideration.
|
||||
@@ -527,7 +530,7 @@ private:
|
||||
}
|
||||
|
||||
// VISITORS - BOTH
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
void visit(AstNode* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
lineTrack(nodep);
|
||||
}
|
||||
@@ -535,7 +538,7 @@ private:
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CoverageVisitor(AstNetlist* rootp) { iterateChildren(rootp); }
|
||||
virtual ~CoverageVisitor() override = default;
|
||||
~CoverageVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -544,5 +547,5 @@ public:
|
||||
void V3Coverage::coverage(AstNetlist* rootp) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CoverageVisitor{rootp}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("coverage", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("coverage", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+10
-8
@@ -20,13 +20,16 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3CoverageJoin.h"
|
||||
|
||||
#include "V3DupFinder.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// CoverageJoin state, as a visitor of each AstNode
|
||||
|
||||
@@ -41,7 +44,6 @@ private:
|
||||
VDouble0 m_statToggleJoins; // Statistic tracking
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void detectDuplicates() {
|
||||
UINFO(9, "Finding duplicates\n");
|
||||
@@ -87,24 +89,24 @@ private:
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNetlist* nodep) override {
|
||||
void visit(AstNetlist* nodep) override {
|
||||
// Find all Coverage's
|
||||
iterateChildren(nodep);
|
||||
// Simplify
|
||||
detectDuplicates();
|
||||
}
|
||||
virtual void visit(AstCoverToggle* nodep) override {
|
||||
void visit(AstCoverToggle* nodep) override {
|
||||
m_toggleps.push_back(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
//--------------------
|
||||
virtual void visit(AstNodeMath*) override {} // Accelerate
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNodeMath*) override {} // Accelerate
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CoverageJoinVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~CoverageJoinVisitor() override {
|
||||
~CoverageJoinVisitor() override {
|
||||
V3Stats::addStat("Coverage, Toggle points joined", m_statToggleJoins);
|
||||
}
|
||||
};
|
||||
@@ -115,5 +117,5 @@ public:
|
||||
void V3CoverageJoin::coverageJoin(AstNetlist* rootp) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CoverageJoinVisitor{rootp}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("coveragejoin", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("coveragejoin", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+31
-30
@@ -36,13 +36,16 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Dead.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Dead state, as a visitor of each AstNode
|
||||
|
||||
@@ -77,7 +80,6 @@ private:
|
||||
bool m_sideEffect = false; // Side effects discovered in assign RHS
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void checkAll(AstNode* nodep) {
|
||||
if (nodep != nodep->dtypep()) { // NodeDTypes reference themselves
|
||||
@@ -85,7 +87,7 @@ private:
|
||||
}
|
||||
if (AstNode* const subnodep = nodep->getChildDTypep()) subnodep->user1Inc();
|
||||
}
|
||||
void checkVarRef(AstNodeVarRef* nodep) {
|
||||
void checkVarRef(AstNodeVarRef* nodep) const {
|
||||
if (nodep->classOrPackagep() && m_elimCells) nodep->classOrPackagep(nullptr);
|
||||
}
|
||||
void checkDType(AstNodeDType* nodep) {
|
||||
@@ -101,7 +103,7 @@ private:
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
if (m_modp) m_modp->user1Inc(); // e.g. Class under Package
|
||||
VL_RESTORER(m_modp);
|
||||
{
|
||||
@@ -119,12 +121,12 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
void visit(AstCFunc* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->scopep()) nodep->scopep()->user1Inc();
|
||||
}
|
||||
virtual void visit(AstScope* nodep) override {
|
||||
void visit(AstScope* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->aboveScopep()) nodep->aboveScopep()->user1Inc();
|
||||
@@ -135,14 +137,14 @@ private:
|
||||
m_scopesp.push_back(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCell* nodep) override {
|
||||
void visit(AstCell* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
m_cellsp.push_back(nodep);
|
||||
nodep->modp()->user1Inc();
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeVarRef* nodep) override {
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
// Note NodeAssign skips calling this in some cases
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
@@ -154,7 +156,7 @@ private:
|
||||
if (nodep->varp()) nodep->varp()->user1Inc();
|
||||
if (nodep->classOrPackagep()) nodep->classOrPackagep()->user1Inc();
|
||||
}
|
||||
virtual void visit(AstNodeFTaskRef* nodep) override {
|
||||
void visit(AstNodeFTaskRef* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->classOrPackagep()) {
|
||||
@@ -165,11 +167,11 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstMethodCall* nodep) override {
|
||||
void visit(AstMethodCall* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
}
|
||||
virtual void visit(AstRefDType* nodep) override {
|
||||
void visit(AstRefDType* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
checkDType(nodep);
|
||||
checkAll(nodep);
|
||||
@@ -183,7 +185,7 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstClassRefDType* nodep) override {
|
||||
void visit(AstClassRefDType* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
checkDType(nodep);
|
||||
checkAll(nodep);
|
||||
@@ -196,12 +198,12 @@ private:
|
||||
}
|
||||
if (nodep->classp()) nodep->classp()->user1Inc();
|
||||
}
|
||||
virtual void visit(AstNodeDType* nodep) override {
|
||||
void visit(AstNodeDType* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
checkDType(nodep);
|
||||
checkAll(nodep);
|
||||
}
|
||||
virtual void visit(AstEnumItemRef* nodep) override {
|
||||
void visit(AstEnumItemRef* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->classOrPackagep()) {
|
||||
@@ -213,13 +215,13 @@ private:
|
||||
}
|
||||
checkAll(nodep);
|
||||
}
|
||||
virtual void visit(AstMemberSel* nodep) override {
|
||||
void visit(AstMemberSel* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (nodep->varp()) nodep->varp()->user1Inc();
|
||||
if (nodep->fromp()->dtypep()) nodep->fromp()->dtypep()->user1Inc(); // classref
|
||||
checkAll(nodep);
|
||||
}
|
||||
virtual void visit(AstModport* nodep) override {
|
||||
void visit(AstModport* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (m_elimCells) {
|
||||
if (!nodep->varsp()) {
|
||||
@@ -229,14 +231,14 @@ private:
|
||||
}
|
||||
checkAll(nodep);
|
||||
}
|
||||
virtual void visit(AstSelLoopVars* nodep) override {
|
||||
void visit(AstSelLoopVars* nodep) override {
|
||||
// Var under a SelLoopVars means we haven't called V3Width to remove them yet
|
||||
VL_RESTORER(m_selloopvarsp);
|
||||
m_selloopvarsp = nodep;
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
}
|
||||
virtual void visit(AstTypedef* nodep) override {
|
||||
void visit(AstTypedef* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (m_elimCells && !nodep->attrPublic()) {
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
@@ -247,20 +249,20 @@ private:
|
||||
// Normal modules may disappear, e.g. if they are parameterized then removed
|
||||
if (nodep->attrPublic() && m_modp && VN_IS(m_modp, Package)) m_modp->user1Inc();
|
||||
}
|
||||
virtual void visit(AstVarScope* nodep) override {
|
||||
void visit(AstVarScope* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->scopep()) nodep->scopep()->user1Inc();
|
||||
if (mightElimVar(nodep->varp())) m_vscsp.push_back(nodep);
|
||||
}
|
||||
virtual void visit(AstVar* nodep) override {
|
||||
void visit(AstVar* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->isSigPublic() && m_modp && VN_IS(m_modp, Package)) m_modp->user1Inc();
|
||||
if (m_selloopvarsp) nodep->user1Inc();
|
||||
if (mightElimVar(nodep)) m_varsp.push_back(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep) override {
|
||||
void visit(AstNodeAssign* nodep) override {
|
||||
// See if simple assignments to variables may be eliminated because
|
||||
// that variable is never used.
|
||||
// Similar code in V3Life
|
||||
@@ -283,7 +285,7 @@ private:
|
||||
}
|
||||
|
||||
//-----
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
void visit(AstNode* nodep) override {
|
||||
if (nodep->isOutputter()) m_sideEffect = true;
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
@@ -316,7 +318,7 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
bool mightElimVar(AstVar* nodep) {
|
||||
bool mightElimVar(AstVar* nodep) const {
|
||||
if (nodep->isSigPublic()) return false; // Can't elim publics!
|
||||
if (nodep->isIO() || nodep->isClassMember()) return false;
|
||||
if (nodep->isTemp() && !nodep->isTrace()) return true;
|
||||
@@ -445,7 +447,7 @@ public:
|
||||
// We may have removed some datatypes, cleanup
|
||||
nodep->typeTablep()->repairCache();
|
||||
}
|
||||
virtual ~DeadVisitor() override = default;
|
||||
~DeadVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -454,30 +456,29 @@ public:
|
||||
void V3Dead::deadifyModules(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DeadVisitor{nodep, false, false, false, false}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadModules", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6);
|
||||
V3Global::dumpCheckGlobalTree("deadModules", 0, dumpTree() >= 6);
|
||||
}
|
||||
|
||||
void V3Dead::deadifyDTypes(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DeadVisitor{nodep, false, true, false, false}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadDtypes", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("deadDtypes", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
void V3Dead::deadifyDTypesScoped(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DeadVisitor{nodep, false, true, true, false}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0,
|
||||
v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
void V3Dead::deadifyAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DeadVisitor{nodep, true, true, false, true}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadAll", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("deadAll", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
void V3Dead::deadifyAllScoped(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DeadVisitor{nodep, true, true, true, true}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadAllScoped", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("deadAllScoped", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+20
-18
@@ -51,15 +51,18 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Delayed.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Delayed state, as a visitor of each AstNode
|
||||
|
||||
@@ -101,7 +104,6 @@ private:
|
||||
std::unordered_map<const AstVarScope*, int> m_scopeVecMap; // Next var number for each scope
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void markVarUsage(AstNodeVarRef* nodep, bool blocking) {
|
||||
// Ignore if warning is disabled on this reference (used by V3Force).
|
||||
@@ -152,13 +154,13 @@ private:
|
||||
varp = new AstVar(oldvarscp->fileline(), VVarType::BLOCKTEMP, name,
|
||||
VFlagBitPacked(), width);
|
||||
}
|
||||
addmodp->addStmtp(varp);
|
||||
addmodp->addStmtsp(varp);
|
||||
m_modVarMap.emplace(std::make_pair(addmodp, name), varp);
|
||||
}
|
||||
|
||||
AstVarScope* const varscp
|
||||
= new AstVarScope(oldvarscp->fileline(), oldvarscp->scopep(), varp);
|
||||
oldvarscp->scopep()->addVarp(varscp);
|
||||
oldvarscp->scopep()->addVarsp(varscp);
|
||||
return varscp;
|
||||
}
|
||||
|
||||
@@ -355,33 +357,33 @@ private:
|
||||
postLogicp = new AstIf(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), setvscp, VAccess::READ));
|
||||
UINFO(9, " Created " << postLogicp << endl);
|
||||
finalp->addStmtp(postLogicp);
|
||||
finalp->addStmtsp(postLogicp);
|
||||
finalp->user3p(setvscp); // Remember IF's vset variable
|
||||
finalp->user4p(postLogicp); // and the associated IF, as we may be able to reuse it
|
||||
}
|
||||
postLogicp->addIfsp(new AstAssign(nodep->fileline(), selectsp, valreadp));
|
||||
postLogicp->addThensp(new AstAssign(nodep->fileline(), selectsp, valreadp));
|
||||
return newlhsp;
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNetlist* nodep) override {
|
||||
void visit(AstNetlist* nodep) override {
|
||||
// VV***** We reset all userp() on the netlist
|
||||
m_modVarMap.clear();
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstScope* nodep) override {
|
||||
void visit(AstScope* nodep) override {
|
||||
UINFO(4, " MOD " << nodep << endl);
|
||||
AstNode::user3ClearTree();
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
void visit(AstCFunc* nodep) override {
|
||||
VL_RESTORER(m_cfuncp);
|
||||
{
|
||||
m_cfuncp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstActive* nodep) override {
|
||||
void visit(AstActive* nodep) override {
|
||||
m_activep = nodep;
|
||||
VL_RESTORER(m_inInitial);
|
||||
{
|
||||
@@ -391,7 +393,7 @@ private:
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstAssignDly* nodep) override {
|
||||
void visit(AstAssignDly* nodep) override {
|
||||
m_inDly = true;
|
||||
m_nextDlyp
|
||||
= VN_CAST(nodep->nextp(), AssignDly); // Next assignment in same block, maybe nullptr.
|
||||
@@ -425,7 +427,7 @@ private:
|
||||
m_nextDlyp = nullptr;
|
||||
}
|
||||
|
||||
virtual void visit(AstVarRef* nodep) override {
|
||||
void visit(AstVarRef* nodep) override {
|
||||
if (!nodep->user2Inc()) { // Not done yet
|
||||
if (m_inDly && nodep->access().isWriteOrRW()) {
|
||||
UINFO(4, "AssignDlyVar: " << nodep << endl);
|
||||
@@ -487,18 +489,18 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeReadWriteMem* nodep) override {
|
||||
void visit(AstNodeReadWriteMem* nodep) override {
|
||||
VL_RESTORER(m_ignoreBlkAndNBlk);
|
||||
m_ignoreBlkAndNBlk = true; // $readmem/$writemem often used in mem models
|
||||
// so we will suppress BLKANDNBLK warnings
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeFor* nodep) override { // LCOV_EXCL_LINE
|
||||
void visit(AstNodeFor* nodep) override { // LCOV_EXCL_LINE
|
||||
nodep->v3fatalSrc(
|
||||
"For statements should have been converted to while statements in V3Begin");
|
||||
}
|
||||
virtual void visit(AstWhile* nodep) override {
|
||||
void visit(AstWhile* nodep) override {
|
||||
VL_RESTORER(m_inLoop);
|
||||
{
|
||||
m_inLoop = true;
|
||||
@@ -507,12 +509,12 @@ private:
|
||||
}
|
||||
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit DelayedVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~DelayedVisitor() override {
|
||||
~DelayedVisitor() override {
|
||||
V3Stats::addStat("Optimizations, Delayed shared-sets", m_statSharedSet);
|
||||
}
|
||||
};
|
||||
@@ -523,5 +525,5 @@ public:
|
||||
void V3Delayed::delayedAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DelayedVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("delayed", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("delayed", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+16
-17
@@ -26,13 +26,16 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Depth.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
|
||||
class DepthVisitor final : public VNVisitor {
|
||||
@@ -48,7 +51,6 @@ private:
|
||||
V3UniqueNames m_tempNames; // For generating unique temporary variable names
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void createDeepTemp(AstNode* nodep) {
|
||||
UINFO(6, " Deep " << nodep << endl);
|
||||
@@ -68,14 +70,11 @@ private:
|
||||
// Put assignment before the referencing statement
|
||||
AstAssign* const assp = new AstAssign{
|
||||
nodep->fileline(), new AstVarRef{nodep->fileline(), varp, VAccess::WRITE}, nodep};
|
||||
VNRelinker linker2;
|
||||
m_stmtp->unlinkFrBack(&linker2);
|
||||
assp->addNext(m_stmtp);
|
||||
linker2.relink(assp);
|
||||
m_stmtp->addHereThisAsNext(assp);
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
void visit(AstCFunc* nodep) override {
|
||||
VL_RESTORER(m_cfuncp);
|
||||
VL_RESTORER(m_mtaskbodyp);
|
||||
{
|
||||
@@ -87,7 +86,7 @@ private:
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstMTaskBody* nodep) override {
|
||||
void visit(AstMTaskBody* nodep) override {
|
||||
VL_RESTORER(m_cfuncp);
|
||||
VL_RESTORER(m_mtaskbodyp);
|
||||
{
|
||||
@@ -108,7 +107,7 @@ private:
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeStmt* nodep) override {
|
||||
void visit(AstNodeStmt* nodep) override {
|
||||
if (!nodep->isStatement()) {
|
||||
iterateChildren(nodep);
|
||||
} else {
|
||||
@@ -116,8 +115,8 @@ private:
|
||||
}
|
||||
}
|
||||
// Operators
|
||||
virtual void visit(AstNodeTermop* nodep) override {}
|
||||
virtual void visit(AstNodeMath* nodep) override {
|
||||
void visit(AstNodeTermop* nodep) override {}
|
||||
void visit(AstNodeMath* nodep) override {
|
||||
// We have some operator defines that use 2 parens, so += 2.
|
||||
{
|
||||
VL_RESTORER(m_depth);
|
||||
@@ -144,19 +143,19 @@ private:
|
||||
m_cfuncp->isStatic(false);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstUCFunc* nodep) override {
|
||||
void visit(AstUCFunc* nodep) override {
|
||||
needNonStaticFunc(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstUCStmt* nodep) override {
|
||||
void visit(AstUCStmt* nodep) override {
|
||||
needNonStaticFunc(nodep);
|
||||
visitStmt(nodep);
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstVar*) override {} // Don't hit varrefs under vars
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstVar*) override {} // Don't hit varrefs under vars
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -164,7 +163,7 @@ public:
|
||||
: m_tempNames{"__Vdeeptemp"} {
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~DepthVisitor() override = default;
|
||||
~DepthVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -173,5 +172,5 @@ public:
|
||||
void V3Depth::depthAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DepthVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("depth", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6);
|
||||
V3Global::dumpCheckGlobalTree("depth", 0, dumpTree() >= 6);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user