mirror of
https://github.com/verilator/verilator.git
synced 2026-10-02 16:13:34 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
7c79f0d431 | ||
|
|
41b9b30b9b | ||
|
|
1fa82ffb7b | ||
|
|
4f93ac6477 | ||
|
|
f7533010c6 | ||
|
|
47b650d821 | ||
|
|
0c2c097377 | ||
|
|
d721f70690 | ||
|
|
ea8aaa21e8 | ||
|
|
660d1059b0 | ||
|
|
e7dc2de14b | ||
|
|
32100a3c91 | ||
|
|
67f7432dd7 | ||
|
|
59dc2853e3 | ||
|
|
ada58465b2 | ||
|
|
173f57c636 | ||
|
|
1f3e8640f7 | ||
|
|
6039e9dcc3 | ||
|
|
d64f979f99 | ||
|
|
0c53d19113 | ||
|
|
c64a07fd09 | ||
|
|
694919b9d1 | ||
|
|
606b35853b | ||
|
|
26b7452178 | ||
|
|
b51f887567 | ||
|
|
c4b8675d77 | ||
|
|
a7cd7a1ed9 | ||
|
|
a48c779367 | ||
|
|
cf1eccc24f | ||
|
|
d45caca011 | ||
|
|
0722f47539 | ||
|
|
a372e010bd | ||
|
|
530817191e | ||
|
|
1282548a1c | ||
|
|
c7610ed044 | ||
|
|
ffc1c51526 | ||
|
|
b130a8cfeb | ||
|
|
551bd284dd | ||
|
|
9edccfdffa | ||
|
|
1a056f6db9 | ||
|
|
e018eb7bac | ||
|
|
0a91ddf38a | ||
|
|
67bb2c640e | ||
|
|
561eaa311d | ||
|
|
3f7a248ed4 | ||
|
|
ecaa07a72a | ||
|
|
99bdc27be3 | ||
|
|
c3c46967dc | ||
|
|
7f1a9239ab | ||
|
|
c8102c8ffe | ||
|
|
5aa12e9b51 | ||
|
|
829437b20b | ||
|
|
3c4131d45d | ||
|
|
ae8d8ee1ac | ||
|
|
89ec3d16dc | ||
|
|
7602c983c5 | ||
|
|
560efb2c9e | ||
|
|
c2328ef46a | ||
|
|
f6035447ae | ||
|
|
38438b3373 | ||
|
|
71dedccbbe | ||
|
|
cddbe4642f | ||
|
|
bdfdc737a0 | ||
|
|
3d045c3aee | ||
|
|
9583f152ee | ||
|
|
5b2755d28d | ||
|
|
9378259779 | ||
|
|
3d762282b9 | ||
|
|
30783e6a79 |
+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
|
||||
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04, ubuntu-18.04]
|
||||
os: [ubuntu-22.04, ubuntu-20.04, ubuntu-18.04]
|
||||
compiler:
|
||||
- { cc: clang, cxx: clang++ }
|
||||
- { cc: gcc, cxx: g++ }
|
||||
@@ -37,9 +37,16 @@ jobs:
|
||||
exclude:
|
||||
# Build pull requests only with ubuntu-20.04 and without m32
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-22.04' || 'do-not-exclude' }}
|
||||
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
|
||||
# Build -m32 only on ubuntu-20.04
|
||||
- {os: ubuntu-18.04, m32: 1}
|
||||
- {os: ubuntu-22.04, m32: 1}
|
||||
include:
|
||||
# Build GCC 10 on ubuntu-20.04
|
||||
- os: ubuntu-20.04
|
||||
compiler: { cc: gcc-10, cxx: g++-10 }
|
||||
m32: 0
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.compiler.cc }} ${{ matrix.m32 && '| -m32' || '' }}
|
||||
env:
|
||||
@@ -90,7 +97,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04, ubuntu-18.04]
|
||||
os: [ubuntu-22.04, ubuntu-20.04, ubuntu-18.04]
|
||||
compiler:
|
||||
- { cc: clang, cxx: clang++ }
|
||||
- { cc: gcc, cxx: g++ }
|
||||
@@ -99,9 +106,18 @@ jobs:
|
||||
exclude:
|
||||
# Build pull requests only with ubuntu-20.04 and without m32
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-22.04' || 'do-not-exclude' }}
|
||||
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
|
||||
# Build -m32 only on ubuntu-20.04
|
||||
- {os: ubuntu-18.04, m32: 1}
|
||||
- {os: ubuntu-22.04, m32: 1}
|
||||
include:
|
||||
# Test with GCC 10 on ubuntu-20.04 without m32
|
||||
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, m32: 0, suite: dist-vlt-0}
|
||||
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, m32: 0, suite: dist-vlt-1}
|
||||
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, m32: 0, suite: dist-vlt-2}
|
||||
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, m32: 0, suite: vltmt-0}
|
||||
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, m32: 0, suite: vltmt-1}
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: Test | ${{ matrix.os }} | ${{ matrix.compiler.cc }} | ${{ matrix.suite }} ${{ matrix.m32 && '| -m32' || '' }}
|
||||
env:
|
||||
@@ -110,7 +126,7 @@ jobs:
|
||||
CI_M32: ${{ matrix.m32 }}
|
||||
CC: ${{ matrix.compiler.cc }}
|
||||
CXX: ${{ matrix.compiler.cxx }}
|
||||
CACHE_BASE_KEY: test-${{ matrix.os }}-${{ matrix.compiler.cc }}-m32=${{ matrix.m32 }}-${ matrix.suite }}
|
||||
CACHE_BASE_KEY: test-${{ matrix.os }}-${{ matrix.compiler.cc }}-m32=${{ matrix.m32 }}-${{ matrix.suite }}
|
||||
CCACHE_MAXSIZE: 64M # Per build matrix entry (2160M in total)
|
||||
VERILATOR_ARCHIVE: verilator-${{ github.sha }}-${{ matrix.os }}-${{ matrix.compiler.cc }}${{ matrix.m32 && '-m32' || '' }}.tar.gz
|
||||
steps:
|
||||
|
||||
@@ -8,6 +8,70 @@ The changes in each Verilator version are described below. The
|
||||
contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
|
||||
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]
|
||||
* 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]
|
||||
* 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]
|
||||
* 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
|
||||
==========================
|
||||
|
||||
**Major:**
|
||||
|
||||
* VCD tracing is now parallelized with --threads (#3449). [Geza Lore, Shunyao CAD]
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Add -f<optimization> options to replace -O<letter> options (#3436).
|
||||
* Changed --no-merge-const-pool to -fno-merge-const-pool (#3436).
|
||||
* Changed --no-decoration to remove output whitespace (#3460). [Kamil Rakoczy]
|
||||
* Support compile time trace signal selection with tracing_on/off (#3323). [Shunyao CAD]
|
||||
* Support non-ANSI interface port declarations (#3439). [Geza Lore, Shunyao CAD]
|
||||
* Support concat assignment to packed array (#3446).
|
||||
* 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/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]
|
||||
* Fix cmake rules to support higher-level targets (#3377) (#3386). [Martin Stadler]
|
||||
* Fix BLKANDNBLK on $readmem/$writemem (#3379). [Alex Solomatnikov]
|
||||
* Fix 'with' operator with type casting (#3387). [xiak95]
|
||||
* Fix incorrect conditional merging (#3409). [Raynard Qiao]
|
||||
* Fix passing VL_TRACE_FST_WRITER_THREAD in CMake build. [Geza Lore, Shunyao CAD]
|
||||
* Fix compile error under strict C++11 mode (#3463). [Kevin Kiningham]
|
||||
* Fix public unpacked input ports (#3465). [Todd Strader]
|
||||
|
||||
|
||||
Verilator 4.222 2022-05-02
|
||||
==========================
|
||||
|
||||
@@ -43,7 +107,7 @@ Verilator 4.220 2022-03-12
|
||||
* Add VERILATOR_VERSION_INTEGER for determining API (#3343). [Larry Doolittle]
|
||||
* Improve various V3Combine algorithm details (#3328). [Yutetsu TAKATSUKASA]
|
||||
* Improve various V3Order algorithm details. [Geza Lore]
|
||||
* Fix macOS arm64 build (#3285) (#3291). [Guokai Chen]
|
||||
* Fix MacOS arm64 build (#3285) (#3291). [Guokai Chen]
|
||||
* Fix signed number operation (#3294) (#3308). [Raynard Qiao]
|
||||
* Fix FST traces to include vector range (#3296) (#3297). [Jamie Iles]
|
||||
* Fix skipping public enum values with four-state values (#3303).
|
||||
@@ -108,7 +172,7 @@ Verilator 4.216 2021-12-05
|
||||
**Minor:**
|
||||
|
||||
* Internal code cleanups and improvements. [Geza Lore]
|
||||
* Improve --thread verilation-time performance.
|
||||
* Improve --thread Verilation-time performance.
|
||||
* Support task name in $display %m (#3211). [Julie Schwartz]
|
||||
* Make 'bit', 'logic' and 'time' types unsigned by default. [Geza Lore]
|
||||
* Optimize $random concatenates/selects (#3114).
|
||||
@@ -132,7 +196,7 @@ Verilator 4.214 2021-10-17
|
||||
|
||||
**Major:**
|
||||
|
||||
* Add profile-guided optmization of mtasks (#3150).
|
||||
* Add profile-guided optimization of mtasks (#3150).
|
||||
|
||||
**Minor:**
|
||||
|
||||
@@ -162,7 +226,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
|
||||
@@ -198,7 +262,7 @@ Verilator 4.210 2021-07-07
|
||||
|
||||
* Add --prof-c to pass profiling to compiler (#3059). [Alexander Grobman]
|
||||
* Optimize a lot more model variables into function locals (#3027). [Geza Lore]
|
||||
* Support middle-of-design nested topmodules (#3026). [Dan Petrisko]
|
||||
* Support middle-of-design nested top modules (#3026). [Dan Petrisko]
|
||||
* Remove deprecated --no-relative-cfuncs option (#3024). [Geza Lore]
|
||||
* Remove deprecated --inhibit-sim option (#3035). [Geza Lore]
|
||||
* Merge const static data globally into a new constant pool (#3013). [Geza Lore]
|
||||
@@ -227,7 +291,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]
|
||||
@@ -237,10 +301,10 @@ Verilator 4.204 2021-06-12
|
||||
* Fix to emit 'else if' without nesting (#2944). [Geza Lore]
|
||||
* Fix part select issues in LATCH warning (#2948) (#2938). [Julien Margetts]
|
||||
* Fix to not emit empty files with low split limits (#2961). [Geza Lore]
|
||||
* Fix merging of assignments in C++ code (#2970). [Ruper Swarbrick]
|
||||
* Fix merging of assignments in C++ code (#2970). [Rupert Swarbrick]
|
||||
* Fix unused variable warnings (#2991). [Pieter Kapsenberg]
|
||||
* Fix --protect-ids when using SV classes (#2994). [Geza Lore]
|
||||
* Fix constant function calls with uninit value (#2995). [yanx21]
|
||||
* Fix constant function calls with uninitialized value (#2995). [yanx21]
|
||||
* Fix Makefiles to support Windows EXEEXT usage (#3008). [Miodrag Milanovic]
|
||||
|
||||
|
||||
@@ -278,7 +342,7 @@ Verilator 4.202 2021-04-24
|
||||
* Fix Cygwin example compile issues (#2856). [Mark Shaw]
|
||||
* Fix select of with index variable (#2880). [Alexander Grobman]
|
||||
* Fix cmake version number to be numeric (#2881). [Yuri Victorovich]
|
||||
* Fix MinGW not supportting 'localtime_r' (#2882). [HyungKi Jeong]
|
||||
* Fix MinGW not supporting 'localtime_r' (#2882). [HyungKi Jeong]
|
||||
* Fix cast from packed, typedef'ed interface signal (#2884). [Todd Strader]
|
||||
* Fix VPI package reported as vpiModule (#2885). [Todd Strader]
|
||||
* Fix dumping waveforms to multiple FST files (#2889). [David Metz]
|
||||
@@ -342,7 +406,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:**
|
||||
|
||||
@@ -402,7 +466,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]
|
||||
@@ -425,7 +489,7 @@ Verilator 4.102 2020-10-15
|
||||
* Support # as a comment in -f files (#2497). [phantom-killua]
|
||||
* Support 'this' (#2585). [Rafal Kapuscik]
|
||||
* Support defines for FST tracing (#2592). [Markus Krause]
|
||||
* Support non-overapping implication inside properties (#1292). [Peter Monsson]
|
||||
* Support non-overlapping implication inside properties (#1292). [Peter Monsson]
|
||||
* Fix timescale with --hierarchical (#2554). [Yutetsu TAKATSUKASA]
|
||||
* Fix cmake build with --hierarchical (#2560). [Yutetsu TAKATSUKASA]
|
||||
* Fix -G dropping public indication (#2561). [Andrew Goessling]
|
||||
@@ -451,7 +515,7 @@ Verilator 4.100 2020-09-07
|
||||
* Support (with limitations) class extern, class extends, virtual class.
|
||||
* Support $urandom, $urandom_range without stability.
|
||||
* Support assume property. [Peter Monsson]
|
||||
* Support non-overapping implication inside properties (#1292). [Peter Monsson]
|
||||
* Support non-overlapping implication inside properties (#1292). [Peter Monsson]
|
||||
* Fix false DECLFILENAME on black-boxed modules (#2430). [Philipp Wagner]
|
||||
* Fix naming of "id : begin" blocks.
|
||||
* Fix class constructor error on assignments to const.
|
||||
|
||||
+5
-2
@@ -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
|
||||
|
||||
+11
-10
@@ -21,7 +21,7 @@ Welcome to Verilator
|
||||
.. list-table::
|
||||
|
||||
* - **Welcome to Verilator, the fastest Verilog/SystemVerilog simulator.**
|
||||
* Accepts synthesizable Verilog or SystemVerilog
|
||||
* Accepts Verilog or SystemVerilog
|
||||
* Performs lint code-quality checks
|
||||
* Compiles into multithreaded C++, or SystemC
|
||||
* Creates XML to front-end your own tools
|
||||
@@ -57,17 +57,18 @@ 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++). The resulting
|
||||
executable performs the design simulation. Verilator also supports linking
|
||||
its generated libraries, optionally encrypted, into other simulators.
|
||||
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.
|
||||
|
||||
Verilator may not be the best choice if you are expecting a full featured
|
||||
replacement for NC-Verilog, 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 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.
|
||||
|
||||
|
||||
Performance
|
||||
|
||||
+22
-19
@@ -284,33 +284,34 @@ detailed descriptions of these arguments.
|
||||
--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
|
||||
--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-tree-addrids Use short identifiers instead of addresses
|
||||
--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-tree-addrids Use short identifiers instead of addresses
|
||||
-E Preprocess, but do not compile
|
||||
--error-limit <value> Abort after this number of errors
|
||||
--exe Link to create executable
|
||||
@@ -319,7 +320,9 @@ detailed descriptions of these arguments.
|
||||
-f <file> Parse arguments from a file
|
||||
-FI <file> Force include of a file
|
||||
--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
|
||||
@@ -327,55 +330,50 @@ 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
|
||||
--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
|
||||
--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
|
||||
--no-merge-const-pool Disable merging of different types in const pool
|
||||
--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.
|
||||
--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 +384,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
|
||||
@@ -405,7 +404,7 @@ detailed descriptions of these arguments.
|
||||
--trace-max-width <width> Maximum array depth for tracing
|
||||
--trace-params Enable tracing of parameters
|
||||
--trace-structs Enable tracing structure names
|
||||
--trace-threads <threads> Enable waveform creation on separate threads
|
||||
--trace-threads <threads> Enable FST waveform creation on separate threads
|
||||
--trace-underscore Enable tracing of _signals
|
||||
-U<var> Undefine preprocessor define
|
||||
--unroll-count <loops> Tune maximum loop iterations
|
||||
@@ -413,6 +412,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 +427,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
|
||||
|
||||
+7
-3
@@ -54,8 +54,12 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
|
||||
|
||||
if [ "$CI_OS_NAME" = "linux" ]; then
|
||||
sudo apt-get update
|
||||
sudo apt-get install libfl-dev libgoogle-perftools-dev ccache
|
||||
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ]; then
|
||||
sudo apt-get install libfl-dev ccache
|
||||
if [ "$CI_RUNS_ON" != "ubuntu-22.04" ]; then
|
||||
# Some conflict of libunwind verison on 22.04, can live without it for now
|
||||
sudo apt-get install libgoogle-perftools-dev
|
||||
fi
|
||||
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ] || [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
|
||||
sudo apt-get install libsystemc libsystemc-dev
|
||||
fi
|
||||
if [ "$COVERAGE" = 1 ]; then
|
||||
@@ -85,7 +89,7 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
||||
sudo apt-get update
|
||||
# libfl-dev needed for internal coverage's test runs
|
||||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache
|
||||
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ]; then
|
||||
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ] || [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
|
||||
sudo apt-get install libsystemc-dev
|
||||
fi
|
||||
if [ "$CI_M32" = 1 ]; then
|
||||
|
||||
+13
-9
@@ -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.222 2022-05-02],
|
||||
[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.226 2022-08-31],
|
||||
[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)
|
||||
@@ -348,14 +348,18 @@ AC_SUBST(CFG_CXXFLAGS_PROFILE)
|
||||
|
||||
# Flag to select newest language standard supported
|
||||
# Macros work such that first option that passes is the one we take
|
||||
# Currently enabled c++14 due to packaged SystemC dependency
|
||||
# c++14 is the newest that Verilator is regressed to support
|
||||
# Currently enable c++17/c++14 due to packaged SystemC dependency
|
||||
# c++17 is the newest that Verilator is regularly tested to support
|
||||
# c++11 is the oldest that Verilator supports
|
||||
# gnu is requried for Cygwin to compile verilated.h successfully
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++20)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++20)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++17)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++17)
|
||||
case "$(which lsb_release 2>&1 > /dev/null && lsb_release -d)" in
|
||||
*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)
|
||||
;;
|
||||
esac
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++14)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++14)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++11)
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
*.html
|
||||
*.pdf
|
||||
_build
|
||||
guide/spelling.txt
|
||||
|
||||
@@ -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,9 +34,11 @@ Gianfranco Costamagna
|
||||
Glen Gibb
|
||||
Graham Rushton
|
||||
Guokai Chen
|
||||
Gustav Svensk
|
||||
Harald Heckmann
|
||||
Howard Su
|
||||
Huang Rui
|
||||
Huanghuang Zhou
|
||||
HungMingWu
|
||||
HyungKi Jeong
|
||||
Iru Cai
|
||||
@@ -67,16 +71,19 @@ Lukasz Dalek
|
||||
Maarten De Braekeleer
|
||||
Maciej Sobkowski
|
||||
Marco Widmer
|
||||
Mariusz Glebocki
|
||||
Markus Krause
|
||||
Marlon James
|
||||
Marshal Qiao
|
||||
Martin Schmidt
|
||||
Martin Stadler
|
||||
Matthew Ballance
|
||||
Michael Killough
|
||||
Michaël Lefebvre
|
||||
Mike Popoloski
|
||||
Miodrag Milanović
|
||||
Morten Borup Petersen
|
||||
Mostafa Gamal
|
||||
Nandu Raj
|
||||
Nathan Kohagen
|
||||
Nathan Myers
|
||||
@@ -93,6 +100,7 @@ Rafal Kapuscik
|
||||
Raynard Qiao
|
||||
Richard Myers
|
||||
Rupert Swarbrick
|
||||
Ryszard Rozak
|
||||
Samuel Riedel
|
||||
Sean Cross
|
||||
Sebastien Van Cauwenberghe
|
||||
@@ -109,9 +117,11 @@ 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
|
||||
|
||||
@@ -35,6 +35,7 @@ default:
|
||||
# Intermediate rules
|
||||
|
||||
vl-extract: ../bin/verilator ../Changes
|
||||
ln -sf ../spelling.txt guide/spelling.txt
|
||||
mkdir -p _build/gen
|
||||
$(PYTHON3) bin/vl_sphinx_extract ../bin/verilator
|
||||
sed 's/`/\&96;/g' < ../Changes > _build/gen/Changes
|
||||
@@ -77,6 +78,7 @@ clean mostlyclean distclean maintainer-clean::
|
||||
rm -f *.pg *.pgs *.toc *.tp *.tps *.vr *.vrs *.idx
|
||||
rm -f *.ev *.evs *.ov *.ovs *.cv *.cvs *.ma *.mas
|
||||
rm -f *.tex
|
||||
rm -f guide/spelling.txt
|
||||
|
||||
distclean maintainer-clean::
|
||||
rm -f *.info* *.1 *.html *.pdf $(INFOS)
|
||||
|
||||
+13
-53
@@ -86,58 +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
|
||||
|
||||
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
|
||||
@@ -446,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
|
||||
|
||||
@@ -20,6 +20,11 @@ Option `--cdc`
|
||||
The experimental `--cdc` option is believed to be generally unused and is
|
||||
planned for removal no sooner than January 2023.
|
||||
|
||||
Option `-O<letter>`
|
||||
The debug `-O<letter>` options have been replaced with
|
||||
`-fno-<optimization>` debug options to match GCC. The old options are
|
||||
planned for removal no sooner than June 2023.
|
||||
|
||||
Option `--prof-threads`
|
||||
The `--prof-threads` option has been superseded by the `--prof-exec` and
|
||||
`--prof-pgo` options and is planned for removal no sooner than April 2023.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+202
-112
@@ -129,16 +129,6 @@ 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>
|
||||
|
||||
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:: --cc
|
||||
|
||||
Specifies C++ without SystemC output mode; see also :vlopt:`--sc`
|
||||
@@ -156,6 +146,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 +179,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).
|
||||
@@ -288,6 +293,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
|
||||
@@ -431,6 +443,78 @@ Summary:
|
||||
flattening large designs may require significant CPU time, memory and
|
||||
storage.
|
||||
|
||||
.. option:: -fno-acyc-simp
|
||||
|
||||
.. option:: -fno-assemble
|
||||
|
||||
.. option:: -fno-case
|
||||
|
||||
.. option:: -fno-combine
|
||||
|
||||
.. option:: -fno-const
|
||||
|
||||
.. option:: -fno-const-bit-op-tree
|
||||
|
||||
.. option:: -fno-dedup
|
||||
|
||||
.. option:: -fno-expand
|
||||
|
||||
.. option:: -fno-gate
|
||||
|
||||
.. option:: -fno-inline
|
||||
|
||||
.. option:: -fno-life
|
||||
|
||||
.. option:: -fno-life-post
|
||||
|
||||
.. option:: -fno-localize
|
||||
|
||||
.. option:: -fno-merge-cond
|
||||
|
||||
.. option:: -fno-merge-cond-motion
|
||||
|
||||
.. option:: -fno-merge-const-pool
|
||||
|
||||
.. option:: -fno-reloop
|
||||
|
||||
.. option:: -fno-reorder
|
||||
|
||||
.. option:: -fno-split
|
||||
|
||||
.. option:: -fno-subst
|
||||
|
||||
.. option:: -fno-subst-const
|
||||
|
||||
.. option:: -fno-table
|
||||
|
||||
Rarely needed. Disables one of the internal optimization steps. These
|
||||
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
|
||||
@@ -537,21 +621,6 @@ Summary:
|
||||
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.)
|
||||
|
||||
.. option:: --l2-name <value>
|
||||
|
||||
Instead of using the module name when showing Verilog scope, use the
|
||||
@@ -568,12 +637,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>
|
||||
|
||||
@@ -592,6 +669,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
|
||||
@@ -630,17 +714,6 @@ Summary:
|
||||
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
|
||||
@@ -648,40 +721,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:: --no-merge-const-pool
|
||||
.. option:: --MMD
|
||||
|
||||
Rarely needed. In order to minimize cache footprint, values of different
|
||||
data type, that are yet emitted identically in C++ are merged in the
|
||||
constant pool. This option disables this and causes every constant pool
|
||||
entry with a distinct data type to be emitted separately.
|
||||
.. 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
|
||||
|
||||
@@ -703,9 +759,9 @@ Summary:
|
||||
|
||||
Rarely needed. Enables or disables a specific optimizations, with the
|
||||
optimization selected based on the letter passed. A lowercase letter
|
||||
disables an optimization, an upper case letter enables it. This is
|
||||
intended for debugging use only; see the source code for
|
||||
version-dependent mappings of optimizations to -O letters.
|
||||
disables an optimization, an upper case letter enables it. This option
|
||||
is deprecated and the various `-f<optimization>` arguments should be
|
||||
used instead.
|
||||
|
||||
.. option:: -o <executable>
|
||||
|
||||
@@ -760,11 +816,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>
|
||||
@@ -797,6 +848,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
|
||||
@@ -826,6 +886,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
|
||||
@@ -847,35 +912,18 @@ Summary:
|
||||
|
||||
.. option:: --prof-exec
|
||||
|
||||
Enable collection of execution trace, that can be convered into a gantt
|
||||
Enable collection of execution trace, that can be converted into a gantt
|
||||
chart with verilator_gantt See :ref:`Execution Profiling`.
|
||||
|
||||
.. option:: --prof-pgo
|
||||
|
||||
Enable collection of profiling data for profile guided verilation. Currently
|
||||
Enable collection of profiling data for profile guided Verilation. Currently
|
||||
this is only useful with :vlopt:`--threads`. See :ref:`Thread PGO`.
|
||||
|
||||
.. option:: --prof-threads
|
||||
|
||||
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
|
||||
@@ -896,6 +944,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
|
||||
@@ -907,11 +972,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
|
||||
@@ -1004,6 +1064,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
|
||||
@@ -1041,7 +1110,8 @@ Summary:
|
||||
is not thread safe. With "--threads 1", the generated model is single
|
||||
threaded but may run in a multithreaded environment. With "--threads N",
|
||||
where N >= 2, the model is generated to run multithreaded on up to N
|
||||
threads. See :ref:`Multithreading`.
|
||||
threads. See :ref:`Multithreading`. This option also applies to
|
||||
:vlopt:`--trace` (but not :vlopt:`--trace-fst`).
|
||||
|
||||
.. option:: --threads-dpi all
|
||||
|
||||
@@ -1119,7 +1189,8 @@ Summary:
|
||||
Having tracing compiled in may result in some small performance losses,
|
||||
even when tracing is not turned on during model execution.
|
||||
|
||||
See also :vlopt:`--trace-threads` option.
|
||||
When using :vlopt:`--threads`, VCD tracing is parallelized, using the
|
||||
same number of threads as passed to :vlopt:`--threads`.
|
||||
|
||||
.. option:: --trace-coverage
|
||||
|
||||
@@ -1173,12 +1244,12 @@ Summary:
|
||||
.. option:: --trace-threads *threads*
|
||||
|
||||
Enable waveform tracing using separate threads. This is typically faster
|
||||
in simulation runtime but uses more total compute. This option is
|
||||
independent of, and works with, both :vlopt:`--trace` and
|
||||
:vlopt:`--trace-fst`. Different trace formats can take advantage of
|
||||
more trace threads to varying degrees. Currently VCD tracing can utilize
|
||||
at most "--trace-threads 1", and FST tracing can utilize at most
|
||||
"--trace-threads 2". This overrides :vlopt:`--no-threads` .
|
||||
in simulation runtime but uses more total compute. This option only
|
||||
applies to :vlopt:`--trace-fst`. FST tracing can utilize at most
|
||||
"--trace-threads 2". This overrides :vlopt:`--no-threads`.
|
||||
|
||||
This option is accepted, but has absolutely no effect with
|
||||
:vlopt:`--trace`, which respects :vlopt:`--threads` instead.
|
||||
|
||||
.. option:: --trace-underscore
|
||||
|
||||
@@ -1676,9 +1747,28 @@ The grammar of configuration commands is as follows:
|
||||
|
||||
.. option:: tracing_off [-file "<filename>" [-lines <line> [ - <line> ]]]
|
||||
|
||||
Enable/disable waveform tracing for all future signals declared in the
|
||||
specified filename (or wildcard with '\*' or '?', or all files if
|
||||
omitted) and range of line numbers (or all lines if omitted).
|
||||
.. option:: tracing_on [-scope "<scopename>" [-levels <levels> ]]
|
||||
|
||||
For tracing_off, instances below any module in the files/ranges
|
||||
specified will also not be traced.
|
||||
.. option:: tracing_off [-scope "<scopename>" [-levels <levels> ]]
|
||||
|
||||
Enable/disable waveform tracing for all future signals declared in
|
||||
all files.
|
||||
|
||||
With -file, enable/disable waveform tracing in the specified
|
||||
filename (or wildcard with '\*' or '?'), and -line range of line
|
||||
numbers (or all lines if omitted).
|
||||
|
||||
For tracing_off with -file, instances below any module in the
|
||||
files/ranges specified will also not be traced. To overcome this
|
||||
feature, use tracing_on on the upper module declaration and on any
|
||||
cells, or use the -scope flavor of the command.
|
||||
|
||||
With -scope enable/disable waveform tracing for the specified scope (or
|
||||
wildcard with '\*' or '?'), and optional --levels number of levels
|
||||
below. These controls only take place after other file/line/module
|
||||
based controls have indicated the signal should be traced.
|
||||
|
||||
With -levels (used with -scope), the number of levels below that
|
||||
scope which the rule is to match, where 0 means all levels below, 1
|
||||
the exact level as the provided scope, and 2 meaning an additional
|
||||
level of children below the provided scope, etc.
|
||||
|
||||
@@ -217,7 +217,7 @@ or "`ifdef`"'s may break other tools.
|
||||
:option:`public_flat` signals.
|
||||
|
||||
To force a marked signal from C++, set the corresponding `__VforceVal`
|
||||
variable to the desired value, and the `__VforceEn` signal to the bitmask
|
||||
variable to the desired value, and the `__VforceEn` signal to the bit-mask
|
||||
indicating which bits of the signal to force. To force all bits of the
|
||||
target signal, set `__VforceEn` to all ones. To release the signal (or part
|
||||
thereof), set appropriate bits of the `__VforceEn` signal to zero.
|
||||
|
||||
+29
-14
@@ -72,23 +72,38 @@ a good thing for getting working silicon.
|
||||
Will Verilator output remain under my own license/copyright?
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Yes, it's just like using GCC on your programs; this is why Verilator uses
|
||||
the "GNU **Lesser** Public License Version 3" instead of the more typical
|
||||
"GNU Public License". See the licenses for details, but in brief, if you
|
||||
change Verilator itself or the header files Verilator includes, you must
|
||||
make the source code available under the GNU Lesser Public License.
|
||||
However, Verilator output (the Verilated code) only "include"s the licensed
|
||||
files, and so you are **not** required to open-source release any output
|
||||
from Verilator.
|
||||
Your SystemVerilog, VPI/DPI, or main() C++ code remains under your own license.
|
||||
|
||||
It's just like how using GCC on your programs does not change the copyright
|
||||
of your program; this is why Verilator uses the "GNU **Lesser** Public
|
||||
License Version 3" instead of the more typical "GNU Public License". See
|
||||
the licenses for details.
|
||||
|
||||
Some examples:
|
||||
|
||||
* Any SystemVerilog or other input fed into Verilator remain your own.
|
||||
|
||||
* Any of your VPI/DPI C++ routines that Verilator calls remain your own.
|
||||
|
||||
* Any of your main() C++ code that calls into Verilator remain your own.
|
||||
|
||||
* If you change Verilator itself, for example changing or adding a file
|
||||
under the src/ directory in the repository, you must make the source code
|
||||
available under the GNU Lesser Public License.
|
||||
|
||||
* If you change a header Verilator provides, for example under include/ in
|
||||
the repository, you must make the source code available under the GNU
|
||||
Lesser Public License.
|
||||
|
||||
You also have the option of using the Perl Artistic License, which again
|
||||
does not require you to release your Verilog or generated code, and also
|
||||
allows you to modify Verilator for internal use without distributing the
|
||||
modified version. But please contribute back to the community!
|
||||
does not require you to release your Verilog, C++, or generated code. This
|
||||
license also allows you to modify Verilator for internal use without
|
||||
distributing the modified version. But please contribute back to the
|
||||
community!
|
||||
|
||||
One limit is that you cannot under either license release a closed-source
|
||||
Verilog simulation product incorporating Verilator. That is you can have a
|
||||
commercial product, but must make the source code available.
|
||||
Under both license you can offer a commercial product that is based on
|
||||
Verilator either directly or embedded within. However under both licenses,
|
||||
any changes you make to Verilator for such a product must be open sourced.
|
||||
|
||||
As is standard with Open Source, contributions back to Verilator will be
|
||||
placed under the Verilator copyright and LGPL/Artistic license. Small test
|
||||
|
||||
+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:
|
||||
|
||||
|
||||
+13
-5
@@ -87,7 +87,7 @@ compiles under all the options above, plus using MSVC++.
|
||||
Install Prerequisites
|
||||
---------------------
|
||||
|
||||
To build or run Verilator you need these standard packages:
|
||||
To build or run Verilator, you need these standard packages:
|
||||
|
||||
::
|
||||
|
||||
@@ -98,13 +98,20 @@ To build or run Verilator you need these standard packages:
|
||||
sudo apt-get install libfl-dev # Ubuntu only (ignore if gives error)
|
||||
sudo apt-get install zlibc zlib1g zlib1g-dev # Ubuntu only (ignore if gives error)
|
||||
|
||||
To build or run the following are optional but should be installed for good
|
||||
performance:
|
||||
To build or run Verilator, the following are optional but should be installed
|
||||
for good performance:
|
||||
|
||||
::
|
||||
|
||||
sudo apt-get install ccache # If present at build, needed for run
|
||||
sudo apt-get install libgoogle-perftools-dev numactl perl-doc
|
||||
sudo apt-get install libgoogle-perftools-dev numactl
|
||||
|
||||
The following is optional but is recommended for nicely rendered command line
|
||||
help when running Verilator:
|
||||
|
||||
::
|
||||
|
||||
sudo apt-get install perl-doc
|
||||
|
||||
To build Verilator you will need to install these packages; these do not
|
||||
need to be present to run Verilator:
|
||||
@@ -118,7 +125,8 @@ 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 pip3 install sphinx sphinx_rtd_theme breathe
|
||||
sudo apt-get install yapf3
|
||||
sudo pip3 install sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe
|
||||
cpan install Pod::Perldoc
|
||||
cpan install Parallel::Forker
|
||||
|
||||
|
||||
@@ -343,7 +343,7 @@ Verilator supports the procedural `force` (and corresponding `release`)
|
||||
statement. The behavior of the `force` statement however does not entirely
|
||||
comply with the IEEE 1800 SystemVerilog standard. According to the standard,
|
||||
when a procedural statement of the form `force a = b;` is executed, the
|
||||
simulation should behave as if from that point onwards, a continuous
|
||||
simulation should behave as if from that point forwards, a continuous
|
||||
assignment `assign a = b;` have been added to override the drivers of `a`.
|
||||
More specifically: the value of `a` should be updated, whenever the value of
|
||||
`b` changes, all the way until a `release a;` statement is executed.
|
||||
@@ -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.
|
||||
|
||||
@@ -405,7 +405,7 @@ others as they prove beneficial.
|
||||
Thread Profile-Guided Optimization
|
||||
----------------------------------
|
||||
|
||||
Verilator supports profile-guided optimization (verilation) of multi-threaded
|
||||
Verilator supports profile-guided optimization (Verilation) of multi-threaded
|
||||
models (Thread PGO) to improve performance.
|
||||
|
||||
When using multithreading, Verilator computes how long macro tasks take and
|
||||
|
||||
+14
-10
@@ -221,9 +221,13 @@ model, it may be beneficial to performance to adjust the
|
||||
influences the partitioning of the model by adjusting the assumed execution
|
||||
time of DPI imports.
|
||||
|
||||
The :vlopt:`--trace-threads` options can be used to produce trace dumps
|
||||
using multiple threads. If :vlopt:`--trace-threads` is set without
|
||||
:vlopt:`--threads`, then :vlopt:`--trace-threads` will imply
|
||||
When using :vlopt:`--trace` to perform VCD tracing, the VCD trace
|
||||
construction is parallelized using the same number of threads as specified
|
||||
with :vlopt:`--threads`, and is executed on the same thread pool as the model.
|
||||
|
||||
The :vlopt:`--trace-threads` options can be used with :vlopt:`--trace-fst`
|
||||
to offload FST tracing using multiple threads. If :vlopt:`--trace-threads` is
|
||||
given without :vlopt:`--threads`, then :vlopt:`--trace-threads` will imply
|
||||
:vlopt:`--threads 1 <--threads>`, i.e.: the support libraries will be
|
||||
thread safe.
|
||||
|
||||
@@ -231,12 +235,12 @@ With :vlopt:`--trace-threads 0 <--trace-threads>`, trace dumps are produced
|
||||
on the main thread. This again gives the highest single thread performance.
|
||||
|
||||
With :vlopt:`--trace-threads {N} <--trace-threads>`, where N is at least 1,
|
||||
N additional threads will be created and managed by the trace files (e.g.:
|
||||
VerilatedVcdC or VerilatedFstC), to generate the trace dump. The main
|
||||
thread will be released to proceed with execution as soon as possible,
|
||||
though some blocking of the main thread is still necessary while capturing
|
||||
the trace. Different trace formats can utilize a various number of
|
||||
threads. See the :vlopt:`--trace-threads` option.
|
||||
up to N additional threads will be created and managed by the trace files
|
||||
(e.g.: VerilatedFstC), to offload construction of the trace dump. The main
|
||||
thread will be released to proceed with execution as soon as possible, though
|
||||
some blocking of the main thread is still necessary while capturing the
|
||||
trace. FST tracing can utilize up to 2 offload threads, so there is no use
|
||||
of setting :vlopt:`--trace-threads` higher than 2 at the moment.
|
||||
|
||||
When running a multithreaded model, the default Linux task scheduler often
|
||||
works against the model, by assuming threads are short lived, and thus
|
||||
@@ -441,7 +445,7 @@ SystemC include directories and link to the SystemC libraries.
|
||||
|
||||
.. describe:: TRACE_THREADS
|
||||
|
||||
Optional. Generated multi-threaded trace dumping, same as
|
||||
Optional. Generated multi-threaded FST trace dumping, same as
|
||||
"--trace-threads".
|
||||
|
||||
.. describe:: TOP_MODULE
|
||||
|
||||
@@ -1506,7 +1506,7 @@ List Of Warnings
|
||||
Error that a construct might be legal according to IEEE but is not
|
||||
currently supported by Verilator.
|
||||
|
||||
A typical workaround is to recode the construct into a simpler and more
|
||||
A typical workaround is to rewrite the construct into a simpler and more
|
||||
common alternative language construct.
|
||||
|
||||
Alternatively, check if the construct is supported by other tools, and
|
||||
|
||||
+1
-1
@@ -274,7 +274,7 @@ path through the graph is the sum of macro-task execution costs. Sarkar
|
||||
does almost the same thing, except that he has nonzero estimates for
|
||||
synchronization costs.
|
||||
|
||||
Verilator's cost estimates are assigned by ``InstrCountCostVisitor``. This
|
||||
Verilator's cost estimates are assigned by ``InstrCountVisitor``. This
|
||||
class is perhaps the most fragile piece of the multithread
|
||||
implementation. It's easy to have a bug where you count something cheap
|
||||
(eg. accessing one element of a huge array) as if it were expensive (eg.
|
||||
|
||||
@@ -2,9 +2,11 @@ ABCp
|
||||
Accellera
|
||||
Affe
|
||||
Aleksander
|
||||
Alexandre
|
||||
Ami
|
||||
Amir
|
||||
Anastasiadis
|
||||
Anikin
|
||||
Antonin
|
||||
Antwerpen
|
||||
Arasanipalai
|
||||
@@ -70,6 +72,7 @@ Eda
|
||||
Eddleston
|
||||
Egbert
|
||||
Egil
|
||||
Ehab
|
||||
Eiler
|
||||
Eivind
|
||||
El
|
||||
@@ -82,6 +85,7 @@ Fabrizio
|
||||
Fekete
|
||||
Ferrandi
|
||||
Flachs
|
||||
Flavien
|
||||
Foletto
|
||||
Forker
|
||||
Francillon
|
||||
@@ -104,6 +108,7 @@ Goessling
|
||||
Gonnen
|
||||
Goorah
|
||||
Gossner
|
||||
Graybeal
|
||||
Grobman
|
||||
Gunter
|
||||
Guo
|
||||
@@ -121,6 +126,7 @@ Hornung
|
||||
Hossell
|
||||
Hsu
|
||||
Hyperthreading
|
||||
Ibrahim
|
||||
Iles
|
||||
Inlines
|
||||
Inout
|
||||
@@ -134,6 +140,7 @@ Jannis
|
||||
Jens
|
||||
Jeras
|
||||
Jiuyang
|
||||
Joannou
|
||||
Joly
|
||||
Jullien
|
||||
Junji
|
||||
@@ -147,6 +154,7 @@ Kamendje
|
||||
Kandadi
|
||||
Kaplan
|
||||
Karge
|
||||
Karlsson
|
||||
Katz
|
||||
Katzman
|
||||
Keren
|
||||
@@ -161,6 +169,7 @@ Kolecki
|
||||
Koonce
|
||||
Korteland
|
||||
Koszek
|
||||
Kouping
|
||||
Kravitz
|
||||
Krolnik
|
||||
Kruse
|
||||
@@ -194,6 +203,7 @@ Mednick
|
||||
Michiels
|
||||
Microsystems
|
||||
Milanovic
|
||||
Millis
|
||||
MinW
|
||||
Mindspeed
|
||||
Miodrag
|
||||
@@ -224,12 +234,14 @@ Petr
|
||||
Piechotka
|
||||
Piersall
|
||||
Plunkett
|
||||
Popolon
|
||||
Popov
|
||||
Prabhat
|
||||
Prabhu
|
||||
Prateek
|
||||
Pre
|
||||
Preprocess
|
||||
Pretet
|
||||
Priyadharshini
|
||||
Pullup
|
||||
Pulver
|
||||
@@ -262,12 +274,15 @@ Shi
|
||||
Shinkarovsky
|
||||
Shirakawa
|
||||
Shuba
|
||||
Shunyao
|
||||
Slatter
|
||||
SoC
|
||||
Sobhan
|
||||
Sokorac
|
||||
Solaris
|
||||
Solomatnikov
|
||||
Solt
|
||||
Southwell
|
||||
Srini
|
||||
Stamness
|
||||
Stephane
|
||||
@@ -286,6 +301,7 @@ SystemVerilog
|
||||
Takatsukasa
|
||||
Tambe
|
||||
Tariq
|
||||
Tejada
|
||||
Tengstrand
|
||||
Terpstra
|
||||
Thiede
|
||||
@@ -293,8 +309,10 @@ Thierry
|
||||
Thyer
|
||||
Tichelaar
|
||||
Tomov
|
||||
Tood
|
||||
Topa
|
||||
Tota
|
||||
Trefor
|
||||
Tresidder
|
||||
Tri
|
||||
Tristate
|
||||
@@ -304,11 +322,13 @@ Ubixum
|
||||
Uints
|
||||
Undefines
|
||||
Unsized
|
||||
Urbach
|
||||
Uselib
|
||||
Usha
|
||||
Usuario
|
||||
VERILATOR
|
||||
Vasu
|
||||
Vdeeptemp
|
||||
Vdly
|
||||
Vemumtab
|
||||
Vemuri
|
||||
@@ -351,7 +371,9 @@ Zhang
|
||||
abirkmanis
|
||||
accessor
|
||||
accessors
|
||||
adrienlemasle
|
||||
agrobman
|
||||
ahouska
|
||||
al
|
||||
ala
|
||||
andit
|
||||
@@ -372,8 +394,12 @@ backtrace
|
||||
backtraces
|
||||
basename
|
||||
bbox
|
||||
benchmarking
|
||||
biguint
|
||||
biops
|
||||
bitOpTree
|
||||
bitOpTree
|
||||
bitop
|
||||
bitstoreal
|
||||
blackbox
|
||||
bokke
|
||||
@@ -389,6 +415,7 @@ casez
|
||||
casted
|
||||
cb
|
||||
ccache
|
||||
ccall
|
||||
cdc
|
||||
ceil
|
||||
celldefine
|
||||
@@ -411,6 +438,8 @@ concat
|
||||
concats
|
||||
config
|
||||
const
|
||||
constexpr
|
||||
constpool
|
||||
coredump
|
||||
countbits
|
||||
countones
|
||||
@@ -426,6 +455,7 @@ da
|
||||
dat
|
||||
datadir
|
||||
datafiles
|
||||
david
|
||||
ddd
|
||||
deassign
|
||||
debugi
|
||||
@@ -434,6 +464,7 @@ defname
|
||||
defparam
|
||||
demangling
|
||||
der
|
||||
dereference
|
||||
desassign
|
||||
destructor
|
||||
detections
|
||||
@@ -462,6 +493,7 @@ dumpvars
|
||||
dut
|
||||
dx
|
||||
elab
|
||||
elike
|
||||
elsif
|
||||
endcase
|
||||
endcelldefine
|
||||
@@ -480,15 +512,18 @@ enums
|
||||
env
|
||||
envvar
|
||||
eof
|
||||
errae
|
||||
erroring
|
||||
et
|
||||
eval
|
||||
evals
|
||||
exe
|
||||
executables
|
||||
expr
|
||||
extern
|
||||
fanin
|
||||
fasttrace
|
||||
fauto
|
||||
fbranch
|
||||
fclose
|
||||
fdisplay
|
||||
@@ -503,6 +538,7 @@ filt
|
||||
flto
|
||||
flushCall
|
||||
fopen
|
||||
forceable
|
||||
foreach
|
||||
fprintf
|
||||
fprofile
|
||||
@@ -521,6 +557,7 @@ funcs
|
||||
fwrite
|
||||
gantt
|
||||
gcc
|
||||
gcda
|
||||
gdb
|
||||
genblk
|
||||
genvar
|
||||
@@ -552,6 +589,7 @@ incdir
|
||||
includer
|
||||
inferfaces
|
||||
inhibitSim
|
||||
initarray
|
||||
initializer
|
||||
initializers
|
||||
inits
|
||||
@@ -570,6 +608,7 @@ killua
|
||||
lang
|
||||
lcov
|
||||
ld
|
||||
leavinel
|
||||
len
|
||||
libext
|
||||
libgoogle
|
||||
@@ -584,6 +623,7 @@ ln
|
||||
loc
|
||||
localparam
|
||||
localparams
|
||||
localtime
|
||||
logicals
|
||||
longint
|
||||
lsb
|
||||
@@ -618,6 +658,7 @@ multithreaded
|
||||
multithreading
|
||||
mutexes
|
||||
mux
|
||||
myftptoyman
|
||||
mysignal
|
||||
namespace
|
||||
nand
|
||||
@@ -642,15 +683,19 @@ onehot
|
||||
ooo
|
||||
oprofile
|
||||
oversubscription
|
||||
parallelized
|
||||
param
|
||||
parameterized
|
||||
params
|
||||
parens
|
||||
pawel
|
||||
pc
|
||||
pdf
|
||||
perf
|
||||
perftools
|
||||
pgo
|
||||
picoChip
|
||||
pinIndex
|
||||
pinout
|
||||
plusargs
|
||||
pmos
|
||||
@@ -676,6 +721,7 @@ prev
|
||||
printf
|
||||
printtimescale
|
||||
profcfunc
|
||||
profiler
|
||||
prototyptes
|
||||
ps
|
||||
pthread
|
||||
@@ -703,12 +749,14 @@ regs
|
||||
reloop
|
||||
resetall
|
||||
respecified
|
||||
rodata
|
||||
rr
|
||||
rst
|
||||
runtime
|
||||
runtimes
|
||||
rw
|
||||
sVerilator
|
||||
sawatzke
|
||||
sc
|
||||
scalared
|
||||
sccanf
|
||||
@@ -724,6 +772,7 @@ specparam
|
||||
splitme
|
||||
spp
|
||||
sqrt
|
||||
src
|
||||
srcdir
|
||||
srcfile
|
||||
sscanf
|
||||
@@ -743,6 +792,7 @@ subcells
|
||||
subexpressions
|
||||
submodule
|
||||
submodules
|
||||
substring
|
||||
sv
|
||||
svBitVal
|
||||
svBitVecVal
|
||||
@@ -777,6 +827,7 @@ trunc
|
||||
txt
|
||||
typ
|
||||
typedef
|
||||
typedef'ed
|
||||
typedefed
|
||||
typedefs
|
||||
typename
|
||||
@@ -790,6 +841,7 @@ uniquified
|
||||
unistd
|
||||
unlink
|
||||
unlinked
|
||||
unnamedblk
|
||||
unopt
|
||||
unoptflat
|
||||
unoptimizable
|
||||
@@ -839,11 +891,14 @@ writeme
|
||||
writemem
|
||||
writememb
|
||||
writememh
|
||||
xiak
|
||||
xin
|
||||
xml
|
||||
xnor
|
||||
xout
|
||||
xuejiazidi
|
||||
yanx
|
||||
ypq
|
||||
yurivict
|
||||
zdave
|
||||
Øyvind
|
||||
|
||||
@@ -33,5 +33,5 @@ add_executable(example ../make_tracing_c/sim_main.cpp)
|
||||
# Add the Verilated circuit to the target
|
||||
verilate(example COVERAGE TRACE
|
||||
INCLUDE_DIRS "../make_tracing_c"
|
||||
VERILATOR_ARGS -f ../make_tracing_c/input.vc -Os -x-assign 0
|
||||
VERILATOR_ARGS -f ../make_tracing_c/input.vc -x-assign fast
|
||||
SOURCES ../make_tracing_c/top.v)
|
||||
|
||||
@@ -45,7 +45,7 @@ set_property(
|
||||
# Add the Verilated circuit to the target
|
||||
verilate(example SYSTEMC COVERAGE TRACE
|
||||
INCLUDE_DIRS "../make_tracing_sc"
|
||||
VERILATOR_ARGS -f ../make_tracing_sc/input.vc -Os -x-assign 0
|
||||
VERILATOR_ARGS -f ../make_tracing_sc/input.vc -x-assign fast
|
||||
SOURCES ../make_tracing_sc/top.v)
|
||||
|
||||
verilator_link_systemc(example)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -33,7 +33,7 @@ VERILATOR_FLAGS =
|
||||
# Generate C++
|
||||
VERILATOR_FLAGS += -cc
|
||||
# Optimize
|
||||
VERILATOR_FLAGS += -Os -x-assign 0
|
||||
VERILATOR_FLAGS += -x-assign fast
|
||||
# Warn abount lint issues; may not want this on less solid designs
|
||||
VERILATOR_FLAGS += -Wall
|
||||
# This example does not use vl_time_stamp but rather
|
||||
|
||||
@@ -36,7 +36,7 @@ VERILATOR_FLAGS += -cc --exe
|
||||
# Generate makefile dependencies (not shown as complicates the Makefile)
|
||||
#VERILATOR_FLAGS += -MMD
|
||||
# Optimize
|
||||
VERILATOR_FLAGS += -Os -x-assign 0
|
||||
VERILATOR_FLAGS += -x-assign fast
|
||||
# Warn abount lint issues; may not want this on less solid designs
|
||||
VERILATOR_FLAGS += -Wall
|
||||
# Make waveforms
|
||||
|
||||
@@ -34,6 +34,8 @@ int main(int argc, char** argv, char** env) {
|
||||
// Using unique_ptr is similar to
|
||||
// "VerilatedContext* contextp = new VerilatedContext" then deleting at end.
|
||||
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
|
||||
// Do not instead make Vtop as a file-scope static variable, as the
|
||||
// "C++ static initialization order fiasco" may cause a crash
|
||||
|
||||
// Set debug level, 0 is off, 9 is highest presently used
|
||||
// May be overridden by commandArgs argument parsing
|
||||
|
||||
@@ -37,7 +37,7 @@ VERILATOR_FLAGS += -sc --exe
|
||||
# Generate makefile dependencies (not shown as complicates the Makefile)
|
||||
#VERILATOR_FLAGS += -MMD
|
||||
# Optimize
|
||||
VERILATOR_FLAGS += -Os -x-assign 0
|
||||
VERILATOR_FLAGS += -x-assign fast
|
||||
# Warn abount lint issues; may not want this on less solid designs
|
||||
VERILATOR_FLAGS += -Wall
|
||||
# Make waveforms
|
||||
|
||||
+106
-44
@@ -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
|
||||
@@ -1453,11 +1464,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 +1618,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 +1852,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 +1986,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 +2168,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",
|
||||
@@ -2289,11 +2276,22 @@ VerilatedContext::VerilatedContext()
|
||||
}
|
||||
|
||||
// Must declare here not in interface, as otherwise forward declarations not known
|
||||
VerilatedContext::~VerilatedContext() {}
|
||||
VerilatedContext::~VerilatedContext() {
|
||||
checkMagic(this);
|
||||
m_magic = 0x1; // Arbitrary but 0x1 is what Verilator src uses for a deleted pointer
|
||||
}
|
||||
|
||||
void VerilatedContext::checkMagic(const VerilatedContext* contextp) {
|
||||
if (VL_UNLIKELY(!contextp || contextp->m_magic != MAGIC)) {
|
||||
VL_FATAL_MT("", 0, "", // LCOV_EXCL_LINE
|
||||
"Attempt to create model using a bad/deleted VerilatedContext pointer");
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -2313,7 +2311,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 "";
|
||||
@@ -2418,6 +2416,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
|
||||
@@ -2448,6 +2473,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
|
||||
|
||||
@@ -2657,6 +2709,7 @@ const VerilatedScopeNameMap* VerilatedContext::scopeNameMap() VL_MT_SAFE {
|
||||
|
||||
VerilatedSyms::VerilatedSyms(VerilatedContext* contextp)
|
||||
: _vm_contextp__(contextp ? contextp : Verilated::threadContextp()) {
|
||||
VerilatedContext::checkMagic(_vm_contextp__);
|
||||
Verilated::threadContextp(_vm_contextp__);
|
||||
#ifdef VL_THREADED
|
||||
__Vm_evalMsgQp = new VerilatedEvalMsgQueue;
|
||||
@@ -2664,6 +2717,7 @@ VerilatedSyms::VerilatedSyms(VerilatedContext* contextp)
|
||||
}
|
||||
|
||||
VerilatedSyms::~VerilatedSyms() {
|
||||
VerilatedContext::checkMagic(_vm_contextp__);
|
||||
#ifdef VL_THREADED
|
||||
delete __Vm_evalMsgQp;
|
||||
#endif
|
||||
@@ -2755,7 +2809,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 {
|
||||
@@ -2838,6 +2892,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
|
||||
|
||||
|
||||
+71
-7
@@ -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;
|
||||
@@ -147,7 +151,7 @@ extern uint32_t VL_THREAD_ID() VL_MT_SAFE;
|
||||
|
||||
#if VL_THREADED
|
||||
|
||||
#define VL_LOCK_SPINS 50000 /// Number of times to spin for a mutex before relaxing
|
||||
#define VL_LOCK_SPINS 50000 /// Number of times to spin for a mutex before yielding
|
||||
|
||||
/// Mutex, wrapped to allow -fthread_safety checks
|
||||
class VL_CAPABILITY("mutex") VerilatedMutex final {
|
||||
@@ -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()
|
||||
inline 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()
|
||||
|
||||
@@ -374,6 +418,10 @@ protected:
|
||||
// List of free descriptors in the MCT region [4, 32)
|
||||
std::vector<IData> m_fdFreeMct VL_GUARDED_BY(m_fdMutex);
|
||||
|
||||
// Magic to check for bad construction
|
||||
static constexpr uint64_t MAGIC = 0xC35F9A6E5298EE6EULL; // SHA256 "VerilatedContext"
|
||||
uint64_t m_magic = MAGIC;
|
||||
|
||||
private:
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedContext);
|
||||
@@ -491,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);
|
||||
@@ -506,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);
|
||||
@@ -535,6 +596,10 @@ public: // But for internal use only
|
||||
// Internal: Serialization setup
|
||||
static constexpr size_t serialized1Size() VL_PURE { return sizeof(m_s); }
|
||||
void* serialized1Ptr() VL_MT_UNSAFE { return &m_s; }
|
||||
|
||||
// Internal: Check magic number
|
||||
static void checkMagic(const VerilatedContext* contextp);
|
||||
void selfTestClearMagic() { m_magic = 0x2; }
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
@@ -822,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)
|
||||
|
||||
+2
-21
@@ -56,6 +56,7 @@ VK_CPPFLAGS_ALWAYS += \
|
||||
-DVM_SC=$(VM_SC) \
|
||||
-DVM_TRACE=$(VM_TRACE) \
|
||||
-DVM_TRACE_FST=$(VM_TRACE_FST) \
|
||||
-DVM_TRACE_VCD=$(VM_TRACE_VCD) \
|
||||
$(CFG_CXXFLAGS_NO_UNUSED) \
|
||||
|
||||
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
|
||||
@@ -141,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
|
||||
@@ -186,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,
|
||||
|
||||
+13
-12
@@ -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;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
@@ -103,7 +106,7 @@ private:
|
||||
using ItemList = std::deque<VerilatedCovImpItem*>;
|
||||
|
||||
// MEMBERS
|
||||
VerilatedMutex m_mutex; // Protects all members
|
||||
mutable VerilatedMutex m_mutex; // Protects all members
|
||||
ValueIndexMap m_valueIndexes VL_GUARDED_BY(m_mutex); // Unique arbitrary value for values
|
||||
IndexValueMap m_indexValues VL_GUARDED_BY(m_mutex); // Unique arbitrary value for keys
|
||||
ItemList m_items VL_GUARDED_BY(m_mutex); // List of all items
|
||||
@@ -122,11 +125,7 @@ public:
|
||||
|
||||
protected:
|
||||
friend class VerilatedCovContext;
|
||||
virtual ~VerilatedCovImp() override { clearGuts(); }
|
||||
static VerilatedCovImp& imp() VL_MT_SAFE {
|
||||
static VerilatedCovImp s_singleton;
|
||||
return s_singleton;
|
||||
}
|
||||
~VerilatedCovImp() override { clearGuts(); }
|
||||
|
||||
private:
|
||||
// PRIVATE METHODS
|
||||
@@ -209,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;
|
||||
@@ -515,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
|
||||
@@ -157,8 +160,8 @@ protected:
|
||||
friend class VerilatedCovImp;
|
||||
// CONSTRUCTORS
|
||||
// Internal: Only made as part of VerilatedCovImp
|
||||
VerilatedCovContext() {}
|
||||
virtual ~VerilatedCovContext() {}
|
||||
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
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
+57
-21
@@ -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
|
||||
@@ -83,15 +83,16 @@ static_assert(static_cast<int>(FST_ST_VCD_PROGRAM) == static_cast<int>(VLT_TRACE
|
||||
//=============================================================================
|
||||
// Specialization of the generics for this trace format
|
||||
|
||||
#define VL_DERIVED_T VerilatedFst
|
||||
#include "verilated_trace_imp.cpp"
|
||||
#undef VL_DERIVED_T
|
||||
#define VL_SUB_T VerilatedFst
|
||||
#define VL_BUF_T VerilatedFstBuffer
|
||||
#include "verilated_trace_imp.h"
|
||||
#undef VL_SUB_T
|
||||
#undef VL_BUF_T
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFst
|
||||
|
||||
VerilatedFst::VerilatedFst(void* fst)
|
||||
: m_fst{fst} {}
|
||||
VerilatedFst::VerilatedFst(void* /*fst*/) {}
|
||||
|
||||
VerilatedFst::~VerilatedFst() {
|
||||
if (m_fst) fstWriterClose(m_fst);
|
||||
@@ -104,14 +105,12 @@ 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();
|
||||
|
||||
VerilatedTrace<VerilatedFst>::traceInit();
|
||||
Super::traceInit();
|
||||
|
||||
// Clear the scope stack
|
||||
auto it = m_curScope.begin();
|
||||
@@ -133,14 +132,14 @@ void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
|
||||
void VerilatedFst::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
VerilatedTrace<VerilatedFst>::closeBase();
|
||||
Super::closeBase();
|
||||
fstWriterClose(m_fst);
|
||||
m_fst = nullptr;
|
||||
}
|
||||
|
||||
void VerilatedFst::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
VerilatedTrace<VerilatedFst>::flushBase();
|
||||
Super::flushBase();
|
||||
fstWriterFlushContext(m_fst);
|
||||
}
|
||||
|
||||
@@ -162,7 +161,7 @@ void VerilatedFst::declare(uint32_t code, const char* name, int dtypenum, fstVar
|
||||
int lsb) {
|
||||
const int bits = ((msb > lsb) ? (msb - lsb) : (lsb - msb)) + 1;
|
||||
|
||||
const bool enabled = VerilatedTrace<VerilatedFst>::declCode(code, name, bits, false);
|
||||
const bool enabled = Super::declCode(code, name, bits, false);
|
||||
if (!enabled) return;
|
||||
|
||||
std::string nameasstr = namePrefix() + name;
|
||||
@@ -245,18 +244,55 @@ void VerilatedFst::declDouble(uint32_t code, const char* name, int dtypenum, fst
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, false, 63, 0);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Get/commit trace buffer
|
||||
|
||||
VerilatedFst::Buffer* VerilatedFst::getTraceBuffer() {
|
||||
#ifdef VL_THREADED
|
||||
if (offload()) return new OffloadBuffer{*this};
|
||||
#endif
|
||||
return new Buffer{*this};
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Configure
|
||||
|
||||
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
|
||||
|
||||
// Note: emit* are only ever called from one place (full* in
|
||||
// verilated_trace_imp.cpp, which is included in this file at the top),
|
||||
// verilated_trace_imp.h, which is included in this file at the top),
|
||||
// so always inline them.
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitBit(uint32_t code, CData newval) {
|
||||
void VerilatedFstBuffer::emitBit(uint32_t code, CData newval) {
|
||||
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], newval ? "1" : "0");
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitCData(uint32_t code, CData newval, int bits) {
|
||||
void VerilatedFstBuffer::emitCData(uint32_t code, CData newval, int bits) {
|
||||
char buf[VL_BYTESIZE];
|
||||
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
|
||||
cvtCDataToStr(buf, newval << (VL_BYTESIZE - bits));
|
||||
@@ -264,7 +300,7 @@ void VerilatedFst::emitCData(uint32_t code, CData newval, int bits) {
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitSData(uint32_t code, SData newval, int bits) {
|
||||
void VerilatedFstBuffer::emitSData(uint32_t code, SData newval, int bits) {
|
||||
char buf[VL_SHORTSIZE];
|
||||
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
|
||||
cvtSDataToStr(buf, newval << (VL_SHORTSIZE - bits));
|
||||
@@ -272,7 +308,7 @@ void VerilatedFst::emitSData(uint32_t code, SData newval, int bits) {
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitIData(uint32_t code, IData newval, int bits) {
|
||||
void VerilatedFstBuffer::emitIData(uint32_t code, IData newval, int bits) {
|
||||
char buf[VL_IDATASIZE];
|
||||
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
|
||||
cvtIDataToStr(buf, newval << (VL_IDATASIZE - bits));
|
||||
@@ -280,7 +316,7 @@ void VerilatedFst::emitIData(uint32_t code, IData newval, int bits) {
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitQData(uint32_t code, QData newval, int bits) {
|
||||
void VerilatedFstBuffer::emitQData(uint32_t code, QData newval, int bits) {
|
||||
char buf[VL_QUADSIZE];
|
||||
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
|
||||
cvtQDataToStr(buf, newval << (VL_QUADSIZE - bits));
|
||||
@@ -288,7 +324,7 @@ void VerilatedFst::emitQData(uint32_t code, QData newval, int bits) {
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitWData(uint32_t code, const WData* newvalp, int bits) {
|
||||
void VerilatedFstBuffer::emitWData(uint32_t code, const WData* newvalp, int bits) {
|
||||
int words = VL_WORDS_I(bits);
|
||||
char* wp = m_strbuf;
|
||||
// Convert the most significant word
|
||||
@@ -304,6 +340,6 @@ void VerilatedFst::emitWData(uint32_t code, const WData* newvalp, int bits) {
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitDouble(uint32_t code, double newval) {
|
||||
void VerilatedFstBuffer::emitDouble(uint32_t code, double newval) {
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], &newval);
|
||||
}
|
||||
|
||||
+75
-29
@@ -31,26 +31,32 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class VerilatedFstBuffer;
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFst
|
||||
// Base class to create a Verilator FST dump
|
||||
// This is an internally used class - see VerilatedFstC for what to call from applications
|
||||
|
||||
class VerilatedFst final : public VerilatedTrace<VerilatedFst> {
|
||||
class VerilatedFst final : public VerilatedTrace<VerilatedFst, VerilatedFstBuffer> {
|
||||
public:
|
||||
using Super = VerilatedTrace<VerilatedFst, VerilatedFstBuffer>;
|
||||
|
||||
private:
|
||||
// Give the superclass access to private bits (to avoid virtual functions)
|
||||
friend class VerilatedTrace<VerilatedFst>;
|
||||
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,
|
||||
@@ -60,31 +66,29 @@ protected:
|
||||
//=========================================================================
|
||||
// Implementation of VerilatedTrace interface
|
||||
|
||||
// Implementations of protected virtual methods for VerilatedTrace
|
||||
virtual void emitTimeChange(uint64_t timeui) override;
|
||||
// Called when the trace moves forward to a new time point
|
||||
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(); }
|
||||
|
||||
// Implementations of duck-typed methods for VerilatedTrace. These are
|
||||
// called from only one place (namely full*) so always inline them.
|
||||
inline void emitBit(uint32_t code, CData newval);
|
||||
inline void emitCData(uint32_t code, CData newval, int bits);
|
||||
inline void emitSData(uint32_t code, SData newval, int bits);
|
||||
inline void emitIData(uint32_t code, IData newval, int bits);
|
||||
inline void emitQData(uint32_t code, QData newval, int bits);
|
||||
inline void emitWData(uint32_t code, const WData* newvalp, int bits);
|
||||
inline void emitDouble(uint32_t code, double newval);
|
||||
// Trace buffer management
|
||||
Buffer* getTraceBuffer() override;
|
||||
void commitTraceBuffer(Buffer*) override;
|
||||
|
||||
// Configure sub-class
|
||||
void configure(const VerilatedTraceConfig&) override;
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
// External interface to client code
|
||||
// (All must be threadsafe)
|
||||
|
||||
// CONSTRUCTOR
|
||||
explicit VerilatedFst(void* fst = nullptr);
|
||||
~VerilatedFst();
|
||||
|
||||
// METHODS - All must be thread safe
|
||||
// Open the file; call isOpen() to see if errors
|
||||
void open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
// Close the file
|
||||
@@ -97,11 +101,6 @@ public:
|
||||
//=========================================================================
|
||||
// Internal interface to Verilator generated code
|
||||
|
||||
// Inside dumping routines, declare a data type
|
||||
void declDTypeEnum(int dtypenum, const char* name, uint32_t elements, unsigned int minValbits,
|
||||
const char** itemNamesp, const char** itemValuesp);
|
||||
|
||||
// Inside dumping routines, declare a signal
|
||||
void declBit(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum);
|
||||
void declBus(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
@@ -112,18 +111,65 @@ public:
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb);
|
||||
void declDouble(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum);
|
||||
|
||||
void declDTypeEnum(int dtypenum, const char* name, uint32_t elements, unsigned int minValbits,
|
||||
const char** itemNamesp, const char** itemValuesp);
|
||||
};
|
||||
|
||||
#ifndef DOXYGEN
|
||||
// Declare specialization here as it's used in VerilatedFstC just below
|
||||
template <> void VerilatedTrace<VerilatedFst>::dump(uint64_t timeui);
|
||||
template <> void VerilatedTrace<VerilatedFst>::set_time_unit(const char* unitp);
|
||||
template <> void VerilatedTrace<VerilatedFst>::set_time_unit(const std::string& unit);
|
||||
template <> void VerilatedTrace<VerilatedFst>::set_time_resolution(const char* unitp);
|
||||
template <> void VerilatedTrace<VerilatedFst>::set_time_resolution(const std::string& unit);
|
||||
template <> void VerilatedTrace<VerilatedFst>::dumpvars(int level, const std::string& hier);
|
||||
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 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;
|
||||
// code to fstHande map, as an array
|
||||
const fstHandle* const m_symbolp = m_owner.m_symbolp;
|
||||
// String buffer long enough to hold maxBits() chars
|
||||
char* const m_strbuf = m_owner.m_strbuf;
|
||||
|
||||
// CONSTRUCTOR
|
||||
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);
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFstC
|
||||
/// Create a FST dump file in C standalone (no SystemC) simulations.
|
||||
|
||||
@@ -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 )
|
||||
|
||||
@@ -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,7 +52,7 @@ public:
|
||||
}
|
||||
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
|
||||
}
|
||||
virtual ~VerilatedFstSc() { close(); }
|
||||
virtual ~VerilatedFstSc() /*override*/ { close(); }
|
||||
|
||||
// METHODS
|
||||
/// Called by SystemC simulate()
|
||||
@@ -60,6 +61,7 @@ public:
|
||||
}
|
||||
|
||||
// Override VerilatedFstC. Must be called after starting simulation.
|
||||
// cppcheck-suppress missingOverride // GCC won't accept override
|
||||
virtual void open(const char* filename) /*override*/ VL_MT_SAFE;
|
||||
|
||||
private:
|
||||
@@ -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 )
|
||||
|
||||
@@ -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
|
||||
|
||||
+17
-22
@@ -91,7 +91,7 @@ class VerilatedEvalMsgQueue final {
|
||||
|
||||
std::atomic<uint64_t> m_depth; // Current depth of queue (see comments below)
|
||||
|
||||
VerilatedMutex m_mutex; // Mutex protecting queue
|
||||
mutable VerilatedMutex m_mutex; // Mutex protecting queue
|
||||
VerilatedThreadQueue m_queue VL_GUARDED_BY(m_mutex); // Message queue
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -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
|
||||
@@ -464,7 +461,6 @@ 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
|
||||
@@ -486,7 +482,8 @@ public:
|
||||
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> 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,13 +34,14 @@
|
||||
#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
|
||||
@@ -136,7 +133,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,8 +151,9 @@ 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
|
||||
VerilatedMutex m_mutex;
|
||||
mutable VerilatedMutex m_mutex;
|
||||
// Map from thread id to &t_trace of given thread
|
||||
std::map<uint32_t, ExecutionTrace*> m_traceps VL_GUARDED_BY(m_mutex);
|
||||
|
||||
@@ -167,31 +165,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; }
|
||||
// Append a trace record to the trace buffer of the current thread
|
||||
inline VlExecutionRecord& addRecord() {
|
||||
static inline 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
|
||||
@@ -228,7 +231,7 @@ void VlPgoProfiler<T_Entries>::write(const char* modelp, const std::string& file
|
||||
// So when we have multiple models in an executable, possibly even
|
||||
// running on different threads, each will have a different symtab so
|
||||
// each will collect is own data correctly. However when each is
|
||||
// destroid we need to get all the data, not keep overwriting and only
|
||||
// destroyed we need to get all the data, not keep overwriting and only
|
||||
// get the last model's data.
|
||||
static bool s_firstCall = true;
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
+24
-26
@@ -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.
|
||||
@@ -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}
|
||||
@@ -150,7 +148,7 @@ private:
|
||||
};
|
||||
|
||||
// MEMBERS
|
||||
VerilatedMutex m_mutex;
|
||||
mutable VerilatedMutex m_mutex;
|
||||
std::condition_variable_any m_cv;
|
||||
// Only notify the condition_variable if the worker is waiting
|
||||
bool m_waiting VL_GUARDED_BY(m_mutex) = false;
|
||||
@@ -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
|
||||
template <bool SpinWait>
|
||||
inline 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)
|
||||
inline 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,8 +219,8 @@ 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(); }
|
||||
|
||||
+267
-121
@@ -28,26 +28,35 @@
|
||||
#include "verilated_trace_defs.h"
|
||||
|
||||
#include <bitset>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
# include <condition_variable>
|
||||
#ifdef VL_THREADED
|
||||
# include <deque>
|
||||
# include <thread>
|
||||
#endif
|
||||
|
||||
// clang-format on
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
class VlThreadPool;
|
||||
template <class T_Buffer>
|
||||
class VerilatedTraceBuffer;
|
||||
template <class T_Buffer>
|
||||
class VerilatedTraceOffloadBuffer;
|
||||
|
||||
#ifdef VL_THREADED
|
||||
//=============================================================================
|
||||
// Threaded tracing
|
||||
// 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:
|
||||
VerilatedMutex m_mutex; // Protects m_queue
|
||||
mutable VerilatedMutex m_mutex; // Protects m_queue
|
||||
std::condition_variable_any m_cv;
|
||||
std::deque<T> m_queue VL_GUARDED_BY(m_mutex);
|
||||
|
||||
@@ -88,7 +97,7 @@ public:
|
||||
|
||||
// Commands used by thread tracing. Anonymous enum in class, as we want
|
||||
// it scoped, but we also want the automatic conversion to integer types.
|
||||
class VerilatedTraceCommand final {
|
||||
class VerilatedTraceOffloadCommand final {
|
||||
public:
|
||||
// These must all fit in 4 bit at the moment, as the tracing routines
|
||||
// pack parameters in the top bits.
|
||||
@@ -102,36 +111,65 @@ public:
|
||||
CHG_WDATA = 0x6,
|
||||
CHG_DOUBLE = 0x8,
|
||||
// TODO: full..
|
||||
TIME_CHANGE = 0xd,
|
||||
TIME_CHANGE = 0xc,
|
||||
TRACE_BUFFER = 0xd,
|
||||
END = 0xe, // End of buffer
|
||||
SHUTDOWN = 0xf // Shutdown worker thread, also marks end of buffer
|
||||
};
|
||||
};
|
||||
#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
|
||||
|
||||
// VerilatedTrace uses F-bounded polymorphism to access duck-typed
|
||||
// implementations in the format specific derived class, which must be passed
|
||||
// as the type parameter T_Derived
|
||||
template <class T_Derived> class VerilatedTrace VL_NOT_FINAL {
|
||||
// T_Trace is the format specific subclass of VerilatedTrace.
|
||||
// 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 = VerilatedTraceBuffer<T_Buffer>;
|
||||
using OffloadBuffer = VerilatedTraceOffloadBuffer<T_Buffer>;
|
||||
|
||||
//=========================================================================
|
||||
// Generic tracing internals
|
||||
|
||||
using initCb_t = void (*)(void*, T_Derived*, uint32_t); // Type of init callbacks
|
||||
using dumpCb_t = void (*)(void*, T_Derived*); // Type of all but init callbacks
|
||||
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
|
||||
// assignment operator on inserting into collections, so they don't work
|
||||
// with const fields...
|
||||
union {
|
||||
initCb_t m_initCb; // The callback function
|
||||
dumpCb_t m_dumpCb; // The callback function
|
||||
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)
|
||||
CallbackRecord(initCb_t cb, void* userp)
|
||||
@@ -140,65 +178,110 @@ 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} {}
|
||||
};
|
||||
|
||||
uint32_t* m_sigs_oldvalp; // Old value store
|
||||
EData* m_sigs_enabledp; // Bit vector of enabled codes (nullptr = all on)
|
||||
uint64_t m_timeLastDump; // Last time we did a dump
|
||||
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
|
||||
Buffer* const m_bufp; // The buffer pointer to pass to the callback
|
||||
std::atomic<bool> m_ready{false}; // The ready flag
|
||||
mutable VerilatedMutex m_mutex; // Mutex for suspension until ready
|
||||
std::condition_variable_any m_cv; // Condition variable for suspension
|
||||
bool m_waiting VL_GUARDED_BY(m_mutex) = false; // Whether a thread is suspended in wait()
|
||||
|
||||
void wait();
|
||||
|
||||
ParallelWorkerData(dumpCb_t cb, void* userp, Buffer* bufp)
|
||||
: m_cb{cb}
|
||||
, m_userp{userp}
|
||||
, m_bufp{bufp} {}
|
||||
};
|
||||
|
||||
// Passed a ParallelWorkerData*, second argument is ignored
|
||||
static void parallelWorkerTask(void*, bool);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
uint32_t* m_sigs_oldvalp = nullptr; // Previous value store
|
||||
EData* m_sigs_enabledp = nullptr; // Bit vector of enabled codes (nullptr = all on)
|
||||
private:
|
||||
std::vector<bool> m_sigs_enabledVec; // Staging for m_sigs_enabledp
|
||||
std::vector<CallbackRecord> m_initCbs; // Routines to initialize traciong
|
||||
std::vector<CallbackRecord> m_initCbs; // Routines to initialize tracing
|
||||
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
|
||||
bool m_fullDump; // Whether a full dump is required on the next call to 'dump'
|
||||
uint32_t m_nextCode; // Next code number to assign
|
||||
uint32_t m_numSignals; // Number of distinct signals
|
||||
uint32_t m_maxBits; // Number of bits in the widest signal
|
||||
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
|
||||
uint32_t m_maxBits = 0; // Number of bits in the widest signal
|
||||
std::vector<std::string> m_namePrefixStack{""}; // Path prefixes to add to signal names
|
||||
std::vector<std::pair<int, std::string>> m_dumpvars; // dumpvar() entries
|
||||
char m_scopeEscape;
|
||||
double m_timeRes; // Time resolution (ns/ms etc)
|
||||
double m_timeUnit; // Time units (ns/ms etc)
|
||||
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 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_Derived*'. Use 'self()->'
|
||||
// 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_Derived* self() { return static_cast<T_Derived*>(this); }
|
||||
T_Trace* self() { return static_cast<T_Trace*>(this); }
|
||||
|
||||
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_THREADED
|
||||
// Number of total trace buffers that have been allocated
|
||||
uint32_t m_numTraceBuffers;
|
||||
// Size of trace buffers
|
||||
size_t m_traceBufferSize;
|
||||
#ifdef VL_THREADED
|
||||
// Number of total offload buffers that have been allocated
|
||||
uint32_t m_numOffloadBuffers = 0;
|
||||
// Size of offload buffers
|
||||
size_t m_offloadBufferSize = 0;
|
||||
// Buffers handed to worker for processing
|
||||
VerilatedThreadQueue<uint32_t*> m_buffersToWorker;
|
||||
VerilatedThreadQueue<uint32_t*> m_offloadBuffersToWorker;
|
||||
// Buffers returned from worker after processing
|
||||
VerilatedThreadQueue<uint32_t*> m_buffersFromWorker;
|
||||
VerilatedThreadQueue<uint32_t*> m_offloadBuffersFromWorker;
|
||||
|
||||
protected:
|
||||
// Write pointer into current buffer
|
||||
uint32_t* m_traceBufferWritep;
|
||||
// End of trace buffer
|
||||
uint32_t* m_traceBufferEndp;
|
||||
// The worker thread itself
|
||||
uint32_t* m_offloadBufferWritep = nullptr;
|
||||
// End of offload buffer
|
||||
uint32_t* m_offloadBufferEndp = nullptr;
|
||||
|
||||
private:
|
||||
// The offload worker thread itself
|
||||
std::unique_ptr<std::thread> m_workerThread;
|
||||
|
||||
// Get a new trace buffer that can be populated. May block if none available
|
||||
uint32_t* getTraceBuffer();
|
||||
// Get a new offload buffer that can be populated. May block if none available
|
||||
uint32_t* getOffloadBuffer();
|
||||
|
||||
// The function executed by the worker thread
|
||||
void workerThreadMain();
|
||||
// The function executed by the offload worker thread
|
||||
void offloadWorkerThreadMain();
|
||||
|
||||
// Wait until given buffer is placed in m_buffersFromWorker
|
||||
void waitForBuffer(const uint32_t* bufferp);
|
||||
// Wait until given offload buffer is placed in m_offloadBuffersFromWorker
|
||||
void waitForOffloadBuffer(const uint32_t* bufferp);
|
||||
|
||||
// Shut down and join worker, if it's running, otherwise do nothing
|
||||
void shutdownWorker();
|
||||
void shutdownOffloadWorker();
|
||||
#endif
|
||||
|
||||
// CONSTRUCTORS
|
||||
@@ -208,13 +291,12 @@ protected:
|
||||
//=========================================================================
|
||||
// Internals available to format specific implementations
|
||||
|
||||
VerilatedMutex m_mutex; // Ensure dump() etc only called from single thread
|
||||
mutable VerilatedMutex m_mutex; // Ensure dump() etc only called from single thread
|
||||
|
||||
uint32_t nextCode() const { return m_nextCode; }
|
||||
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; }
|
||||
@@ -235,6 +317,14 @@ protected:
|
||||
void closeBase();
|
||||
void flushBase();
|
||||
|
||||
#ifdef VL_THREADED
|
||||
inline bool offload() const { return m_offload; }
|
||||
inline 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
|
||||
|
||||
@@ -246,6 +336,13 @@ protected:
|
||||
virtual bool preFullDump() = 0;
|
||||
virtual bool preChangeDump() = 0;
|
||||
|
||||
// Trace buffer management
|
||||
virtual Buffer* getTraceBuffer() = 0;
|
||||
virtual void commitTraceBuffer(Buffer*) = 0;
|
||||
|
||||
// Configure sub-class
|
||||
virtual void configure(const VerilatedTraceConfig&) = 0;
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
// External interface to client code
|
||||
@@ -266,19 +363,51 @@ public:
|
||||
// Call
|
||||
void dump(uint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
|
||||
//=========================================================================
|
||||
// Internal interface to Verilator generated code
|
||||
|
||||
//=========================================================================
|
||||
// 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) VL_MT_SAFE;
|
||||
void addFullCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE;
|
||||
void addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE;
|
||||
void addCleanupCb(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; }
|
||||
|
||||
void pushNamePrefix(const std::string&);
|
||||
void popNamePrefix(unsigned count = 1);
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// 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_Buffer>
|
||||
class VerilatedTraceBuffer VL_NOT_FINAL : public T_Buffer {
|
||||
protected:
|
||||
// Type of the owner trace file
|
||||
using Trace = typename std::remove_cv<
|
||||
typename std::remove_reference<decltype(T_Buffer::m_owner)>::type>::type;
|
||||
|
||||
static_assert(std::has_virtual_destructor<T_Buffer>::value, "");
|
||||
static_assert(std::is_base_of<VerilatedTrace<Trace, T_Buffer>, Trace>::value, "");
|
||||
|
||||
friend Trace; // Give the trace file access to the private bits
|
||||
friend std::default_delete<VerilatedTraceBuffer<T_Buffer>>;
|
||||
|
||||
uint32_t* const m_sigs_oldvalp; // Previous value store
|
||||
EData* const m_sigs_enabledp; // Bit vector of enabled codes (nullptr = all on)
|
||||
|
||||
explicit VerilatedTraceBuffer(Trace& owner);
|
||||
~VerilatedTraceBuffer() override = default;
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
// Hot path internal interface to Verilator generated code
|
||||
|
||||
@@ -296,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;
|
||||
|
||||
uint32_t* oldp(uint32_t code) { return m_sigs_oldvalp + code; }
|
||||
VL_ATTR_ALWINLINE inline 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);
|
||||
@@ -307,89 +436,32 @@ public:
|
||||
void fullWData(uint32_t* oldp, const WData* newvalp, int bits);
|
||||
void fullDouble(uint32_t* oldp, double newval);
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
// Threaded tracing. Just dump everything in the trace buffer
|
||||
inline void chgBit(uint32_t code, CData newval) {
|
||||
m_traceBufferWritep[0] = VerilatedTraceCommand::CHG_BIT_0 | newval;
|
||||
m_traceBufferWritep[1] = code;
|
||||
m_traceBufferWritep += 2;
|
||||
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
|
||||
}
|
||||
inline void chgCData(uint32_t code, CData newval, int bits) {
|
||||
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_CDATA;
|
||||
m_traceBufferWritep[1] = code;
|
||||
m_traceBufferWritep[2] = newval;
|
||||
m_traceBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
|
||||
}
|
||||
inline void chgSData(uint32_t code, SData newval, int bits) {
|
||||
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_SDATA;
|
||||
m_traceBufferWritep[1] = code;
|
||||
m_traceBufferWritep[2] = newval;
|
||||
m_traceBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
|
||||
}
|
||||
inline void chgIData(uint32_t code, IData newval, int bits) {
|
||||
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_IDATA;
|
||||
m_traceBufferWritep[1] = code;
|
||||
m_traceBufferWritep[2] = newval;
|
||||
m_traceBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
|
||||
}
|
||||
inline void chgQData(uint32_t code, QData newval, int bits) {
|
||||
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_QDATA;
|
||||
m_traceBufferWritep[1] = code;
|
||||
*reinterpret_cast<QData*>(m_traceBufferWritep + 2) = newval;
|
||||
m_traceBufferWritep += 4;
|
||||
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
|
||||
}
|
||||
inline void chgWData(uint32_t code, const WData* newvalp, int bits) {
|
||||
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_WDATA;
|
||||
m_traceBufferWritep[1] = code;
|
||||
m_traceBufferWritep += 2;
|
||||
for (int i = 0; i < (bits + 31) / 32; ++i) { *m_traceBufferWritep++ = newvalp[i]; }
|
||||
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
|
||||
}
|
||||
inline void chgDouble(uint32_t code, double newval) {
|
||||
m_traceBufferWritep[0] = VerilatedTraceCommand::CHG_DOUBLE;
|
||||
m_traceBufferWritep[1] = code;
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
*reinterpret_cast<double*>(m_traceBufferWritep + 2) = newval;
|
||||
m_traceBufferWritep += 4;
|
||||
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
|
||||
}
|
||||
|
||||
#define CHG(name) chg##name##Impl
|
||||
#else
|
||||
#define CHG(name) chg##name
|
||||
#endif
|
||||
|
||||
// In non-threaded mode, these are called directly by the trace callbacks,
|
||||
// and are called chg*. In threaded mode, they are called by the worker
|
||||
// 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
|
||||
inline void CHG(Bit)(uint32_t* oldp, CData newval) {
|
||||
VL_ATTR_ALWINLINE inline void chgBit(uint32_t* oldp, CData newval) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullBit(oldp, newval);
|
||||
}
|
||||
inline void CHG(CData)(uint32_t* oldp, CData newval, int bits) {
|
||||
VL_ATTR_ALWINLINE inline void chgCData(uint32_t* oldp, CData newval, int bits) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullCData(oldp, newval, bits);
|
||||
}
|
||||
inline void CHG(SData)(uint32_t* oldp, SData newval, int bits) {
|
||||
VL_ATTR_ALWINLINE inline void chgSData(uint32_t* oldp, SData newval, int bits) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullSData(oldp, newval, bits);
|
||||
}
|
||||
inline void CHG(IData)(uint32_t* oldp, IData newval, int bits) {
|
||||
VL_ATTR_ALWINLINE inline void chgIData(uint32_t* oldp, IData newval, int bits) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullIData(oldp, newval, bits);
|
||||
}
|
||||
inline void CHG(QData)(uint32_t* oldp, QData newval, int bits) {
|
||||
VL_ATTR_ALWINLINE inline 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);
|
||||
}
|
||||
inline void CHG(WData)(uint32_t* oldp, const WData* newvalp, int bits) {
|
||||
VL_ATTR_ALWINLINE inline 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);
|
||||
@@ -397,11 +469,85 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void CHG(Double)(uint32_t* oldp, double newval) {
|
||||
VL_ATTR_ALWINLINE inline void chgDouble(uint32_t* oldp, double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
if (VL_UNLIKELY(*reinterpret_cast<double*>(oldp) != newval)) fullDouble(oldp, newval);
|
||||
}
|
||||
|
||||
#undef CHG
|
||||
};
|
||||
|
||||
#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
|
||||
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););
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // guard
|
||||
|
||||
@@ -10,32 +10,32 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//=============================================================================
|
||||
///
|
||||
/// \file
|
||||
/// \brief Verilated common-format tracing implementation code
|
||||
///
|
||||
/// This file must be compiled and linked against all Verilated objects
|
||||
/// that use --trace.
|
||||
///
|
||||
/// Use "verilator --trace" to add this to the Makefile for the linker.
|
||||
///
|
||||
//
|
||||
// Verilated tracing implementation code template common to all formats.
|
||||
// This file is included by the format specific implementations and
|
||||
// should not be used otherwise.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
// clang-format off
|
||||
|
||||
#ifndef VL_CPPCHECK
|
||||
#ifndef VL_DERIVED_T
|
||||
#if !defined(VL_SUB_T) || !defined(VL_BUF_T)
|
||||
# error "This file should be included in trace format implementations"
|
||||
#endif
|
||||
|
||||
#include "verilated_intrinsics.h"
|
||||
#include "verilated_trace.h"
|
||||
#ifdef VL_THREADED
|
||||
# include "verilated_threads.h"
|
||||
# include <list>
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
# include <iostream>
|
||||
# define VL_TRACE_THREAD_DEBUG(msg) std::cout << "TRACE THREAD: " << msg << std::endl
|
||||
# define VL_TRACE_OFFLOAD_DEBUG(msg) std::cout << "TRACE OFFLOAD THREAD: " << msg << std::endl
|
||||
#else
|
||||
# define VL_TRACE_THREAD_DEBUG(msg)
|
||||
# define VL_TRACE_OFFLOAD_DEBUG(msg)
|
||||
#endif
|
||||
|
||||
// clang-format on
|
||||
@@ -78,37 +78,39 @@ static std::string doubleToTimescale(double value) {
|
||||
return valuestr; // Gets converted to string, so no ref to stack
|
||||
}
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
#ifdef VL_THREADED
|
||||
//=========================================================================
|
||||
// Buffer management
|
||||
|
||||
template <> uint32_t* VerilatedTrace<VL_DERIVED_T>::getTraceBuffer() {
|
||||
template <>
|
||||
uint32_t* VerilatedTrace<VL_SUB_T, VL_BUF_T>::getOffloadBuffer() {
|
||||
uint32_t* bufferp;
|
||||
// Some jitter is expected, so some number of alternative trace buffers are
|
||||
// Some jitter is expected, so some number of alternative offlaod buffers are
|
||||
// required, but don't allocate more than 8 buffers.
|
||||
if (m_numTraceBuffers < 8) {
|
||||
if (m_numOffloadBuffers < 8) {
|
||||
// Allocate a new buffer if none is available
|
||||
if (!m_buffersFromWorker.tryGet(bufferp)) {
|
||||
++m_numTraceBuffers;
|
||||
if (!m_offloadBuffersFromWorker.tryGet(bufferp)) {
|
||||
++m_numOffloadBuffers;
|
||||
// Note: over allocate a bit so pointer comparison is well defined
|
||||
// if we overflow only by a small amount
|
||||
bufferp = new uint32_t[m_traceBufferSize + 16];
|
||||
bufferp = new uint32_t[m_offloadBufferSize + 16];
|
||||
}
|
||||
} else {
|
||||
// Block until a buffer becomes available
|
||||
bufferp = m_buffersFromWorker.get();
|
||||
bufferp = m_offloadBuffersFromWorker.get();
|
||||
}
|
||||
return bufferp;
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::waitForBuffer(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;
|
||||
do { stash.push_back(m_buffersFromWorker.get()); } while (stash.back() != buffp);
|
||||
do { stash.push_back(m_offloadBuffersFromWorker.get()); } while (stash.back() != buffp);
|
||||
// Now put them back in the queue, in the original order.
|
||||
while (!stash.empty()) {
|
||||
m_buffersFromWorker.put_front(stash.back());
|
||||
m_offloadBuffersFromWorker.put_front(stash.back());
|
||||
stash.pop_back();
|
||||
}
|
||||
}
|
||||
@@ -116,17 +118,20 @@ template <> void VerilatedTrace<VL_DERIVED_T>::waitForBuffer(const uint32_t* buf
|
||||
//=========================================================================
|
||||
// Worker thread
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::workerThreadMain() {
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
|
||||
bool shutdown = false;
|
||||
|
||||
do {
|
||||
uint32_t* const bufferp = m_buffersToWorker.get();
|
||||
uint32_t* const bufferp = m_offloadBuffersToWorker.get();
|
||||
|
||||
VL_TRACE_THREAD_DEBUG("");
|
||||
VL_TRACE_THREAD_DEBUG("Got buffer: " << bufferp);
|
||||
VL_TRACE_OFFLOAD_DEBUG("");
|
||||
VL_TRACE_OFFLOAD_DEBUG("Got buffer: " << bufferp);
|
||||
|
||||
const uint32_t* readp = bufferp;
|
||||
|
||||
std::unique_ptr<Buffer> traceBufp; // We own the passed tracebuffer
|
||||
|
||||
while (true) {
|
||||
const uint32_t cmd = readp[0];
|
||||
const uint32_t top = cmd >> 4;
|
||||
@@ -139,71 +144,79 @@ template <> void VerilatedTrace<VL_DERIVED_T>::workerThreadMain() {
|
||||
switch (cmd & 0xF) {
|
||||
//===
|
||||
// CHG_* commands
|
||||
case VerilatedTraceCommand::CHG_BIT_0:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_BIT_0 " << top);
|
||||
chgBitImpl(oldp, 0);
|
||||
case VerilatedTraceOffloadCommand::CHG_BIT_0:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_BIT_0 " << top);
|
||||
traceBufp->chgBit(oldp, 0);
|
||||
continue;
|
||||
case VerilatedTraceCommand::CHG_BIT_1:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_BIT_1 " << top);
|
||||
chgBitImpl(oldp, 1);
|
||||
case VerilatedTraceOffloadCommand::CHG_BIT_1:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_BIT_1 " << top);
|
||||
traceBufp->chgBit(oldp, 1);
|
||||
continue;
|
||||
case VerilatedTraceCommand::CHG_CDATA:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_CDATA " << top);
|
||||
case VerilatedTraceOffloadCommand::CHG_CDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_CDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
chgCDataImpl(oldp, *readp, top);
|
||||
traceBufp->chgCData(oldp, *readp, top);
|
||||
readp += 1;
|
||||
continue;
|
||||
case VerilatedTraceCommand::CHG_SDATA:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_SDATA " << top);
|
||||
case VerilatedTraceOffloadCommand::CHG_SDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_SDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
chgSDataImpl(oldp, *readp, top);
|
||||
traceBufp->chgSData(oldp, *readp, top);
|
||||
readp += 1;
|
||||
continue;
|
||||
case VerilatedTraceCommand::CHG_IDATA:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_IDATA " << top);
|
||||
case VerilatedTraceOffloadCommand::CHG_IDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_IDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
chgIDataImpl(oldp, *readp, top);
|
||||
traceBufp->chgIData(oldp, *readp, top);
|
||||
readp += 1;
|
||||
continue;
|
||||
case VerilatedTraceCommand::CHG_QDATA:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_QDATA " << top);
|
||||
case VerilatedTraceOffloadCommand::CHG_QDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_QDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
chgQDataImpl(oldp, *reinterpret_cast<const QData*>(readp), top);
|
||||
traceBufp->chgQData(oldp, *reinterpret_cast<const QData*>(readp), top);
|
||||
readp += 2;
|
||||
continue;
|
||||
case VerilatedTraceCommand::CHG_WDATA:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_WDATA " << top);
|
||||
chgWDataImpl(oldp, readp, top);
|
||||
case VerilatedTraceOffloadCommand::CHG_WDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_WDATA " << top);
|
||||
traceBufp->chgWData(oldp, readp, top);
|
||||
readp += VL_WORDS_I(top);
|
||||
continue;
|
||||
case VerilatedTraceCommand::CHG_DOUBLE:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_DOUBLE " << top);
|
||||
chgDoubleImpl(oldp, *reinterpret_cast<const double*>(readp));
|
||||
case VerilatedTraceOffloadCommand::CHG_DOUBLE:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_DOUBLE " << top);
|
||||
traceBufp->chgDouble(oldp, *reinterpret_cast<const double*>(readp));
|
||||
readp += 2;
|
||||
continue;
|
||||
|
||||
//===
|
||||
// Rare commands
|
||||
case VerilatedTraceCommand::TIME_CHANGE:
|
||||
VL_TRACE_THREAD_DEBUG("Command TIME_CHANGE " << top);
|
||||
case VerilatedTraceOffloadCommand::TIME_CHANGE:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command TIME_CHANGE " << top);
|
||||
readp -= 1; // No code in this command, undo increment
|
||||
emitTimeChange(*reinterpret_cast<const uint64_t*>(readp));
|
||||
readp += 2;
|
||||
continue;
|
||||
|
||||
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<Buffer* const*>(readp));
|
||||
readp += 2;
|
||||
continue;
|
||||
|
||||
//===
|
||||
// Commands ending this buffer
|
||||
case VerilatedTraceCommand::END: VL_TRACE_THREAD_DEBUG("Command END"); break;
|
||||
case VerilatedTraceCommand::SHUTDOWN:
|
||||
VL_TRACE_THREAD_DEBUG("Command SHUTDOWN");
|
||||
case VerilatedTraceOffloadCommand::END: //
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command END");
|
||||
break;
|
||||
case VerilatedTraceOffloadCommand::SHUTDOWN:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command SHUTDOWN");
|
||||
shutdown = true;
|
||||
break;
|
||||
|
||||
//===
|
||||
// Unknown command
|
||||
default: { // LCOV_EXCL_START
|
||||
VL_TRACE_THREAD_DEBUG("Command UNKNOWN");
|
||||
VL_PRINTF_MT("Trace command: 0x%08x\n", cmd);
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command UNKNOWN " << cmd);
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Unknown trace command");
|
||||
break;
|
||||
} // LCOV_EXCL_STOP
|
||||
@@ -214,23 +227,24 @@ template <> void VerilatedTrace<VL_DERIVED_T>::workerThreadMain() {
|
||||
break;
|
||||
}
|
||||
|
||||
VL_TRACE_THREAD_DEBUG("Returning buffer");
|
||||
VL_TRACE_OFFLOAD_DEBUG("Returning buffer");
|
||||
|
||||
// Return buffer
|
||||
m_buffersFromWorker.put(bufferp);
|
||||
m_offloadBuffersFromWorker.put(bufferp);
|
||||
} while (VL_LIKELY(!shutdown));
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::shutdownWorker() {
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::shutdownOffloadWorker() {
|
||||
// If the worker thread is not running, done..
|
||||
if (!m_workerThread) return;
|
||||
|
||||
// Hand an buffer with a shutdown command to the worker thread
|
||||
uint32_t* const bufferp = getTraceBuffer();
|
||||
bufferp[0] = VerilatedTraceCommand::SHUTDOWN;
|
||||
m_buffersToWorker.put(bufferp);
|
||||
uint32_t* const bufferp = getOffloadBuffer();
|
||||
bufferp[0] = VerilatedTraceOffloadCommand::SHUTDOWN;
|
||||
m_offloadBuffersToWorker.put(bufferp);
|
||||
// Wait for it to return
|
||||
waitForBuffer(bufferp);
|
||||
waitForOffloadBuffer(bufferp);
|
||||
// Join the thread and delete it
|
||||
m_workerThread->join();
|
||||
m_workerThread.reset(nullptr);
|
||||
@@ -241,80 +255,72 @@ template <> void VerilatedTrace<VL_DERIVED_T>::shutdownWorker() {
|
||||
//=============================================================================
|
||||
// Life cycle
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::closeBase() {
|
||||
#ifdef VL_TRACE_THREADED
|
||||
shutdownWorker();
|
||||
while (m_numTraceBuffers) {
|
||||
delete[] m_buffersFromWorker.get();
|
||||
--m_numTraceBuffers;
|
||||
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_DERIVED_T>::flushBase() {
|
||||
#ifdef VL_TRACE_THREADED
|
||||
// Hand an empty buffer to the worker thread
|
||||
uint32_t* const bufferp = getTraceBuffer();
|
||||
*bufferp = VerilatedTraceCommand::END;
|
||||
m_buffersToWorker.put(bufferp);
|
||||
// Wait for it to be returned. As the processing is in-order,
|
||||
// this ensures all previous buffers have been processed.
|
||||
waitForBuffer(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_DERIVED_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_DERIVED_T*>(selfp)->flush();
|
||||
reinterpret_cast<VL_SUB_T*>(selfp)->flush();
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_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_DERIVED_T*>(selfp)->close();
|
||||
reinterpret_cast<VL_SUB_T*>(selfp)->close();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedTrace
|
||||
|
||||
template <>
|
||||
VerilatedTrace<VL_DERIVED_T>::VerilatedTrace()
|
||||
: m_sigs_oldvalp{nullptr}
|
||||
, m_sigs_enabledp{nullptr}
|
||||
, m_timeLastDump{0}
|
||||
, m_fullDump{true}
|
||||
, m_nextCode{0}
|
||||
, m_numSignals{0}
|
||||
, m_maxBits{0}
|
||||
, m_scopeEscape{'.'}
|
||||
, m_timeRes{1e-9}
|
||||
, m_timeUnit {
|
||||
1e-9
|
||||
}
|
||||
#ifdef VL_TRACE_THREADED
|
||||
, m_numTraceBuffers { 0 }
|
||||
#endif
|
||||
{
|
||||
VerilatedTrace<VL_SUB_T, VL_BUF_T>::VerilatedTrace() {
|
||||
set_time_unit(Verilated::threadContextp()->timeunitString());
|
||||
set_time_resolution(Verilated::threadContextp()->timeprecisionString());
|
||||
}
|
||||
|
||||
template <> VerilatedTrace<VL_DERIVED_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_DERIVED_T>::onFlush, this);
|
||||
Verilated::removeExitCb(VerilatedTrace<VL_DERIVED_T>::onExit, this);
|
||||
#ifdef VL_TRACE_THREADED
|
||||
closeBase();
|
||||
#endif
|
||||
Verilated::removeFlushCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush, this);
|
||||
Verilated::removeExitCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit, this);
|
||||
if (offload()) closeBase();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Internals available to format specific implementations
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_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
|
||||
@@ -359,25 +365,28 @@ template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
|
||||
}
|
||||
|
||||
// Set callback so flush/abort will flush this file
|
||||
Verilated::addFlushCb(VerilatedTrace<VL_DERIVED_T>::onFlush, this);
|
||||
Verilated::addExitCb(VerilatedTrace<VL_DERIVED_T>::onExit, this);
|
||||
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_THREADED
|
||||
// Compute trace 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_traceBufferSize = 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_DERIVED_T>::workerThreadMain, this});
|
||||
// Start the worker thread
|
||||
m_workerThread.reset(
|
||||
new std::thread{&VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain, this});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template <>
|
||||
bool VerilatedTrace<VL_DERIVED_T>::declCode(uint32_t code, const char* namep, uint32_t bits,
|
||||
bool tri) {
|
||||
bool VerilatedTrace<VL_SUB_T, VL_BUF_T>::declCode(uint32_t code, const char* namep, uint32_t bits,
|
||||
bool tri) {
|
||||
if (VL_UNCOVERABLE(!code)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: internal trace problem, code 0 is illegal");
|
||||
}
|
||||
@@ -421,28 +430,32 @@ bool VerilatedTrace<VL_DERIVED_T>::declCode(uint32_t code, const char* namep, ui
|
||||
//=========================================================================
|
||||
// Internals available to format specific implementations
|
||||
|
||||
template <> std::string VerilatedTrace<VL_DERIVED_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_DERIVED_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 <> void VerilatedTrace<VL_DERIVED_T>::set_time_unit(const std::string& unit) VL_MT_SAFE {
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::set_time_unit(const std::string& unit) VL_MT_SAFE {
|
||||
set_time_unit(unit.c_str());
|
||||
}
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_resolution(const char* unitp) VL_MT_SAFE {
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::set_time_resolution(const char* unitp) VL_MT_SAFE {
|
||||
m_timeRes = timescaleToDouble(unitp);
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::set_time_resolution(const std::string& unit) VL_MT_SAFE {
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::set_time_resolution(const std::string& unit) VL_MT_SAFE {
|
||||
set_time_resolution(unit.c_str());
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::dumpvars(int level, const std::string& hier) VL_MT_SAFE {
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dumpvars(int level, const std::string& hier) VL_MT_SAFE {
|
||||
if (level == 0) {
|
||||
m_dumpvars.clear(); // empty = everything on
|
||||
} else {
|
||||
@@ -455,18 +468,114 @@ void VerilatedTrace<VL_DERIVED_T>::dumpvars(int level, const std::string& hier)
|
||||
}
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
#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
|
||||
wdp->m_cb(wdp->m_userp, wdp->m_bufp);
|
||||
// Mark buffer as ready
|
||||
const VerilatedLockGuard lock{wdp->m_mutex};
|
||||
wdp->m_ready.store(true);
|
||||
if (wdp->m_waiting) wdp->m_cv.notify_one();
|
||||
}
|
||||
|
||||
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;
|
||||
VL_CPU_RELAX();
|
||||
}
|
||||
// We have been spinning for a while, so yield the thread
|
||||
VerilatedLockGuard lock{m_mutex};
|
||||
m_waiting = true;
|
||||
m_cv.wait(lock, [this] { return m_ready.load(std::memory_order_relaxed); });
|
||||
m_waiting = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
template <>
|
||||
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);
|
||||
}
|
||||
|
||||
// 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
|
||||
for (const CallbackRecord& cbr : cbVec) {
|
||||
#ifdef VL_THREADED
|
||||
Buffer* traceBufferp = getTraceBuffer();
|
||||
cbr.m_dumpOffloadCb(cbr.m_userp, static_cast<OffloadBuffer*>(traceBufferp));
|
||||
commitTraceBuffer(traceBufferp);
|
||||
#else
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
// Not really VL_MT_SAFE but more VL_MT_UNSAFE_ONE.
|
||||
// This does get the mutex, but if multiple threads are trying to dump
|
||||
// chances are the data being dumped will have other problems
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
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();
|
||||
|
||||
@@ -477,58 +586,64 @@ template <> void VerilatedTrace<VL_DERIVED_T>::dump(uint64_t timeui) VL_MT_SAFE_
|
||||
if (!preChangeDump()) return;
|
||||
}
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
// Currently only incremental dumps run on the worker thread
|
||||
uint32_t* bufferp = nullptr;
|
||||
if (VL_LIKELY(!m_fullDump)) {
|
||||
// Get the trace buffer we are about to fill
|
||||
bufferp = getTraceBuffer();
|
||||
m_traceBufferWritep = bufferp;
|
||||
m_traceBufferEndp = bufferp + m_traceBufferSize;
|
||||
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_traceBufferWritep[0] = VerilatedTraceCommand::TIME_CHANGE;
|
||||
*reinterpret_cast<uint64_t*>(m_traceBufferWritep + 1) = timeui;
|
||||
m_traceBufferWritep += 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
|
||||
for (uint32_t i = 0; i < m_fullCbs.size(); ++i) {
|
||||
const CallbackRecord& cbr = m_fullCbs[i];
|
||||
cbr.m_dumpCb(cbr.m_userp, self());
|
||||
if (offload()) {
|
||||
runOffloadedCallbacks(m_fullOffloadCbs);
|
||||
} else {
|
||||
runCallbacks(m_fullCbs);
|
||||
}
|
||||
} else {
|
||||
for (uint32_t i = 0; i < m_chgCbs.size(); ++i) {
|
||||
const CallbackRecord& cbr = m_chgCbs[i];
|
||||
cbr.m_dumpCb(cbr.m_userp, self());
|
||||
if (offload()) {
|
||||
runOffloadedCallbacks(m_chgOffloadCbs);
|
||||
} else {
|
||||
runCallbacks(m_chgCbs);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < m_cleanupCbs.size(); ++i) {
|
||||
const CallbackRecord& cbr = m_cleanupCbs[i];
|
||||
cbr.m_dumpCb(cbr.m_userp, self());
|
||||
cbr.m_cleanupCb(cbr.m_userp, self());
|
||||
}
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
if (VL_LIKELY(bufferp)) {
|
||||
// Mark end of the trace buffer we just filled
|
||||
*m_traceBufferWritep++ = VerilatedTraceCommand::END;
|
||||
#ifdef VL_THREADED
|
||||
if (offload() && VL_LIKELY(bufferp)) {
|
||||
// Mark end of the offload buffer we just filled
|
||||
*m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::END;
|
||||
|
||||
// Assert no buffer overflow
|
||||
assert(m_traceBufferWritep - bufferp <= m_traceBufferSize);
|
||||
assert(m_offloadBufferWritep - bufferp <= m_offloadBufferSize);
|
||||
|
||||
// Pass it to the worker thread
|
||||
m_buffersToWorker.put(bufferp);
|
||||
m_offloadBuffersToWorker.put(bufferp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -537,101 +652,100 @@ template <> void VerilatedTrace<VL_DERIVED_T>::dump(uint64_t timeui) VL_MT_SAFE_
|
||||
// Non-hot path internal interface to Verilator generated code
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::addCallbackRecord(std::vector<CallbackRecord>& cbVec,
|
||||
CallbackRecord& cbRec)
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addModel(VerilatedModel* modelp)
|
||||
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());
|
||||
|
||||
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");
|
||||
}
|
||||
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
|
||||
cbVec.push_back(cbRec);
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::addInitCb(initCb_t cb, void* userp) VL_MT_SAFE {
|
||||
CallbackRecord cbr{cb, userp};
|
||||
addCallbackRecord(m_initCbs, cbr);
|
||||
}
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
|
||||
CallbackRecord cbr{cb, userp};
|
||||
addCallbackRecord(m_fullCbs, cbr);
|
||||
}
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
|
||||
CallbackRecord cbr{cb, userp};
|
||||
addCallbackRecord(m_chgCbs, cbr);
|
||||
}
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::addCleanupCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
|
||||
CallbackRecord cbr{cb, userp};
|
||||
addCallbackRecord(m_cleanupCbs, cbr);
|
||||
}
|
||||
// Keep hold of the context
|
||||
m_contextp = contextp;
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::pushNamePrefix(const std::string& prefix) {
|
||||
m_namePrefixStack.push_back(m_namePrefixStack.back() + prefix);
|
||||
}
|
||||
// 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
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::popNamePrefix(unsigned count) {
|
||||
while (count--) m_namePrefixStack.pop_back();
|
||||
assert(!m_namePrefixStack.empty());
|
||||
}
|
||||
// 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;
|
||||
|
||||
//=========================================================================
|
||||
// Hot path internal interface to Verilator generated code
|
||||
if (VL_UNCOVERABLE(m_parallel && m_offload)) { // LCOV_EXCL_START
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Cannot use parallel tracing with offloading");
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
// 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 VerilatedTrace<VL_DERIVED_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);
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_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);
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_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);
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_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);
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_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);
|
||||
// Configure format specific sub class
|
||||
configure(*(configp.get()));
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_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);
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCallbackRecord(std::vector<CallbackRecord>& cbVec,
|
||||
CallbackRecord&& cbRec)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
cbVec.push_back(cbRec);
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_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);
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addInitCb(initCb_t cb, void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_initCbs, CallbackRecord{cb, userp});
|
||||
}
|
||||
template <>
|
||||
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>::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 {
|
||||
addCallbackRecord(m_cleanupCbs, CallbackRecord{cb, userp});
|
||||
}
|
||||
|
||||
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) {
|
||||
while (count--) m_namePrefixStack.pop_back();
|
||||
assert(!m_namePrefixStack.empty());
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
@@ -724,41 +838,96 @@ static inline void cvtQDataToStr(char* dstp, QData value) {
|
||||
|
||||
#define cvtEDataToStr cvtIDataToStr
|
||||
|
||||
//=============================================================================
|
||||
//=========================================================================
|
||||
// VerilatedTraceBuffer
|
||||
|
||||
#ifdef VERILATED_VCD_TEST
|
||||
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} {}
|
||||
|
||||
void verilated_trace_imp_selftest() {
|
||||
#define SELF_CHECK(got, exp) \
|
||||
do { \
|
||||
if ((got) != (exp)) VL_FATAL_MT(__FILE__, __LINE__, "", "%Error: selftest"); \
|
||||
} while (0)
|
||||
// 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.
|
||||
|
||||
#define SELF_CHECK_TS(scale) \
|
||||
SELF_CHECK(doubleToTimescale(timescaleToDouble(scale)), std::string{scale});
|
||||
SELF_CHECK_TS("100s");
|
||||
SELF_CHECK_TS("10s");
|
||||
SELF_CHECK_TS("1s");
|
||||
SELF_CHECK_TS("100ms");
|
||||
SELF_CHECK_TS("10ms");
|
||||
SELF_CHECK_TS("1ms");
|
||||
SELF_CHECK_TS("100us");
|
||||
SELF_CHECK_TS("10us");
|
||||
SELF_CHECK_TS("1us");
|
||||
SELF_CHECK_TS("100ns");
|
||||
SELF_CHECK_TS("10ns");
|
||||
SELF_CHECK_TS("1ns");
|
||||
SELF_CHECK_TS("100ps");
|
||||
SELF_CHECK_TS("10ps");
|
||||
SELF_CHECK_TS("1ps");
|
||||
SELF_CHECK_TS("100fs");
|
||||
SELF_CHECK_TS("10fs");
|
||||
SELF_CHECK_TS("1fs");
|
||||
SELF_CHECK_TS("100as");
|
||||
SELF_CHECK_TS("10as");
|
||||
SELF_CHECK_TS("1as");
|
||||
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;
|
||||
emitBit(code, newval);
|
||||
}
|
||||
|
||||
template <>
|
||||
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;
|
||||
emitCData(code, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
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;
|
||||
emitSData(code, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
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;
|
||||
emitIData(code, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
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;
|
||||
emitQData(code, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
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;
|
||||
emitWData(code, newvalp, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
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
|
||||
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
|
||||
+69
-38
@@ -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;
|
||||
@@ -289,7 +293,7 @@ public:
|
||||
// Can't just overload operator[] or provide a "at" reference to set,
|
||||
// because we need to be able to insert only when the value is set
|
||||
T_Value& at(int32_t index) {
|
||||
static T_Value s_throwAway;
|
||||
static VL_THREAD_LOCAL T_Value s_throwAway;
|
||||
// Needs to work for dynamic arrays, so does not use T_MaxSize
|
||||
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
|
||||
s_throwAway = atDefault();
|
||||
@@ -300,7 +304,7 @@ public:
|
||||
}
|
||||
// Accessing. Verilog: v = assoc[index]
|
||||
const T_Value& at(int32_t index) const {
|
||||
static T_Value s_throwAway;
|
||||
static VL_THREAD_LOCAL T_Value s_throwAway;
|
||||
// Needs to work for dynamic arrays, so does not use T_MaxSize
|
||||
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
|
||||
return atDefault();
|
||||
@@ -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
|
||||
|
||||
+164
-344
@@ -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
|
||||
|
||||
@@ -62,12 +62,24 @@ constexpr unsigned VL_TRACE_MAX_VCD_CODE_SIZE = 5; // Maximum length of a VCD s
|
||||
// cache-lines.
|
||||
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) {
|
||||
static_assert((N & (N - 1)) == 0, "'N' must be a power of 2");
|
||||
size_t mask = N - 1;
|
||||
return (value + mask) & ~mask;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Specialization of the generics for this trace format
|
||||
|
||||
#define VL_DERIVED_T VerilatedVcd
|
||||
#include "verilated_trace_imp.cpp"
|
||||
#undef VL_DERIVED_T
|
||||
#define VL_SUB_T VerilatedVcd
|
||||
#define VL_BUF_T VerilatedVcdBuffer
|
||||
#include "verilated_trace_imp.h"
|
||||
#undef VL_SUB_T
|
||||
#undef VL_BUF_T
|
||||
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
@@ -108,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) {
|
||||
@@ -168,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();
|
||||
}
|
||||
|
||||
@@ -183,7 +195,7 @@ void VerilatedVcd::makeNameMap() {
|
||||
deleteNameMap();
|
||||
m_namemapp = new NameMap;
|
||||
|
||||
VerilatedTrace<VerilatedVcd>::traceInit();
|
||||
Super::traceInit();
|
||||
|
||||
// Though not speced, it's illegal to generate a vcd with signals
|
||||
// not under any module - it crashes at least two viewers.
|
||||
@@ -218,13 +230,19 @@ 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_THREADED
|
||||
if (parallel()) {
|
||||
assert(m_numBuffers == m_freeBuffers.size());
|
||||
for (auto& pair : m_freeBuffers) VL_DO_CLEAR(delete[] pair.first, pair.first = nullptr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void VerilatedVcd::closePrev() {
|
||||
// This function is on the flush() call path
|
||||
if (!isOpen()) return;
|
||||
|
||||
VerilatedTrace<VerilatedVcd>::flushBase();
|
||||
Super::flushBase();
|
||||
bufferFlush();
|
||||
m_isOpen = false;
|
||||
m_filep->close();
|
||||
@@ -245,20 +263,15 @@ 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 VerilatedTrace<VerilatedVcd>::flush(), so we just
|
||||
// closePrev() called Super::flush(), so we just
|
||||
// need to shut down the tracing thread here.
|
||||
VerilatedTrace<VerilatedVcd>::closeBase();
|
||||
Super::closeBase();
|
||||
}
|
||||
|
||||
void VerilatedVcd::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
VerilatedTrace<VerilatedVcd>::flushBase();
|
||||
Super::flushBase();
|
||||
bufferFlush();
|
||||
}
|
||||
|
||||
@@ -277,12 +290,12 @@ void VerilatedVcd::printQuad(uint64_t n) {
|
||||
printStr(buf);
|
||||
}
|
||||
|
||||
void VerilatedVcd::bufferResize(uint64_t minsize) {
|
||||
void VerilatedVcd::bufferResize(size_t minsize) {
|
||||
// minsize is size of largest write. We buffer at least 8 times as much data,
|
||||
// writing when we are 3/4 full (with thus 2*minsize remaining free)
|
||||
if (VL_UNLIKELY(minsize > m_wrChunkSize)) {
|
||||
const char* oldbufp = m_wrBufp;
|
||||
m_wrChunkSize = minsize * 2;
|
||||
m_wrChunkSize = roundUpToMultipleOf<1024>(minsize * 2);
|
||||
m_wrBufp = new char[m_wrChunkSize * 8];
|
||||
std::memcpy(m_wrBufp, oldbufp, m_writep - oldbufp);
|
||||
m_writep = m_wrBufp + (m_writep - oldbufp);
|
||||
@@ -292,7 +305,7 @@ void VerilatedVcd::bufferResize(uint64_t minsize) {
|
||||
}
|
||||
|
||||
void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
|
||||
// This function can be called from the trace thread
|
||||
// This function can be called from the trace offload thread
|
||||
// This function is on the flush() call path
|
||||
// We add output data to m_writep.
|
||||
// When it gets nearly full we dump it using this routine which calls write()
|
||||
@@ -463,14 +476,16 @@ void VerilatedVcd::declare(uint32_t code, const char* name, const char* wirep, b
|
||||
int arraynum, bool tri, bool bussed, int msb, int lsb) {
|
||||
const int bits = ((msb > lsb) ? (msb - lsb) : (lsb - msb)) + 1;
|
||||
|
||||
const bool enabled = VerilatedTrace<VerilatedVcd>::declCode(code, name, bits, tri);
|
||||
const bool enabled = Super::declCode(code, name, bits, tri);
|
||||
|
||||
if (m_suffixes.size() <= nextCode() * VL_TRACE_SUFFIX_ENTRY_SIZE) {
|
||||
m_suffixes.resize(nextCode() * VL_TRACE_SUFFIX_ENTRY_SIZE * 2, 0);
|
||||
}
|
||||
|
||||
// Make sure write buffer is large enough (one character per bit), plus header
|
||||
bufferResize(bits + 1024);
|
||||
// Keep upper bound on bytes a single signal cna emit into the buffer
|
||||
m_maxSignalBytes = std::max<size_t>(m_maxSignalBytes, bits + 32);
|
||||
// Make sure write buffer is large enough, plus header
|
||||
bufferResize(m_maxSignalBytes + 1024);
|
||||
|
||||
if (!enabled) return;
|
||||
|
||||
@@ -497,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) {
|
||||
@@ -562,26 +568,69 @@ void VerilatedVcd::declArray(uint32_t code, const char* name, bool array, int ar
|
||||
void VerilatedVcd::declDouble(uint32_t code, const char* name, bool array, int arraynum) {
|
||||
declare(code, name, "real", array, arraynum, false, false, 63, 0);
|
||||
}
|
||||
#ifdef VL_TRACE_VCD_OLD_API
|
||||
void VerilatedVcd::declTriBit(uint32_t code, const char* name, bool array, int arraynum) {
|
||||
declare(code, name, "wire", array, arraynum, true, false, 0, 0);
|
||||
}
|
||||
void VerilatedVcd::declTriBus(uint32_t code, const char* name, bool array, int arraynum, int msb,
|
||||
int lsb) {
|
||||
declare(code, name, "wire", array, arraynum, true, true, msb, lsb);
|
||||
}
|
||||
void VerilatedVcd::declTriQuad(uint32_t code, const char* name, bool array, int arraynum, int msb,
|
||||
int lsb) {
|
||||
declare(code, name, "wire", array, arraynum, true, true, msb, lsb);
|
||||
}
|
||||
void VerilatedVcd::declTriArray(uint32_t code, const char* name, bool array, int arraynum, int msb,
|
||||
int lsb) {
|
||||
declare(code, name, "wire", array, arraynum, true, true, msb, lsb);
|
||||
}
|
||||
#endif // VL_TRACE_VCD_OLD_API
|
||||
|
||||
//=============================================================================
|
||||
// Trace rendering prinitives
|
||||
// Get/commit trace buffer
|
||||
|
||||
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();
|
||||
}
|
||||
#endif
|
||||
// Return the buffer
|
||||
return bufp;
|
||||
}
|
||||
|
||||
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
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
|
||||
#endif
|
||||
} else {
|
||||
// Needs adjusting for emitTimeChange
|
||||
m_writep = bufp->m_writep;
|
||||
}
|
||||
delete bufp;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcdBuffer implementation
|
||||
|
||||
//=============================================================================
|
||||
// Trace rendering primitives
|
||||
|
||||
static inline void
|
||||
VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) VL_ATTR_NO_SANITIZE_ALIGN;
|
||||
@@ -606,26 +655,59 @@ static inline void VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* s
|
||||
#endif
|
||||
}
|
||||
|
||||
void VerilatedVcd::finishLine(uint32_t code, char* writep) {
|
||||
const char* const suffixp = m_suffixes.data() + code * VL_TRACE_SUFFIX_ENTRY_SIZE;
|
||||
void VerilatedVcdBuffer::finishLine(uint32_t code, char* writep) {
|
||||
const char* const suffixp = m_suffixes + code * VL_TRACE_SUFFIX_ENTRY_SIZE;
|
||||
VL_DEBUG_IFDEF(assert(suffixp[0]););
|
||||
VerilatedVcdCCopyAndAppendNewLine(writep, suffixp);
|
||||
|
||||
// Now write back the write pointer incremented by the actual size of the
|
||||
// suffix, which was stored in the last byte of the suffix buffer entry.
|
||||
m_writep = writep + suffixp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1];
|
||||
bufferCheck();
|
||||
|
||||
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
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// emit* trace routines
|
||||
|
||||
// Note: emit* are only ever called from one place (full* in
|
||||
// verilated_trace_imp.cpp, which is included in this file at the top),
|
||||
// verilated_trace_imp.h, which is included in this file at the top),
|
||||
// so always inline them.
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedVcd::emitBit(uint32_t code, CData newval) {
|
||||
void VerilatedVcdBuffer::emitBit(uint32_t code, CData newval) {
|
||||
// Don't prefetch suffix as it's a bit too late;
|
||||
char* wp = m_writep;
|
||||
*wp++ = '0' | static_cast<char>(newval);
|
||||
@@ -633,7 +715,7 @@ void VerilatedVcd::emitBit(uint32_t code, CData newval) {
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedVcd::emitCData(uint32_t code, CData newval, int bits) {
|
||||
void VerilatedVcdBuffer::emitCData(uint32_t code, CData newval, int bits) {
|
||||
char* wp = m_writep;
|
||||
*wp++ = 'b';
|
||||
cvtCDataToStr(wp, newval << (VL_BYTESIZE - bits));
|
||||
@@ -641,7 +723,7 @@ void VerilatedVcd::emitCData(uint32_t code, CData newval, int bits) {
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedVcd::emitSData(uint32_t code, SData newval, int bits) {
|
||||
void VerilatedVcdBuffer::emitSData(uint32_t code, SData newval, int bits) {
|
||||
char* wp = m_writep;
|
||||
*wp++ = 'b';
|
||||
cvtSDataToStr(wp, newval << (VL_SHORTSIZE - bits));
|
||||
@@ -649,7 +731,7 @@ void VerilatedVcd::emitSData(uint32_t code, SData newval, int bits) {
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedVcd::emitIData(uint32_t code, IData newval, int bits) {
|
||||
void VerilatedVcdBuffer::emitIData(uint32_t code, IData newval, int bits) {
|
||||
char* wp = m_writep;
|
||||
*wp++ = 'b';
|
||||
cvtIDataToStr(wp, newval << (VL_IDATASIZE - bits));
|
||||
@@ -657,7 +739,7 @@ void VerilatedVcd::emitIData(uint32_t code, IData newval, int bits) {
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedVcd::emitQData(uint32_t code, QData newval, int bits) {
|
||||
void VerilatedVcdBuffer::emitQData(uint32_t code, QData newval, int bits) {
|
||||
char* wp = m_writep;
|
||||
*wp++ = 'b';
|
||||
cvtQDataToStr(wp, newval << (VL_QUADSIZE - bits));
|
||||
@@ -665,7 +747,7 @@ void VerilatedVcd::emitQData(uint32_t code, QData newval, int bits) {
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedVcd::emitWData(uint32_t code, const WData* newvalp, int bits) {
|
||||
void VerilatedVcdBuffer::emitWData(uint32_t code, const WData* newvalp, int bits) {
|
||||
int words = VL_WORDS_I(bits);
|
||||
char* wp = m_writep;
|
||||
*wp++ = 'b';
|
||||
@@ -682,272 +764,10 @@ void VerilatedVcd::emitWData(uint32_t code, const WData* newvalp, int bits) {
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedVcd::emitDouble(uint32_t code, double newval) {
|
||||
void VerilatedVcdBuffer::emitDouble(uint32_t code, double newval) {
|
||||
char* wp = m_writep;
|
||||
// Buffer can't overflow before VL_SNPRINTF; we sized during declaration
|
||||
VL_SNPRINTF(wp, m_wrChunkSize, "r%.16g", newval);
|
||||
VL_SNPRINTF(wp, m_maxSignalBytes, "r%.16g", newval);
|
||||
wp += std::strlen(wp);
|
||||
finishLine(code, wp);
|
||||
}
|
||||
|
||||
#ifdef VL_TRACE_VCD_OLD_API
|
||||
|
||||
void VerilatedVcd::fullBit(uint32_t code, const uint32_t newval) {
|
||||
// Note the &1, so we don't require clean input -- makes more common no change case faster
|
||||
*oldp(code) = newval;
|
||||
*m_writep++ = ('0' + static_cast<char>(newval & 1));
|
||||
m_writep = writeCode(m_writep, code);
|
||||
*m_writep++ = '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullBus(uint32_t code, const uint32_t newval, int bits) {
|
||||
*oldp(code) = newval;
|
||||
*m_writep++ = 'b';
|
||||
for (int bit = bits - 1; bit >= 0; --bit) {
|
||||
*m_writep++ = ((newval & (1L << bit)) ? '1' : '0');
|
||||
}
|
||||
*m_writep++ = ' ';
|
||||
m_writep = writeCode(m_writep, code);
|
||||
*m_writep++ = '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullQuad(uint32_t code, const uint64_t newval, int bits) {
|
||||
(*(reinterpret_cast<uint64_t*>(oldp(code)))) = newval;
|
||||
*m_writep++ = 'b';
|
||||
for (int bit = bits - 1; bit >= 0; --bit) {
|
||||
*m_writep++ = ((newval & (1ULL << bit)) ? '1' : '0');
|
||||
}
|
||||
*m_writep++ = ' ';
|
||||
m_writep = writeCode(m_writep, code);
|
||||
*m_writep++ = '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullArray(uint32_t code, const uint32_t* newval, int bits) {
|
||||
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) { oldp(code)[word] = newval[word]; }
|
||||
*m_writep++ = 'b';
|
||||
for (int bit = bits - 1; bit >= 0; --bit) {
|
||||
*m_writep++ = ((newval[(bit / 32)] & (1L << (bit & 0x1f))) ? '1' : '0');
|
||||
}
|
||||
*m_writep++ = ' ';
|
||||
m_writep = writeCode(m_writep, code);
|
||||
*m_writep++ = '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullArray(uint32_t code, const uint64_t* newval, int bits) {
|
||||
for (int word = 0; word < (((bits - 1) / 64) + 1); ++word) { oldp(code)[word] = newval[word]; }
|
||||
*m_writep++ = 'b';
|
||||
for (int bit = bits - 1; bit >= 0; --bit) {
|
||||
*m_writep++ = ((newval[(bit / 64)] & (1ULL << (bit & 0x3f))) ? '1' : '0');
|
||||
}
|
||||
*m_writep++ = ' ';
|
||||
m_writep = writeCode(m_writep, code);
|
||||
*m_writep++ = '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullTriBit(uint32_t code, const uint32_t newval, const uint32_t newtri) {
|
||||
oldp(code)[0] = newval;
|
||||
oldp(code)[1] = newtri;
|
||||
*m_writep++ = "01zz"[newval | (newtri << 1)];
|
||||
m_writep = writeCode(m_writep, code);
|
||||
*m_writep++ = '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullTriBus(uint32_t code, const uint32_t newval, const uint32_t newtri,
|
||||
int bits) {
|
||||
oldp(code)[0] = newval;
|
||||
oldp(code)[1] = newtri;
|
||||
*m_writep++ = 'b';
|
||||
for (int bit = bits - 1; bit >= 0; --bit) {
|
||||
*m_writep++ = "01zz"[((newval >> bit) & 1) | (((newtri >> bit) & 1) << 1)];
|
||||
}
|
||||
*m_writep++ = ' ';
|
||||
m_writep = writeCode(m_writep, code);
|
||||
*m_writep++ = '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullTriQuad(uint32_t code, const uint64_t newval, const uint64_t newtri,
|
||||
int bits) {
|
||||
(*(reinterpret_cast<uint64_t*>(oldp(code)))) = newval;
|
||||
(*(reinterpret_cast<uint64_t*>(oldp(code + 1)))) = newtri;
|
||||
*m_writep++ = 'b';
|
||||
for (int bit = bits - 1; bit >= 0; --bit) {
|
||||
*m_writep++ = "01zz"[((newval >> bit) & 1ULL) | (((newtri >> bit) & 1ULL) << 1ULL)];
|
||||
}
|
||||
*m_writep++ = ' ';
|
||||
m_writep = writeCode(m_writep, code);
|
||||
*m_writep++ = '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullTriArray(uint32_t code, const uint32_t* newvalp, const uint32_t* newtrip,
|
||||
int bits) {
|
||||
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) {
|
||||
oldp(code)[word * 2] = newvalp[word];
|
||||
oldp(code)[word * 2 + 1] = newtrip[word];
|
||||
}
|
||||
*m_writep++ = 'b';
|
||||
for (int bit = bits - 1; bit >= 0; --bit) {
|
||||
uint32_t valbit = (newvalp[(bit / 32)] >> (bit & 0x1f)) & 1;
|
||||
uint32_t tribit = (newtrip[(bit / 32)] >> (bit & 0x1f)) & 1;
|
||||
*m_writep++ = "01zz"[valbit | (tribit << 1)];
|
||||
}
|
||||
*m_writep++ = ' ';
|
||||
m_writep = writeCode(m_writep, code);
|
||||
*m_writep++ = '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullDouble(uint32_t code, const double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
(*(reinterpret_cast<double*>(oldp(code)))) = newval;
|
||||
// Buffer can't overflow before VL_SNPRINTF; we sized during declaration
|
||||
VL_SNPRINTF(m_writep, m_wrChunkSize, "r%.16g", newval);
|
||||
m_writep += std::strlen(m_writep);
|
||||
*m_writep++ = ' ';
|
||||
m_writep = writeCode(m_writep, code);
|
||||
*m_writep++ = '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
|
||||
#endif // VL_TRACE_VCD_OLD_API
|
||||
|
||||
//======================================================================
|
||||
//======================================================================
|
||||
//======================================================================
|
||||
|
||||
#ifdef VERILATED_VCD_TEST
|
||||
#include <iostream>
|
||||
|
||||
extern void verilated_trace_imp_selftest();
|
||||
|
||||
uint32_t v1, v2, s1, s2[3];
|
||||
uint32_t tri96[3];
|
||||
uint32_t tri96__tri[3];
|
||||
uint64_t quad96[2];
|
||||
uint64_t tquad;
|
||||
uint64_t tquad__tri;
|
||||
uint8_t ch;
|
||||
uint64_t timestamp = 1;
|
||||
double doub = 0.0;
|
||||
float flo = 0.0f;
|
||||
|
||||
void vcdInit(void*, VerilatedVcd* vcdp, uint32_t) {
|
||||
vcdp->scopeEscape('.');
|
||||
vcdp->pushNamePrefix("top.");
|
||||
/**/ vcdp->declBus(0x2, "v1", -1, 0, 5, 1);
|
||||
/**/ vcdp->declBus(0x3, "v2", -1, 0, 6, 1);
|
||||
/**/ vcdp->pushNamePrefix("sub1.");
|
||||
/***/ vcdp->declBit(0x4, "s1", -1, 0);
|
||||
/***/ vcdp->declBit(0x5, "ch", -1, 0);
|
||||
/**/ vcdp->popNamePrefix();
|
||||
/**/ vcdp->pushNamePrefix("sub2.");
|
||||
/***/ vcdp->declArray(0x6, "s2", -1, 0, 40, 3);
|
||||
/**/ vcdp->popNamePrefix();
|
||||
vcdp->popNamePrefix();
|
||||
// Note need to add 3 for next code.
|
||||
vcdp->pushNamePrefix("top2.");
|
||||
/**/ vcdp->declBus(0x2, "t2v1", -1, 0, 4, 1);
|
||||
/**/ vcdp->declTriBit(0x10, "io1", -1, 0);
|
||||
/**/ vcdp->declTriBus(0x12, "io5", -1, 0, 4, 0);
|
||||
/**/ vcdp->declTriArray(0x16, "io96", -1, 0, 95, 0);
|
||||
/**/ // Note need to add 6 for next code.
|
||||
/**/ vcdp->declDouble(0x1c, "doub", -1, 0);
|
||||
/**/ // Note need to add 2 for next code.
|
||||
/**/ vcdp->declArray(0x20, "q2", -1, 0, 95, 0);
|
||||
/**/ // Note need to add 4 for next code.
|
||||
/**/ vcdp->declTriQuad(0x24, "tq", -1, 0, 63, 0);
|
||||
/**/ // Note need to add 4 for next code.
|
||||
vcdp->popNamePrefix();
|
||||
}
|
||||
|
||||
void vcdFull(void*, VerilatedVcd* vcdp) {
|
||||
vcdp->fullBus(0x2, v1, 5);
|
||||
vcdp->fullBus(0x3, v2, 7);
|
||||
vcdp->fullBit(0x4, s1);
|
||||
vcdp->fullBus(0x5, ch, 2);
|
||||
vcdp->fullArray(0x6, &s2[0], 38);
|
||||
vcdp->fullTriBit(0x10, tri96[0] & 1, tri96__tri[0] & 1);
|
||||
vcdp->fullTriBus(0x12, tri96[0] & 0x1f, tri96__tri[0] & 0x1f, 5);
|
||||
vcdp->fullTriArray(0x16, tri96, tri96__tri, 96);
|
||||
vcdp->fullDouble(0x1c, doub);
|
||||
vcdp->fullArray(0x20, &quad96[0], 96);
|
||||
vcdp->fullTriQuad(0x24, tquad, tquad__tri, 64);
|
||||
}
|
||||
|
||||
void vcdChange(void*, VerilatedVcd* vcdp) {
|
||||
vcdp->chgBus(0x2, v1, 5);
|
||||
vcdp->chgBus(0x3, v2, 7);
|
||||
vcdp->chgBit(0x4, s1);
|
||||
vcdp->chgBus(0x5, ch, 2);
|
||||
vcdp->chgArray(0x6, &s2[0], 38);
|
||||
vcdp->chgTriBit(0x10, tri96[0] & 1, tri96__tri[0] & 1);
|
||||
vcdp->chgTriBus(0x12, tri96[0] & 0x1f, tri96__tri[0] & 0x1f, 5);
|
||||
vcdp->chgTriArray(0x16, tri96, tri96__tri, 96);
|
||||
vcdp->chgDouble(0x1c, doub);
|
||||
vcdp->chgArray(0x20, &quad96[0], 96);
|
||||
vcdp->chgTriQuad(0x24, tquad, tquad__tri, 64);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
void vcdTestMain(const char* filenamep) {
|
||||
verilated_trace_imp_selftest();
|
||||
|
||||
v1 = v2 = s1 = 0;
|
||||
s2[0] = s2[1] = s2[2] = 0;
|
||||
tri96[2] = tri96[1] = tri96[0] = 0;
|
||||
tri96__tri[2] = tri96__tri[1] = tri96__tri[0] = ~0;
|
||||
quad96[1] = quad96[0] = 0;
|
||||
ch = 0;
|
||||
doub = 0;
|
||||
tquad = tquad__tri = 0;
|
||||
{
|
||||
VerilatedVcdC* vcdp = new VerilatedVcdC;
|
||||
vcdp->evcd(true);
|
||||
vcdp->set_time_unit("1ms");
|
||||
vcdp->set_time_unit(std::string{"1ms"});
|
||||
vcdp->set_time_resolution("1ns");
|
||||
vcdp->set_time_resolution(std::string{"1ns"});
|
||||
vcdp->spTrace()->addInitCb(&vcdInit, 0);
|
||||
vcdp->spTrace()->addFullCb(&vcdFull, 0);
|
||||
vcdp->spTrace()->addChgCb(&vcdChange, 0);
|
||||
vcdp->open(filenamep);
|
||||
// Dumping
|
||||
vcdp->dump(++timestamp);
|
||||
v1 = 0xfff;
|
||||
tri96[2] = 4; tri96[1] = 2; tri96[0] = 1;
|
||||
tri96__tri[2] = tri96__tri[1] = tri96__tri[0] = ~0; // Still tri
|
||||
quad96[1] = 0xffffffff; quad96[0] = 0;
|
||||
doub = 1.5;
|
||||
flo = 1.4f;
|
||||
vcdp->dump(++timestamp);
|
||||
v2 = 0x1;
|
||||
s2[1] = 2;
|
||||
tri96__tri[2] = tri96__tri[1] = tri96__tri[0] = 0; // enable w/o data change
|
||||
quad96[1] = 0; quad96[0] = ~0;
|
||||
doub = -1.66e13;
|
||||
flo = 0.123f;
|
||||
tquad = 0x00ff00ff00ff00ffULL;
|
||||
tquad__tri = 0x0000fffff0000ffffULL;
|
||||
vcdp->dump(++timestamp);
|
||||
ch = 2;
|
||||
tri96[2] = ~4; tri96[1] = ~2; tri96[0] = ~1;
|
||||
doub = -3.33e-13;
|
||||
vcdp->dump(++timestamp);
|
||||
vcdp->dump(++timestamp);
|
||||
# ifdef VERILATED_VCD_TEST_64BIT
|
||||
const uint64_t bytesPerDump = 15ULL;
|
||||
for (uint64_t i = 0; i < ((1ULL << 32) / bytesPerDump); i++) {
|
||||
v1 = i;
|
||||
vcdp->dump(++timestamp);
|
||||
}
|
||||
# endif
|
||||
vcdp->close();
|
||||
VL_DO_CLEAR(delete vcdp, vcdp = nullptr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
//********************************************************************
|
||||
// ;compile-command: "v4make test_regress/t/t_trace_c_api.pl"
|
||||
//
|
||||
// Local Variables:
|
||||
// End:
|
||||
|
||||
+196
-288
@@ -28,7 +28,196 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class VerilatedVcd;
|
||||
class VerilatedVcdBuffer;
|
||||
class VerilatedVcdFile;
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcd
|
||||
// Base class to create a Verilator VCD dump
|
||||
// This is an internally used class - see VerilatedVcdC for what to call from applications
|
||||
|
||||
class VerilatedVcd VL_NOT_FINAL : public VerilatedTrace<VerilatedVcd, VerilatedVcdBuffer> {
|
||||
public:
|
||||
using Super = VerilatedTrace<VerilatedVcd, VerilatedVcdBuffer>;
|
||||
|
||||
private:
|
||||
friend VerilatedVcdBuffer; // Give the buffer access to the private bits
|
||||
|
||||
//=========================================================================
|
||||
// VCD specific internals
|
||||
|
||||
VerilatedVcdFile* m_filep; // File we're writing to
|
||||
bool m_fileNewed; // m_filep needs destruction
|
||||
bool m_isOpen = false; // True indicates open file
|
||||
std::string m_filename; // Filename we're writing to (if open)
|
||||
uint64_t m_rolloverSize = 0; // File size to rollover at
|
||||
int m_modDepth = 0; // Depth of module hierarchy
|
||||
|
||||
char* m_wrBufp; // Output buffer
|
||||
char* m_wrFlushp; // Output buffer flush trigger location
|
||||
char* m_writep; // Write pointer into output buffer
|
||||
size_t m_wrChunkSize; // Output buffer size
|
||||
size_t m_maxSignalBytes = 0; // Upper bound on number of bytes a single signal can generate
|
||||
uint64_t m_wroteBytes = 0; // Number of bytes written to this file
|
||||
|
||||
std::vector<char> m_suffixes; // VCD line end string codes + metadata
|
||||
|
||||
using NameMap = std::map<const std::string, const std::string>;
|
||||
NameMap* m_namemapp = nullptr; // List of names for the header
|
||||
|
||||
#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
|
||||
#endif
|
||||
|
||||
void bufferResize(size_t minsize);
|
||||
void bufferFlush() VL_MT_UNSAFE_ONE;
|
||||
inline void bufferCheck() {
|
||||
// Flush the write buffer if there's not enough space left for new information
|
||||
// We only call this once per vector, so we need enough slop for a very wide "b###" line
|
||||
if (VL_UNLIKELY(m_writep > m_wrFlushp)) bufferFlush();
|
||||
}
|
||||
void openNextImp(bool incFilename);
|
||||
void closePrev();
|
||||
void closeErr();
|
||||
void makeNameMap();
|
||||
void deleteNameMap();
|
||||
void printIndent(int level_change);
|
||||
void printStr(const char* str);
|
||||
void printQuad(uint64_t n);
|
||||
void printTime(uint64_t timeui);
|
||||
void declare(uint32_t code, const char* name, const char* wirep, bool array, int arraynum,
|
||||
bool tri, bool bussed, int msb, int lsb);
|
||||
|
||||
void dumpHeader();
|
||||
|
||||
static char* writeCode(char* writep, uint32_t code);
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedVcd);
|
||||
|
||||
protected:
|
||||
//=========================================================================
|
||||
// Implementation of VerilatedTrace interface
|
||||
|
||||
// Called when the trace moves forward to a new time point
|
||||
void emitTimeChange(uint64_t timeui) override;
|
||||
|
||||
// Hooks called from VerilatedTrace
|
||||
bool preFullDump() override { return isOpen(); }
|
||||
bool preChangeDump() override;
|
||||
|
||||
// Trace buffer management
|
||||
Buffer* getTraceBuffer() override;
|
||||
void commitTraceBuffer(Buffer*) override;
|
||||
|
||||
// Configure sub-class
|
||||
void configure(const VerilatedTraceConfig&) override{};
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
// External interface to client code
|
||||
|
||||
// CONSTRUCTOR
|
||||
explicit VerilatedVcd(VerilatedVcdFile* filep = nullptr);
|
||||
~VerilatedVcd();
|
||||
|
||||
// ACCESSORS
|
||||
// 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
|
||||
void open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
// Open next data-only file
|
||||
void openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
// Close the file
|
||||
void close() VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
// Flush any remaining data to this file
|
||||
void flush() VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
// Return if file is open
|
||||
bool isOpen() const VL_MT_SAFE { return m_isOpen; }
|
||||
|
||||
//=========================================================================
|
||||
// Internal interface to Verilator generated code
|
||||
|
||||
void declBit(uint32_t code, const char* name, bool array, int arraynum);
|
||||
void declBus(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declQuad(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declArray(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declDouble(uint32_t code, const char* name, bool array, int arraynum);
|
||||
};
|
||||
|
||||
#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);
|
||||
#endif // DOXYGEN
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcdBuffer
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
#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
|
||||
|
||||
void adjustGrowp() {
|
||||
m_growp = (m_bufp + m_size) - (2 * m_maxSignalBytes);
|
||||
assert(m_growp >= m_bufp + m_maxSignalBytes);
|
||||
}
|
||||
#endif
|
||||
|
||||
void finishLine(uint32_t code, char* writep);
|
||||
|
||||
// CONSTRUCTOR
|
||||
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);
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFile
|
||||
@@ -52,280 +241,6 @@ public:
|
||||
virtual ssize_t write(const char* bufp, ssize_t len) VL_MT_UNSAFE;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcd
|
||||
// Base class to create a Verilator VCD dump
|
||||
// This is an internally used class - see VerilatedVcdC for what to call from applications
|
||||
|
||||
class VerilatedVcd VL_NOT_FINAL : public VerilatedTrace<VerilatedVcd> {
|
||||
private:
|
||||
// Give the superclass access to private bits (to avoid virtual functions)
|
||||
friend class VerilatedTrace<VerilatedVcd>;
|
||||
|
||||
//=========================================================================
|
||||
// VCD specific internals
|
||||
|
||||
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
|
||||
int m_modDepth = 0; // Depth of module hierarchy
|
||||
|
||||
char* m_wrBufp; // Output buffer
|
||||
const char* m_wrFlushp; // Output buffer flush trigger location
|
||||
char* m_writep; // Write pointer into output buffer
|
||||
uint64_t m_wrChunkSize; // Output buffer size
|
||||
uint64_t m_wroteBytes = 0; // Number of bytes written to this file
|
||||
|
||||
std::vector<char> m_suffixes; // VCD line end string codes + metadata
|
||||
|
||||
using NameMap = std::map<const std::string, const std::string>;
|
||||
NameMap* m_namemapp = nullptr; // List of names for the header
|
||||
|
||||
void bufferResize(uint64_t minsize);
|
||||
void bufferFlush() VL_MT_UNSAFE_ONE;
|
||||
inline void bufferCheck() {
|
||||
// Flush the write buffer if there's not enough space left for new information
|
||||
// We only call this once per vector, so we need enough slop for a very wide "b###" line
|
||||
if (VL_UNLIKELY(m_writep > m_wrFlushp)) bufferFlush();
|
||||
}
|
||||
void openNextImp(bool incFilename);
|
||||
void closePrev();
|
||||
void closeErr();
|
||||
void makeNameMap();
|
||||
void deleteNameMap();
|
||||
void printIndent(int level_change);
|
||||
void printStr(const char* str);
|
||||
void printQuad(uint64_t n);
|
||||
void printTime(uint64_t timeui);
|
||||
void declare(uint32_t code, const char* name, const char* wirep, bool array, int arraynum,
|
||||
bool tri, bool bussed, int msb, int lsb);
|
||||
|
||||
void dumpHeader();
|
||||
|
||||
static char* writeCode(char* writep, uint32_t code);
|
||||
|
||||
void finishLine(uint32_t code, char* writep);
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedVcd);
|
||||
|
||||
protected:
|
||||
//=========================================================================
|
||||
// Implementation of VerilatedTrace interface
|
||||
|
||||
// Implementations of protected virtual methods for VerilatedTrace
|
||||
virtual void emitTimeChange(uint64_t timeui) override;
|
||||
|
||||
// Hooks called from VerilatedTrace
|
||||
virtual bool preFullDump() override { return isOpen(); }
|
||||
virtual bool preChangeDump() override;
|
||||
|
||||
// Implementations of duck-typed methods for VerilatedTrace. These are
|
||||
// called from only one place (namely full*) so always inline them.
|
||||
inline void emitBit(uint32_t code, CData newval);
|
||||
inline void emitCData(uint32_t code, CData newval, int bits);
|
||||
inline void emitSData(uint32_t code, SData newval, int bits);
|
||||
inline void emitIData(uint32_t code, IData newval, int bits);
|
||||
inline void emitQData(uint32_t code, QData newval, int bits);
|
||||
inline void emitWData(uint32_t code, const WData* newvalp, int bits);
|
||||
inline void emitDouble(uint32_t code, double newval);
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
// External interface to client code
|
||||
|
||||
explicit VerilatedVcd(VerilatedVcdFile* filep = nullptr);
|
||||
~VerilatedVcd();
|
||||
|
||||
// ACCESSORS
|
||||
// Set size in megabytes after which new file should be created
|
||||
void rolloverMB(uint64_t rolloverMB) { m_rolloverMB = rolloverMB; }
|
||||
|
||||
// METHODS
|
||||
// Open the file; call isOpen() to see if errors
|
||||
void open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
// Open next data-only file
|
||||
void openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
// Close the file
|
||||
void close() VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
// Flush any remaining data to this file
|
||||
void flush() VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
// Return if file is open
|
||||
bool isOpen() const VL_MT_SAFE { return m_isOpen; }
|
||||
|
||||
//=========================================================================
|
||||
// Internal interface to Verilator generated code
|
||||
|
||||
void declBit(uint32_t code, const char* name, bool array, int arraynum);
|
||||
void declBus(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declQuad(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declArray(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declDouble(uint32_t code, const char* name, bool array, int arraynum);
|
||||
|
||||
#ifdef VL_TRACE_VCD_OLD_API
|
||||
//=========================================================================
|
||||
// Note: These are only for testing for backward compatibility with foreign
|
||||
// code and is not used by Verilator. Do not use these as there is no
|
||||
// guarantee of functionality.
|
||||
|
||||
void declTriBit(uint32_t code, const char* name, bool array, int arraynum);
|
||||
void declTriBus(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declTriQuad(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declTriArray(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
|
||||
void fullBit(uint32_t* oldp, CData newval) { fullBit(oldp - this->oldp(0), newval); }
|
||||
void fullCData(uint32_t* oldp, CData newval, int bits) {
|
||||
fullBus(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
void fullSData(uint32_t* oldp, SData newval, int bits) {
|
||||
fullBus(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
void fullIData(uint32_t* oldp, IData newval, int bits) {
|
||||
fullBus(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
void fullQData(uint32_t* oldp, QData newval, int bits) {
|
||||
fullQuad(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
void fullWData(uint32_t* oldp, const WData* newvalp, int bits) {
|
||||
fullArray(oldp - this->oldp(0), newvalp, bits);
|
||||
}
|
||||
void fullDouble(uint32_t* oldp, double newval) { fullDouble(oldp - this->oldp(0), newval); }
|
||||
|
||||
inline void chgBit(uint32_t* oldp, CData newval) { chgBit(oldp - this->oldp(0), newval); }
|
||||
inline void chgCData(uint32_t* oldp, CData newval, int bits) {
|
||||
chgBus(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
inline void chgSData(uint32_t* oldp, SData newval, int bits) {
|
||||
chgBus(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
inline void chgIData(uint32_t* oldp, IData newval, int bits) {
|
||||
chgBus(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
inline void chgQData(uint32_t* oldp, QData newval, int bits) {
|
||||
chgQuad(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
inline void chgWData(uint32_t* oldp, const WData* newvalp, int bits) {
|
||||
chgArray(oldp - this->oldp(0), newvalp, bits);
|
||||
}
|
||||
inline void chgDouble(uint32_t* oldp, double newval) {
|
||||
chgDouble(oldp - this->oldp(0), newval);
|
||||
}
|
||||
|
||||
// Inside dumping routines, dump one signal, faster when not inlined
|
||||
// due to code size reduction.
|
||||
void fullBit(uint32_t code, const uint32_t newval);
|
||||
void fullBus(uint32_t code, const uint32_t newval, int bits);
|
||||
void fullQuad(uint32_t code, const uint64_t newval, int bits);
|
||||
void fullArray(uint32_t code, const uint32_t* newvalp, int bits);
|
||||
void fullArray(uint32_t code, const uint64_t* newvalp, int bits);
|
||||
void fullTriBit(uint32_t code, const uint32_t newval, const uint32_t newtri);
|
||||
void fullTriBus(uint32_t code, const uint32_t newval, const uint32_t newtri, int bits);
|
||||
void fullTriQuad(uint32_t code, const uint64_t newval, const uint64_t newtri, int bits);
|
||||
void fullTriArray(uint32_t code, const uint32_t* newvalp, const uint32_t* newtrip, int bits);
|
||||
void fullDouble(uint32_t code, const double newval);
|
||||
|
||||
// Inside dumping routines, dump one signal if it has changed.
|
||||
// We do want to inline these to avoid calls when the value did not change.
|
||||
inline void chgBit(uint32_t code, const uint32_t newval) {
|
||||
const uint32_t diff = oldp(code)[0] ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullBit(code, newval);
|
||||
}
|
||||
inline void chgBus(uint32_t code, const uint32_t newval, int bits) {
|
||||
const uint32_t diff = oldp(code)[0] ^ newval;
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits == 32 || (diff & ((1U << bits) - 1)))) {
|
||||
fullBus(code, newval, bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgQuad(uint32_t code, const uint64_t newval, int bits) {
|
||||
const uint64_t diff = (*(reinterpret_cast<uint64_t*>(oldp(code)))) ^ newval;
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits == 64 || (diff & ((1ULL << bits) - 1)))) {
|
||||
fullQuad(code, newval, bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgArray(uint32_t code, const uint32_t* newvalp, int bits) {
|
||||
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) {
|
||||
if (VL_UNLIKELY(oldp(code)[word] ^ newvalp[word])) {
|
||||
fullArray(code, newvalp, bits);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgArray(uint32_t code, const uint64_t* newvalp, int bits) {
|
||||
for (int word = 0; word < (((bits - 1) / 64) + 1); ++word) {
|
||||
if (VL_UNLIKELY(*(reinterpret_cast<uint64_t*>(oldp(code + 2 * word)))
|
||||
^ newvalp[word])) {
|
||||
fullArray(code, newvalp, bits);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriBit(uint32_t code, const uint32_t newval, const uint32_t newtri) {
|
||||
const uint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
// Verilator 3.510 and newer provide clean input, so the below
|
||||
// is only for back compatibility
|
||||
if (VL_UNLIKELY(diff & 1)) { // Change after clean?
|
||||
fullTriBit(code, newval, newtri);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriBus(uint32_t code, const uint32_t newval, const uint32_t newtri, int bits) {
|
||||
const uint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits == 32 || (diff & ((1U << bits) - 1)))) {
|
||||
fullTriBus(code, newval, newtri, bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriQuad(uint32_t code, const uint64_t newval, const uint64_t newtri, int bits) {
|
||||
const uint64_t diff = (((*(reinterpret_cast<uint64_t*>(oldp(code)))) ^ newval)
|
||||
| ((*(reinterpret_cast<uint64_t*>(oldp(code + 1)))) ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits == 64 || (diff & ((1ULL << bits) - 1)))) {
|
||||
fullTriQuad(code, newval, newtri, bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriArray(uint32_t code, const uint32_t* newvalp, const uint32_t* newtrip,
|
||||
int bits) {
|
||||
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) {
|
||||
if (VL_UNLIKELY((oldp(code)[word * 2] ^ newvalp[word])
|
||||
| (oldp(code)[word * 2 + 1] ^ newtrip[word]))) {
|
||||
fullTriArray(code, newvalp, newtrip, bits);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgDouble(uint32_t code, const double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
if (VL_UNLIKELY((*(reinterpret_cast<double*>(oldp(code)))) != newval)) {
|
||||
fullDouble(code, newval);
|
||||
}
|
||||
}
|
||||
|
||||
// METHODS
|
||||
// Old/standalone API only
|
||||
void evcd(bool flag) { m_evcd = flag; }
|
||||
#endif // VL_TRACE_VCD_OLD_API
|
||||
};
|
||||
|
||||
#ifndef DOXYGEN
|
||||
// Declare specializations here they are used in VerilatedVcdC just below
|
||||
template <> void VerilatedTrace<VerilatedVcd>::dump(uint64_t timeui);
|
||||
template <> void VerilatedTrace<VerilatedVcd>::set_time_unit(const char* unitp);
|
||||
template <> void VerilatedTrace<VerilatedVcd>::set_time_unit(const std::string& unit);
|
||||
template <> void VerilatedTrace<VerilatedVcd>::set_time_resolution(const char* unitp);
|
||||
template <> void VerilatedTrace<VerilatedVcd>::set_time_resolution(const std::string& unit);
|
||||
template <> void VerilatedTrace<VerilatedVcd>::dumpvars(int level, const std::string& hier);
|
||||
#endif // DOXYGEN
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcdC
|
||||
/// Class representing a VCD dump file in C standalone (no SystemC)
|
||||
@@ -344,7 +259,6 @@ public:
|
||||
/// Destruct, flush, and close the dump
|
||||
virtual ~VerilatedVcdC() { close(); }
|
||||
|
||||
public:
|
||||
// METHODS - User called
|
||||
|
||||
/// Return if file is open
|
||||
@@ -357,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
|
||||
@@ -396,16 +314,6 @@ public:
|
||||
|
||||
// Internal class access
|
||||
inline VerilatedVcd* spTrace() { return &m_sptrace; }
|
||||
|
||||
#ifdef VL_TRACE_VCD_OLD_API
|
||||
//=========================================================================
|
||||
// Note: These are only for testing for backward compatibility with foreign
|
||||
// code and is not used by Verilator. Do not use these as there is no
|
||||
// guarantee of functionality.
|
||||
|
||||
// Use evcd format
|
||||
void evcd(bool flag) VL_MT_UNSAFE_ONE { m_sptrace.evcd(flag); }
|
||||
#endif
|
||||
};
|
||||
|
||||
#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 )
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#define VERILATOR_VERILATED_VCD_SC_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated_sc.h"
|
||||
#include "verilated_vcd_c.h"
|
||||
|
||||
@@ -54,7 +55,7 @@ public:
|
||||
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
|
||||
}
|
||||
/// Destruct, flush, and close the dump
|
||||
virtual ~VerilatedVcdSc() { close(); }
|
||||
virtual ~VerilatedVcdSc() /*override*/ { close(); }
|
||||
|
||||
// METHODS - for SC kernel
|
||||
// Called by SystemC simulate()
|
||||
@@ -63,6 +64,7 @@ public:
|
||||
}
|
||||
|
||||
// Override VerilatedVcdC. Must be called after starting simulation.
|
||||
// cppcheck-suppress missingOverride // GCC won't accept override
|
||||
virtual void open(const char* filename) /*override*/ VL_MT_SAFE;
|
||||
|
||||
private:
|
||||
@@ -98,9 +100,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 )
|
||||
|
||||
+50
-48
@@ -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>
|
||||
@@ -81,8 +82,9 @@ public:
|
||||
// To simplify our free list, we use a size large enough for all derived types
|
||||
// We reserve word zero for the next pointer, as that's safer in case a
|
||||
// dangling reference to the original remains around.
|
||||
static const size_t chunk = 96;
|
||||
if (VL_UNCOVERABLE(size > chunk)) VL_FATAL_MT(__FILE__, __LINE__, "", "increase chunk");
|
||||
static constexpr size_t CHUNK_SIZE = 96;
|
||||
if (VL_UNCOVERABLE(size > CHUNK_SIZE))
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "increase CHUNK_SIZE");
|
||||
if (VL_LIKELY(t_freeHead)) {
|
||||
uint8_t* const newp = t_freeHead;
|
||||
t_freeHead = *(reinterpret_cast<uint8_t**>(newp));
|
||||
@@ -90,7 +92,7 @@ public:
|
||||
return newp + 8;
|
||||
}
|
||||
// +8: 8 bytes for next
|
||||
uint8_t* newp = reinterpret_cast<uint8_t*>(::operator new(chunk + 8));
|
||||
uint8_t* newp = reinterpret_cast<uint8_t*>(::operator new(CHUNK_SIZE + 8));
|
||||
*(reinterpret_cast<uint32_t*>(newp)) = activeMagic();
|
||||
return newp + 8;
|
||||
}
|
||||
@@ -134,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 {
|
||||
@@ -153,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 {
|
||||
@@ -167,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; }
|
||||
};
|
||||
|
||||
@@ -199,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();
|
||||
@@ -213,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(); }
|
||||
};
|
||||
|
||||
@@ -228,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 {
|
||||
@@ -245,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;
|
||||
@@ -267,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 {
|
||||
@@ -307,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) {
|
||||
@@ -317,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; }
|
||||
@@ -338,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];
|
||||
@@ -363,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)) {
|
||||
@@ -404,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;
|
||||
@@ -437,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 {
|
||||
@@ -451,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
-23
@@ -40,6 +40,7 @@
|
||||
#ifdef __GNUC__
|
||||
# define VL_ATTR_ALIGNED(alignment) __attribute__((aligned(alignment)))
|
||||
# define VL_ATTR_ALWINLINE __attribute__((always_inline))
|
||||
# define VL_ATTR_NOINLINE __attribute__((noinline))
|
||||
# define VL_ATTR_COLD __attribute__((cold))
|
||||
# define VL_ATTR_HOT __attribute__((hot))
|
||||
# define VL_ATTR_NORETURN __attribute__((noreturn))
|
||||
@@ -82,6 +83,9 @@
|
||||
#ifndef VL_ATTR_ALWINLINE
|
||||
# define VL_ATTR_ALWINLINE ///< Attribute to inline, even when not optimizing
|
||||
#endif
|
||||
#ifndef VL_ATTR_NOINLINE
|
||||
# define VL_ATTR_NOINLINE ///< Attribute to never inline, even when optimizing
|
||||
#endif
|
||||
#ifndef VL_ATTR_COLD
|
||||
# define VL_ATTR_COLD ///< Attribute that function is rarely executed
|
||||
#endif
|
||||
@@ -214,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
|
||||
|
||||
@@ -241,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
|
||||
|
||||
//=========================================================================
|
||||
@@ -442,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); \
|
||||
}
|
||||
@@ -517,12 +530,20 @@ 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)
|
||||
@@ -532,10 +553,16 @@ template <typename T> struct reverse_wrapper {
|
||||
};
|
||||
|
||||
// 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
|
||||
|
||||
//=========================================================================
|
||||
|
||||
+10
-9
@@ -29,10 +29,11 @@
|
||||
#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>
|
||||
@@ -75,7 +76,7 @@ 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()
|
||||
@@ -290,13 +291,13 @@ private:
|
||||
// STATE
|
||||
LatchDetectGraph m_graph; // Graph used to detect latches in combo always
|
||||
// VISITORS
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
virtual 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) {
|
||||
virtual void visit(AstNodeIf* nodep) override {
|
||||
if (!nodep->isBoundsCheck()) {
|
||||
LatchDetectGraphVertex* const parentp = m_graph.currentp();
|
||||
LatchDetectGraphVertex* const branchp = m_graph.addPathVertex(parentp, "BRANCH", true);
|
||||
@@ -308,7 +309,7 @@ private:
|
||||
}
|
||||
}
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) { iterateChildren(nodep); }
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -317,7 +318,7 @@ public:
|
||||
iterate(nodep);
|
||||
m_graph.latchCheck(nodep, kwd == VAlwaysKwd::ALWAYS_LATCH);
|
||||
}
|
||||
virtual ~ActiveLatchCheckVisitor() {}
|
||||
~ActiveLatchCheckVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -387,7 +388,7 @@ public:
|
||||
: m_check{check} {
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~ActiveDlyVisitor() override = default;
|
||||
~ActiveDlyVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -530,7 +531,7 @@ 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};
|
||||
@@ -598,7 +599,7 @@ private:
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit ActiveVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~ActiveVisitor() override = default;
|
||||
~ActiveVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
+3
-2
@@ -26,11 +26,12 @@
|
||||
#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"
|
||||
|
||||
//######################################################################
|
||||
// Active class functions
|
||||
|
||||
+2
-1
@@ -17,9 +17,10 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Assert.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
//######################################################################
|
||||
|
||||
+1
-1
@@ -20,8 +20,8 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3Error.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
|
||||
+2
-1
@@ -20,9 +20,10 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3AssertPre.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
//######################################################################
|
||||
// Assert class functions
|
||||
|
||||
+65
-14
@@ -18,10 +18,11 @@
|
||||
#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>
|
||||
@@ -205,7 +206,7 @@ string AstNode::prettyName(const string& namein) {
|
||||
|
||||
string AstNode::prettyTypeName() const {
|
||||
if (name() == "") return typeName();
|
||||
return string(typeName()) + " '" + prettyName() + "'";
|
||||
return std::string{typeName()} + " '" + prettyName() + "'";
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
@@ -281,13 +282,13 @@ void AstNode::addNextHere(AstNode* newp) {
|
||||
// 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 +438,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 +498,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 +566,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 +632,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) {
|
||||
@@ -758,7 +804,7 @@ void AstNode::deleteTree() {
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
void* AstNode::operator new(size_t size) {
|
||||
// Optimization note: Aligning to cache line is a loss, due to lost packing
|
||||
const AstNode* const objp = static_cast<AstNode*>(::operator new(size));
|
||||
AstNode* const objp = static_cast<AstNode*>(::operator new(size));
|
||||
V3Broken::addNewed(objp);
|
||||
return objp;
|
||||
}
|
||||
@@ -1248,3 +1294,8 @@ void VNDeleter::doDeletes() {
|
||||
for (AstNode* const nodep : m_deleteps) nodep->deleteTree();
|
||||
m_deleteps.clear();
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// VNVisitor
|
||||
|
||||
#include "V3Ast__gen_visitor_defns.h" // From ./astgen
|
||||
|
||||
+337
-174
@@ -20,11 +20,13 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Broken.h"
|
||||
#include "V3Error.h"
|
||||
#include "V3FileLine.h"
|
||||
#include "V3Number.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Broken.h"
|
||||
#include "V3Number.h"
|
||||
|
||||
#include "V3Ast__gen_classes.h" // From ./astgen
|
||||
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
@@ -34,8 +36,6 @@
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "V3Ast__gen_classes.h" // From ./astgen
|
||||
// Things like:
|
||||
// class V3AstNode;
|
||||
|
||||
@@ -95,7 +95,7 @@ public:
|
||||
// const char* ascii() const {...};
|
||||
enum en m_e;
|
||||
// cppcheck-suppress uninitVar // responsibility of each subclass
|
||||
inline VNType() {}
|
||||
inline VNType() = default;
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline VNType(en _e)
|
||||
: m_e{_e} {}
|
||||
@@ -997,7 +997,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
//
|
||||
VNumRange() {}
|
||||
VNumRange() = default;
|
||||
VNumRange(int hi, int lo, bool littleEndian) { init(hi, lo, littleEndian); }
|
||||
VNumRange(int left, int right)
|
||||
: m_left{left}
|
||||
@@ -1123,7 +1123,7 @@ class VNUser final {
|
||||
} m_u;
|
||||
|
||||
public:
|
||||
VNUser() {}
|
||||
VNUser() = default;
|
||||
// non-explicit:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
VNUser(int i) {
|
||||
@@ -1133,7 +1133,7 @@ public:
|
||||
explicit VNUser(void* p) { m_u.up = p; }
|
||||
~VNUser() = default;
|
||||
// Casters
|
||||
template <class T> //
|
||||
template <class T>
|
||||
typename std::enable_if<std::is_pointer<T>::value, T>::type to() const {
|
||||
return reinterpret_cast<T>(m_u.up);
|
||||
}
|
||||
@@ -1297,10 +1297,8 @@ public:
|
||||
/// Return edited nodep; see comments in V3Ast.cpp
|
||||
AstNode* iterateSubtreeReturnEdits(AstNode* nodep);
|
||||
|
||||
#include "V3Ast__gen_visitor.h" // From ./astgen
|
||||
// Things like:
|
||||
// virtual void visit(AstBreak* nodep) { visit((AstNodeStmt*)(nodep)); }
|
||||
// virtual void visit(AstNodeStmt* nodep) { visit((AstNode*)(nodep)); }
|
||||
virtual void visit(AstNode* nodep) = 0;
|
||||
#include "V3Ast__gen_visitor_decls.h" // From ./astgen
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -1334,25 +1332,27 @@ inline std::ostream& operator<<(std::ostream& os, const VNRelinker& rhs) {
|
||||
return os;
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// Callback base class to determine if node matches some formula
|
||||
// ######################################################################
|
||||
// Callback base class to determine if node matches some formula
|
||||
|
||||
class VNodeMatcher VL_NOT_FINAL {
|
||||
public:
|
||||
virtual bool nodeMatch(const AstNode* nodep) const { return true; }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// AstNode -- Base type of all Ast types
|
||||
// ######################################################################
|
||||
// AstNode -- Base type of all Ast types
|
||||
|
||||
// Prefetch a node.
|
||||
#define ASTNODE_PREFETCH_NON_NULL(nodep) \
|
||||
do { \
|
||||
VL_PREFETCH_RD(&((nodep)->m_nextp)); \
|
||||
VL_PREFETCH_RD(&((nodep)->m_type)); \
|
||||
} while (false)
|
||||
// The if() makes it faster, even though prefetch won't fault on null pointers
|
||||
#define ASTNODE_PREFETCH(nodep) \
|
||||
do { \
|
||||
if (nodep) { \
|
||||
VL_PREFETCH_RD(&((nodep)->m_nextp)); \
|
||||
VL_PREFETCH_RD(&((nodep)->m_type)); \
|
||||
} \
|
||||
if (nodep) ASTNODE_PREFETCH_NON_NULL(nodep); \
|
||||
} while (false)
|
||||
|
||||
class AstNode VL_NOT_FINAL {
|
||||
@@ -1509,6 +1509,16 @@ public:
|
||||
AstNode* firstAbovep() const { // Returns nullptr when second or later in list
|
||||
return ((backp() && backp()->nextp() != this) ? backp() : nullptr);
|
||||
}
|
||||
// isFirstInMyListOfStatements(n) -- implemented by child classes:
|
||||
// AstNodeBlock, AstCaseItem, AstNodeIf, AstNodeFTask, and possibly others.
|
||||
virtual bool isFirstInMyListOfStatements(AstNode* n) const { return false; }
|
||||
// isStandaloneBodyStmt == Do we need a ; on generated cpp for this node?
|
||||
bool isStandaloneBodyStmt() {
|
||||
return (!firstAbovep() // we're 2nd or later in the list, so yes need ;
|
||||
|
||||
// If we're first in the list, check what backp() thinks of us:
|
||||
|| (backp() && backp()->isFirstInMyListOfStatements(this)));
|
||||
}
|
||||
uint8_t brokenState() const { return m_brokenState; }
|
||||
void brokenState(uint8_t value) { m_brokenState = value; }
|
||||
|
||||
@@ -1555,7 +1565,7 @@ public:
|
||||
static string dedotName(const string& namein); // Name with dots removed
|
||||
static string prettyName(const string& namein); // Name for printing out to the user
|
||||
static string prettyNameQ(const string& namein) { // Quoted pretty name (for errors)
|
||||
return string("'") + prettyName(namein) + "'";
|
||||
return std::string{"'"} + prettyName(namein) + "'";
|
||||
}
|
||||
static string
|
||||
encodeName(const string& namein); // Encode user name into internal C representation
|
||||
@@ -1717,13 +1727,13 @@ public:
|
||||
void dtypeSetVoid() { dtypep(findVoidDType()); }
|
||||
|
||||
// Data type locators
|
||||
AstNodeDType* findBitDType() { return findBasicDType(VBasicDTypeKwd::LOGIC); }
|
||||
AstNodeDType* findDoubleDType() { return findBasicDType(VBasicDTypeKwd::DOUBLE); }
|
||||
AstNodeDType* findStringDType() { return findBasicDType(VBasicDTypeKwd::STRING); }
|
||||
AstNodeDType* findSigned32DType() { return findBasicDType(VBasicDTypeKwd::INTEGER); }
|
||||
AstNodeDType* findUInt32DType() { return findBasicDType(VBasicDTypeKwd::UINT32); }
|
||||
AstNodeDType* findUInt64DType() { return findBasicDType(VBasicDTypeKwd::UINT64); }
|
||||
AstNodeDType* findCHandleDType() { return findBasicDType(VBasicDTypeKwd::CHANDLE); }
|
||||
AstNodeDType* findBitDType() const { return findBasicDType(VBasicDTypeKwd::LOGIC); }
|
||||
AstNodeDType* findDoubleDType() const { return findBasicDType(VBasicDTypeKwd::DOUBLE); }
|
||||
AstNodeDType* findStringDType() const { return findBasicDType(VBasicDTypeKwd::STRING); }
|
||||
AstNodeDType* findSigned32DType() const { return findBasicDType(VBasicDTypeKwd::INTEGER); }
|
||||
AstNodeDType* findUInt32DType() const { return findBasicDType(VBasicDTypeKwd::UINT32); }
|
||||
AstNodeDType* findUInt64DType() const { return findBasicDType(VBasicDTypeKwd::UINT64); }
|
||||
AstNodeDType* findCHandleDType() const { return findBasicDType(VBasicDTypeKwd::CHANDLE); }
|
||||
AstNodeDType* findEmptyQueueDType() const;
|
||||
AstNodeDType* findVoidDType() const;
|
||||
AstNodeDType* findQueueIndexDType() const;
|
||||
@@ -1787,7 +1797,6 @@ public:
|
||||
void deleteTree(); // Always deletes the next link
|
||||
void checkTree(); // User Interface version
|
||||
void checkIter() const;
|
||||
void clearIter() { m_iterpp = nullptr; }
|
||||
void dumpPtrs(std::ostream& os = std::cout) const;
|
||||
void dumpTree(std::ostream& os = std::cout, const string& indent = " ",
|
||||
int maxDepth = 0) const;
|
||||
@@ -1858,16 +1867,12 @@ private:
|
||||
|
||||
// For internal use only.
|
||||
// Note: specializations for particular node types are provided by 'astgen'
|
||||
template <typename T> inline static bool privateTypeTest(const AstNode* nodep);
|
||||
template <typename T>
|
||||
inline static bool privateTypeTest(const AstNode* nodep);
|
||||
|
||||
// For internal use only.
|
||||
// Note: specializations for particular node types are provided below
|
||||
template <typename T_Node> inline static bool privateMayBeUnder(const AstNode* nodep) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// For internal use only.
|
||||
template <typename TargetType, typename DeclType> constexpr static bool uselessCast() {
|
||||
template <typename TargetType, typename DeclType>
|
||||
constexpr static bool uselessCast() {
|
||||
using NonRef = typename std::remove_reference<DeclType>::type;
|
||||
using NonPtr = typename std::remove_pointer<NonRef>::type;
|
||||
using NonCV = typename std::remove_cv<NonPtr>::type;
|
||||
@@ -1875,7 +1880,8 @@ private:
|
||||
}
|
||||
|
||||
// For internal use only.
|
||||
template <typename TargetType, typename DeclType> constexpr static bool impossibleCast() {
|
||||
template <typename TargetType, typename DeclType>
|
||||
constexpr static bool impossibleCast() {
|
||||
using NonRef = typename std::remove_reference<DeclType>::type;
|
||||
using NonPtr = typename std::remove_pointer<NonRef>::type;
|
||||
using NonCV = typename std::remove_cv<NonPtr>::type;
|
||||
@@ -1884,20 +1890,23 @@ private:
|
||||
|
||||
public:
|
||||
// For use via the VN_IS macro only
|
||||
template <typename T, typename E> inline static bool privateIs(const AstNode* nodep) {
|
||||
template <typename T, typename E>
|
||||
inline static bool privateIs(const AstNode* nodep) {
|
||||
static_assert(!uselessCast<T, E>(), "Unnecessary VN_IS, node known to have target type.");
|
||||
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_IS, node cannot be this type.");
|
||||
return nodep && privateTypeTest<T>(nodep);
|
||||
}
|
||||
|
||||
// For use via the VN_CAST macro only
|
||||
template <typename T, typename E> inline static T* privateCast(AstNode* nodep) {
|
||||
template <typename T, typename E>
|
||||
inline static T* privateCast(AstNode* nodep) {
|
||||
static_assert(!uselessCast<T, E>(),
|
||||
"Unnecessary VN_CAST, node known to have target type.");
|
||||
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_CAST, node cannot be this type.");
|
||||
return nodep && privateTypeTest<T>(nodep) ? reinterpret_cast<T*>(nodep) : nullptr;
|
||||
}
|
||||
template <typename T, typename E> inline static const T* privateCast(const AstNode* nodep) {
|
||||
template <typename T, typename E>
|
||||
inline static const T* privateCast(const AstNode* nodep) {
|
||||
static_assert(!uselessCast<T, E>(),
|
||||
"Unnecessary VN_CAST, node known to have target type.");
|
||||
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_CAST, node cannot be this type.");
|
||||
@@ -1905,7 +1914,8 @@ public:
|
||||
}
|
||||
|
||||
// For use via the VN_AS macro only
|
||||
template <typename T, typename E> inline static T* privateAs(AstNode* nodep) {
|
||||
template <typename T, typename E>
|
||||
inline static T* privateAs(AstNode* nodep) {
|
||||
static_assert(!uselessCast<T, E>(), "Unnecessary VN_AS, node known to have target type.");
|
||||
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_AS, node cannot be this type.");
|
||||
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
|
||||
@@ -1913,7 +1923,8 @@ public:
|
||||
<< "'");
|
||||
return reinterpret_cast<T*>(nodep);
|
||||
}
|
||||
template <typename T, typename E> inline static const T* privateAs(const AstNode* nodep) {
|
||||
template <typename T, typename E>
|
||||
inline static const T* privateAs(const AstNode* nodep) {
|
||||
static_assert(!uselessCast<T, E>(), "Unnecessary VN_AS, node known to have target type.");
|
||||
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_AS, node cannot be this type.");
|
||||
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
|
||||
@@ -1924,167 +1935,117 @@ public:
|
||||
|
||||
// Predicate that returns true if the given 'nodep' might have a descendant of type 'T_Node'.
|
||||
// This is conservative and is used to speed up traversals.
|
||||
template <typename T_Node> inline static bool mayBeUnder(const AstNode* nodep) {
|
||||
// Note: specializations for particular node types are provided below
|
||||
template <typename T_Node>
|
||||
static bool mayBeUnder(const AstNode* nodep) {
|
||||
static_assert(!std::is_const<T_Node>::value,
|
||||
"Type parameter 'T_Node' should not be const qualified");
|
||||
static_assert(std::is_base_of<AstNode, T_Node>::value,
|
||||
"Type parameter 'T_Node' must be a subtype of AstNode");
|
||||
return privateMayBeUnder<T_Node>(nodep);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Predicate that is true for node subtypes 'T_Node' that do not have any children
|
||||
// This is conservative and is used to speed up traversals.
|
||||
// Note: specializations for particular node types are provided below
|
||||
template <typename T_Node>
|
||||
static constexpr bool isLeaf() {
|
||||
static_assert(!std::is_const<T_Node>::value,
|
||||
"Type parameter 'T_Node' should not be const qualified");
|
||||
static_assert(std::is_base_of<AstNode, T_Node>::value,
|
||||
"Type parameter 'T_Node' must be a subtype of AstNode");
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T_Arg, bool VisitNext>
|
||||
static void foreachImpl(
|
||||
// Using std::conditional for const correctness in the public 'foreach' functions
|
||||
typename std::conditional<std::is_const<T_Arg>::value, const AstNode*, AstNode*>::type
|
||||
nodep,
|
||||
std::function<void(T_Arg*)> f) {
|
||||
// Using std::conditional for const correctness in the public 'foreach' functions
|
||||
template <typename T_Arg>
|
||||
using ConstCorrectAstNode =
|
||||
typename std::conditional<std::is_const<T_Arg>::value, const AstNode, AstNode>::type;
|
||||
|
||||
// Note: Using a loop to iterate the nextp() chain, instead of tail recursion, because
|
||||
// debug builds don't eliminate tail calls, causing stack overflow on long lists of nodes.
|
||||
do {
|
||||
// Prefetch children and next
|
||||
ASTNODE_PREFETCH(nodep->op1p());
|
||||
ASTNODE_PREFETCH(nodep->op2p());
|
||||
ASTNODE_PREFETCH(nodep->op3p());
|
||||
ASTNODE_PREFETCH(nodep->op4p());
|
||||
if /* TODO: 'constexpr' in C++17 */ (VisitNext) ASTNODE_PREFETCH(nodep->nextp());
|
||||
template <typename T_Arg>
|
||||
inline static void foreachImpl(ConstCorrectAstNode<T_Arg>* nodep,
|
||||
const std::function<void(T_Arg*)>& f, bool visitNext);
|
||||
|
||||
// Apply function in pre-order
|
||||
if (privateTypeTest<typename std::remove_const<T_Arg>::type>(nodep)) {
|
||||
f(static_cast<T_Arg*>(nodep));
|
||||
}
|
||||
template <typename T_Arg, bool Default>
|
||||
inline static bool predicateImpl(ConstCorrectAstNode<T_Arg>* nodep,
|
||||
const std::function<bool(T_Arg*)>& p);
|
||||
|
||||
// Traverse children (including their 'nextp()' chains), unless futile
|
||||
if (mayBeUnder<typename std::remove_const<T_Arg>::type>(nodep)) {
|
||||
if (AstNode* const op1p = nodep->op1p()) foreachImpl<T_Arg, true>(op1p, f);
|
||||
if (AstNode* const op2p = nodep->op2p()) foreachImpl<T_Arg, true>(op2p, f);
|
||||
if (AstNode* const op3p = nodep->op3p()) foreachImpl<T_Arg, true>(op3p, f);
|
||||
if (AstNode* const op4p = nodep->op4p()) foreachImpl<T_Arg, true>(op4p, f);
|
||||
}
|
||||
|
||||
// Traverse 'nextp()' chain if requested
|
||||
if /* TODO: 'constexpr' in C++17 */ (VisitNext) {
|
||||
nodep = nodep->nextp();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (nodep);
|
||||
}
|
||||
|
||||
template <typename T_Arg, bool Default, bool VisitNext>
|
||||
static bool predicateImpl(
|
||||
// Using std::conditional for const correctness in the public 'foreach' functions
|
||||
typename std::conditional<std::is_const<T_Arg>::value, const AstNode*, AstNode*>::type
|
||||
nodep,
|
||||
std::function<bool(T_Arg*)> p) {
|
||||
|
||||
// Note: Using a loop to iterate the nextp() chain, instead of tail recursion, because
|
||||
// debug builds don't eliminate tail calls, causing stack overflow on long lists of nodes.
|
||||
do {
|
||||
// Prefetch children and next
|
||||
ASTNODE_PREFETCH(nodep->op1p());
|
||||
ASTNODE_PREFETCH(nodep->op2p());
|
||||
ASTNODE_PREFETCH(nodep->op3p());
|
||||
ASTNODE_PREFETCH(nodep->op4p());
|
||||
if /* TODO: 'constexpr' in C++17 */ (VisitNext) ASTNODE_PREFETCH(nodep->nextp());
|
||||
|
||||
// Apply function in pre-order
|
||||
if (privateTypeTest<typename std::remove_const<T_Arg>::type>(nodep)) {
|
||||
if (p(static_cast<T_Arg*>(nodep)) != Default) return !Default;
|
||||
}
|
||||
|
||||
// Traverse children (including their 'nextp()' chains), unless futile
|
||||
if (mayBeUnder<typename std::remove_const<T_Arg>::type>(nodep)) {
|
||||
if (AstNode* const op1p = nodep->op1p()) {
|
||||
if (predicateImpl<T_Arg, Default, true>(op1p, p) != Default) return !Default;
|
||||
}
|
||||
if (AstNode* const op2p = nodep->op2p()) {
|
||||
if (predicateImpl<T_Arg, Default, true>(op2p, p) != Default) return !Default;
|
||||
}
|
||||
if (AstNode* const op3p = nodep->op3p()) {
|
||||
if (predicateImpl<T_Arg, Default, true>(op3p, p) != Default) return !Default;
|
||||
}
|
||||
if (AstNode* const op4p = nodep->op4p()) {
|
||||
if (predicateImpl<T_Arg, Default, true>(op4p, p) != Default) return !Default;
|
||||
}
|
||||
}
|
||||
|
||||
// Traverse 'nextp()' chain if requested
|
||||
if /* TODO: 'constexpr' in C++17 */ (VisitNext) {
|
||||
nodep = nodep->nextp();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (nodep);
|
||||
|
||||
return Default;
|
||||
}
|
||||
|
||||
template <typename T_Node> constexpr static void checkTypeParameter() {
|
||||
template <typename T_Node>
|
||||
constexpr static bool checkTypeParameter() {
|
||||
static_assert(!std::is_const<T_Node>::value,
|
||||
"Type parameter 'T_Node' should not be const qualified");
|
||||
static_assert(std::is_base_of<AstNode, T_Node>::value,
|
||||
"Type parameter 'T_Node' must be a subtype of AstNode");
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
// Traverse subtree and call given function 'f' in pre-order on each node that has type
|
||||
// 'T_Node'. Prefer 'foreach' over simple VNVisitor that only needs to handle a single (or a
|
||||
// few) node types, as it's easier to write, but more importantly, the dispatch to the
|
||||
// operation function in 'foreach' should be completely predictable by branch target caches in
|
||||
// modern CPUs, while it is basically unpredictable for VNVisitor.
|
||||
template <typename T_Node> void foreach (std::function<void(T_Node*)> f) {
|
||||
checkTypeParameter<T_Node>();
|
||||
foreachImpl<T_Node, /* VisitNext: */ false>(this, f);
|
||||
// 'T_Node'. The node passd to the function 'f' can be removed or replaced, but other editing
|
||||
// of the iterated tree is not safe. Prefer 'foreach' over simple VNVisitor that only needs to
|
||||
// handle a single (or a few) node types, as it's easier to write, but more importantly, the
|
||||
// dispatch to the operation function in 'foreach' should be completely predictable by branch
|
||||
// target caches in modern CPUs, while it is basically unpredictable for VNVisitor.
|
||||
template <typename T_Node>
|
||||
void foreach (std::function<void(T_Node*)> f) {
|
||||
static_assert(checkTypeParameter<T_Node>(), "Invalid type parameter 'T_Node'");
|
||||
foreachImpl<T_Node>(this, f, /* visitNext: */ false);
|
||||
}
|
||||
|
||||
// Same as above, but for 'const' nodes
|
||||
template <typename T_Node> void foreach (std::function<void(const T_Node*)> f) const {
|
||||
checkTypeParameter<T_Node>();
|
||||
foreachImpl<const T_Node, /* VisitNext: */ false>(this, f);
|
||||
template <typename T_Node>
|
||||
void foreach (std::function<void(const T_Node*)> f) const {
|
||||
static_assert(checkTypeParameter<T_Node>(), "Invalid type parameter 'T_Node'");
|
||||
foreachImpl<const T_Node>(this, f, /* visitNext: */ false);
|
||||
}
|
||||
|
||||
// Same as 'foreach' but also follows 'this->nextp()'
|
||||
template <typename T_Node> void foreachAndNext(std::function<void(T_Node*)> f) {
|
||||
checkTypeParameter<T_Node>();
|
||||
foreachImpl<T_Node, /* VisitNext: */ true>(this, f);
|
||||
template <typename T_Node>
|
||||
void foreachAndNext(std::function<void(T_Node*)> f) {
|
||||
static_assert(checkTypeParameter<T_Node>(), "Invalid type parameter 'T_Node'");
|
||||
foreachImpl<T_Node>(this, f, /* visitNext: */ true);
|
||||
}
|
||||
|
||||
// Same as 'foreach' but also follows 'this->nextp()'
|
||||
template <typename T_Node> void foreachAndNext(std::function<void(const T_Node*)> f) const {
|
||||
checkTypeParameter<T_Node>();
|
||||
foreachImpl<const T_Node, /* VisitNext: */ true>(this, f);
|
||||
template <typename T_Node>
|
||||
void foreachAndNext(std::function<void(const T_Node*)> f) const {
|
||||
static_assert(checkTypeParameter<T_Node>(), "Invalid type parameter 'T_Node'");
|
||||
foreachImpl<const T_Node>(this, f, /* visitNext: */ true);
|
||||
}
|
||||
|
||||
// Given a predicate function 'p' return true if and only if there exists a node of type
|
||||
// 'T_Node' that satisfies the predicate 'p'. Returns false if no node of type 'T_Node' is
|
||||
// present. Traversal is performed in some arbitrary order and is terminated as soon as the
|
||||
// result can be determined.
|
||||
template <typename T_Node> bool exists(std::function<bool(T_Node*)> p) {
|
||||
checkTypeParameter<T_Node>();
|
||||
return predicateImpl<T_Node, /* Default: */ false, /* VisitNext: */ false>(this, p);
|
||||
template <typename T_Node>
|
||||
bool exists(std::function<bool(T_Node*)> p) {
|
||||
static_assert(checkTypeParameter<T_Node>(), "Invalid type parameter 'T_Node'");
|
||||
return predicateImpl<T_Node, /* Default: */ false>(this, p);
|
||||
}
|
||||
|
||||
// Same as above, but for 'const' nodes
|
||||
template <typename T_Node> void exists(std::function<bool(const T_Node*)> p) const {
|
||||
checkTypeParameter<T_Node>();
|
||||
return predicateImpl<const T_Node, /* Default: */ false, /* VisitNext: */ false>(this, p);
|
||||
template <typename T_Node>
|
||||
bool exists(std::function<bool(const T_Node*)> p) const {
|
||||
static_assert(checkTypeParameter<T_Node>(), "Invalid type parameter 'T_Node'");
|
||||
return predicateImpl<const T_Node, /* Default: */ false>(this, p);
|
||||
}
|
||||
|
||||
// Given a predicate function 'p' return true if and only if all nodes of type
|
||||
// 'T_Node' satisfy the predicate 'p'. Returns true if no node of type 'T_Node' is
|
||||
// present. Traversal is performed in some arbitrary order and is terminated as soon as the
|
||||
// result can be determined.
|
||||
template <typename T_Node> bool forall(std::function<bool(T_Node*)> p) {
|
||||
checkTypeParameter<T_Node>();
|
||||
return predicateImpl<T_Node, /* Default: */ true, /* VisitNext: */ false>(this, p);
|
||||
template <typename T_Node>
|
||||
bool forall(std::function<bool(T_Node*)> p) {
|
||||
static_assert(checkTypeParameter<T_Node>(), "Invalid type parameter 'T_Node'");
|
||||
return predicateImpl<T_Node, /* Default: */ true>(this, p);
|
||||
}
|
||||
|
||||
// Same as above, but for 'const' nodes
|
||||
template <typename T_Node> void forall(std::function<bool(const T_Node*)> p) const {
|
||||
checkTypeParameter<T_Node>();
|
||||
return predicateImpl<const T_Node, /* Default: */ true, /* VisitNext: */ false>(this, p);
|
||||
template <typename T_Node>
|
||||
bool forall(std::function<bool(const T_Node*)> p) const {
|
||||
static_assert(checkTypeParameter<T_Node>(), "Invalid type parameter 'T_Node'");
|
||||
return predicateImpl<const T_Node, /* Default: */ true>(this, p);
|
||||
}
|
||||
|
||||
int nodeCount() const {
|
||||
@@ -2098,22 +2059,214 @@ public:
|
||||
// Specialisations of privateTypeTest
|
||||
#include "V3Ast__gen_impl.h" // From ./astgen
|
||||
|
||||
// Specializations of privateMayBeUnder
|
||||
template <> inline bool AstNode::privateMayBeUnder<AstCell>(const AstNode* nodep) {
|
||||
// Specializations of AstNode::mayBeUnder
|
||||
template <>
|
||||
inline bool AstNode::mayBeUnder<AstCell>(const AstNode* nodep) {
|
||||
return !VN_IS(nodep, NodeStmt) && !VN_IS(nodep, NodeMath);
|
||||
}
|
||||
template <> inline bool AstNode::privateMayBeUnder<AstNodeAssign>(const AstNode* nodep) {
|
||||
template <>
|
||||
inline bool AstNode::mayBeUnder<AstNodeAssign>(const AstNode* nodep) {
|
||||
return !VN_IS(nodep, NodeMath);
|
||||
}
|
||||
template <> inline bool AstNode::privateMayBeUnder<AstVarScope>(const AstNode* nodep) {
|
||||
return !VN_IS(nodep, NodeStmt) && !VN_IS(nodep, NodeMath);
|
||||
template <>
|
||||
inline bool AstNode::mayBeUnder<AstVarScope>(const AstNode* nodep) {
|
||||
if (VN_IS(nodep, VarScope)) return false; // Should not nest
|
||||
if (VN_IS(nodep, Var)) return false;
|
||||
if (VN_IS(nodep, Active)) return false;
|
||||
if (VN_IS(nodep, NodeStmt)) return false;
|
||||
if (VN_IS(nodep, NodeMath)) return false;
|
||||
return true;
|
||||
}
|
||||
template <> inline bool AstNode::privateMayBeUnder<AstExecGraph>(const AstNode* nodep) {
|
||||
template <>
|
||||
inline bool AstNode::mayBeUnder<AstExecGraph>(const AstNode* nodep) {
|
||||
if (VN_IS(nodep, ExecGraph)) return false; // Should not nest
|
||||
if (VN_IS(nodep, NodeStmt)) return false; // Should be directly under CFunc
|
||||
return true;
|
||||
}
|
||||
|
||||
// Specializations of AstNode::isLeaf
|
||||
template <>
|
||||
constexpr bool AstNode::isLeaf<AstNodeVarRef>() {
|
||||
return true;
|
||||
}
|
||||
template <>
|
||||
constexpr bool AstNode::isLeaf<AstVarRef>() {
|
||||
return true;
|
||||
}
|
||||
template <>
|
||||
constexpr bool AstNode::isLeaf<AstVarXRef>() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// foreach implementation
|
||||
template <typename T_Arg>
|
||||
void AstNode::foreachImpl(ConstCorrectAstNode<T_Arg>* nodep, const std::function<void(T_Arg*)>& f,
|
||||
bool visitNext) {
|
||||
// Checking the function is bound up front eliminates this check from the loop at invocation
|
||||
if (!f) {
|
||||
nodep->v3fatal("AstNode::foreach called with unbound function"); // LCOV_EXCL_LINE
|
||||
} else {
|
||||
// Pre-order traversal implemented directly (without recursion) for speed reasons. The very
|
||||
// first iteration (the one that operates on the input nodep) is special, as we might or
|
||||
// might not need to enqueue nodep->nextp() depending on VisitNext, while in all other
|
||||
// iterations, we do want to enqueue nodep->nextp(). Duplicating code (via
|
||||
// 'foreachImplVisit') for the initial iteration here to avoid an extra branch in the loop
|
||||
|
||||
using T_Arg_NonConst = typename std::remove_const<T_Arg>::type;
|
||||
using Node = ConstCorrectAstNode<T_Arg>;
|
||||
|
||||
// Traversal stack
|
||||
std::vector<Node*> stack; // Kept as a vector for easy resizing
|
||||
Node** basep = nullptr; // Pointer to base of stack
|
||||
Node** topp = nullptr; // Pointer to top of stack
|
||||
Node** limp = nullptr; // Pointer to stack limit (when need growing)
|
||||
|
||||
// We prefetch this far into the stack
|
||||
constexpr int prefetchDistance = 2;
|
||||
|
||||
// Grow stack to given size
|
||||
const auto grow = [&](size_t size) VL_ATTR_ALWINLINE {
|
||||
const ptrdiff_t occupancy = topp - basep;
|
||||
stack.resize(size);
|
||||
basep = stack.data() + prefetchDistance;
|
||||
topp = basep + occupancy;
|
||||
limp = basep + size - 5; // We push max 5 items per iteration
|
||||
};
|
||||
|
||||
// Initial stack size
|
||||
grow(32);
|
||||
|
||||
// We want some non-null pointers at the beginning. These will be prefetched, but not
|
||||
// visited, so the root node will suffice. This eliminates needing branches in the loop.
|
||||
for (int i = -prefetchDistance; i; ++i) basep[i] = nodep;
|
||||
|
||||
// Visit given node, enqueue children for traversal
|
||||
const auto visit = [&](Node* currp) VL_ATTR_ALWINLINE {
|
||||
// Type test this node
|
||||
if (AstNode::privateTypeTest<T_Arg_NonConst>(currp)) {
|
||||
// Call the client function
|
||||
f(static_cast<T_Arg*>(currp));
|
||||
// Short circuit if iterating leaf nodes
|
||||
if VL_CONSTEXPR_CXX17 (isLeaf<T_Arg_NonConst>()) return;
|
||||
}
|
||||
|
||||
// Enqueue children for traversal, unless futile
|
||||
if (mayBeUnder<T_Arg_NonConst>(currp)) {
|
||||
if (AstNode* const op4p = currp->op4p()) *topp++ = op4p;
|
||||
if (AstNode* const op3p = currp->op3p()) *topp++ = op3p;
|
||||
if (AstNode* const op2p = currp->op2p()) *topp++ = op2p;
|
||||
if (AstNode* const op1p = currp->op1p()) *topp++ = op1p;
|
||||
}
|
||||
};
|
||||
|
||||
// Enqueue the next of the root node, if required
|
||||
if (visitNext && nodep->nextp()) *topp++ = nodep->nextp();
|
||||
|
||||
// Visit the root node
|
||||
visit(nodep);
|
||||
|
||||
// Visit the rest of the tree
|
||||
while (VL_LIKELY(topp > basep)) {
|
||||
// Pop next node in the traversal
|
||||
Node* const headp = *--topp;
|
||||
|
||||
// Prefetch in case we are ascending the tree
|
||||
ASTNODE_PREFETCH_NON_NULL(topp[-prefetchDistance]);
|
||||
|
||||
// Ensure we have stack space for nextp and the 4 children
|
||||
if (VL_UNLIKELY(topp >= limp)) grow(stack.size() * 2);
|
||||
|
||||
// Enqueue the next node
|
||||
if (headp->nextp()) *topp++ = headp->nextp();
|
||||
|
||||
// Visit the head node
|
||||
visit(headp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// predicate implementation
|
||||
template <typename T_Arg, bool Default>
|
||||
bool AstNode::predicateImpl(ConstCorrectAstNode<T_Arg>* nodep,
|
||||
const std::function<bool(T_Arg*)>& p) {
|
||||
// Implementation similar to foreach, but abort traversal as soon as result is determined.
|
||||
if (!p) {
|
||||
nodep->v3fatal("AstNode::foreach called with unbound function"); // LCOV_EXCL_LINE
|
||||
} else {
|
||||
using T_Arg_NonConst = typename std::remove_const<T_Arg>::type;
|
||||
using Node = ConstCorrectAstNode<T_Arg>;
|
||||
|
||||
// Traversal stack
|
||||
std::vector<Node*> stack; // Kept as a vector for easy resizing
|
||||
Node** basep = nullptr; // Pointer to base of stack
|
||||
Node** topp = nullptr; // Pointer to top of stack
|
||||
Node** limp = nullptr; // Pointer to stack limit (when need growing)
|
||||
|
||||
// We prefetch this far into the stack
|
||||
constexpr int prefetchDistance = 2;
|
||||
|
||||
// Grow stack to given size
|
||||
const auto grow = [&](size_t size) VL_ATTR_ALWINLINE {
|
||||
const ptrdiff_t occupancy = topp - basep;
|
||||
stack.resize(size);
|
||||
basep = stack.data() + prefetchDistance;
|
||||
topp = basep + occupancy;
|
||||
limp = basep + size - 5; // We push max 5 items per iteration
|
||||
};
|
||||
|
||||
// Initial stack size
|
||||
grow(32);
|
||||
|
||||
// We want some non-null pointers at the beginning. These will be prefetched, but not
|
||||
// visited, so the root node will suffice. This eliminates needing branches in the loop.
|
||||
for (int i = -prefetchDistance; i; ++i) basep[i] = nodep;
|
||||
|
||||
// Visit given node, enqueue children for traversal, return true if result determined.
|
||||
const auto visit = [&](Node* currp) VL_ATTR_ALWINLINE {
|
||||
// Type test this node
|
||||
if (AstNode::privateTypeTest<T_Arg_NonConst>(currp)) {
|
||||
// Call the client function
|
||||
if (p(static_cast<T_Arg*>(currp)) != Default) return true;
|
||||
// Short circuit if iterating leaf nodes
|
||||
if VL_CONSTEXPR_CXX17 (isLeaf<T_Arg_NonConst>()) return false;
|
||||
}
|
||||
|
||||
// Enqueue children for traversal, unless futile
|
||||
if (mayBeUnder<T_Arg_NonConst>(currp)) {
|
||||
if (AstNode* const op4p = currp->op4p()) *topp++ = op4p;
|
||||
if (AstNode* const op3p = currp->op3p()) *topp++ = op3p;
|
||||
if (AstNode* const op2p = currp->op2p()) *topp++ = op2p;
|
||||
if (AstNode* const op1p = currp->op1p()) *topp++ = op1p;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// Visit the root node
|
||||
if (visit(nodep)) return !Default;
|
||||
|
||||
// Visit the rest of the tree
|
||||
while (VL_LIKELY(topp > basep)) {
|
||||
// Pop next node in the traversal
|
||||
Node* const headp = *--topp;
|
||||
|
||||
// Prefetch in case we are ascending the tree
|
||||
ASTNODE_PREFETCH_NON_NULL(topp[-prefetchDistance]);
|
||||
|
||||
// Ensure we have stack space for nextp and the 4 children
|
||||
if (VL_UNLIKELY(topp >= limp)) grow(stack.size() * 2);
|
||||
|
||||
// Enqueue the next node
|
||||
if (headp->nextp()) *topp++ = headp->nextp();
|
||||
|
||||
// Visit the head node
|
||||
if (visit(headp)) return !Default;
|
||||
}
|
||||
|
||||
return Default;
|
||||
}
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const AstNode* rhs) {
|
||||
if (!rhs) {
|
||||
os << "nullptr";
|
||||
@@ -2127,7 +2280,7 @@ inline void VNRelinker::relink(AstNode* newp) { newp->AstNode::relink(this); }
|
||||
//######################################################################
|
||||
|
||||
// VNRef is std::reference_wrapper that can only hold AstNode subtypes
|
||||
template <typename T_Node> //
|
||||
template <typename T_Node>
|
||||
class VNRef final : public std::reference_wrapper<T_Node> {
|
||||
static_assert(std::is_base_of<AstNode, T_Node>::value,
|
||||
"Type parameter 'T_Node' must be a subtype of AstNode");
|
||||
@@ -2150,16 +2303,16 @@ static_assert(sizeof(VNRef<AstNode>) == sizeof(std::reference_wrapper<AstNode>),
|
||||
// without having to copy nodes into the collections.
|
||||
|
||||
// Forward declaration to avoid including V3Hasher.h which needs V3Ast.h (this file).
|
||||
size_t V3HasherUncachedHash(AstNode&);
|
||||
size_t V3HasherUncachedHash(const AstNode&);
|
||||
|
||||
// Specialization of std::hash for VNRef
|
||||
template <typename T_Node> //
|
||||
template <typename T_Node>
|
||||
struct std::hash<VNRef<T_Node>> final {
|
||||
size_t operator()(VNRef<T_Node> r) const { return V3HasherUncachedHash(r); }
|
||||
};
|
||||
|
||||
// Specialization of std::equal_to for VNRef
|
||||
template <typename T_Node> //
|
||||
template <typename T_Node>
|
||||
struct std::equal_to<VNRef<T_Node>> final {
|
||||
size_t operator()(VNRef<T_Node> ra, VNRef<T_Node> rb) const {
|
||||
return ra.get().sameTree(&(rb.get()));
|
||||
@@ -2423,6 +2576,7 @@ public:
|
||||
AstNode* stmtsp() const { return op1p(); } // op1 = List of statements
|
||||
void addStmtsp(AstNode* nodep) { addNOp1p(nodep); }
|
||||
bool unnamed() const { return m_unnamed; }
|
||||
bool isFirstInMyListOfStatements(AstNode* nodep) const override { return nodep == stmtsp(); }
|
||||
};
|
||||
|
||||
class AstNodePreSel VL_NOT_FINAL : public AstNode {
|
||||
@@ -2444,7 +2598,7 @@ public:
|
||||
void fromp(AstNode* nodep) { return setOp1p(nodep); }
|
||||
void rhsp(AstNode* nodep) { return setOp2p(nodep); }
|
||||
void thsp(AstNode* nodep) { return setOp3p(nodep); }
|
||||
void attrp(AstAttrOf* nodep) { return setOp4p((AstNode*)nodep); }
|
||||
void attrp(AstAttrOf* nodep) { return setOp4p(reinterpret_cast<AstNode*>(nodep)); }
|
||||
// METHODS
|
||||
virtual bool same(const AstNode*) const override { return true; }
|
||||
};
|
||||
@@ -2488,10 +2642,12 @@ public:
|
||||
|
||||
class AstNodeAssign VL_NOT_FINAL : public AstNodeStmt {
|
||||
protected:
|
||||
AstNodeAssign(VNType t, FileLine* fl, AstNode* lhsp, AstNode* rhsp)
|
||||
AstNodeAssign(VNType t, FileLine* fl, AstNode* lhsp, AstNode* rhsp,
|
||||
AstNode* timingControlp = nullptr)
|
||||
: AstNodeStmt{t, fl} {
|
||||
setOp1p(rhsp);
|
||||
setOp2p(lhsp);
|
||||
addNOp3p(timingControlp);
|
||||
dtypeFrom(lhsp);
|
||||
}
|
||||
|
||||
@@ -2502,6 +2658,9 @@ public:
|
||||
// So iteration hits the RHS which is "earlier" in execution order, it's op1, not op2
|
||||
AstNode* rhsp() const { return op1p(); } // op1 = Assign from
|
||||
AstNode* lhsp() const { return op2p(); } // op2 = Assign to
|
||||
// op3 = Timing controls (delays, event controls)
|
||||
AstNode* timingControlp() const { return op3p(); }
|
||||
void addTimingControlp(AstNode* const np) { addNOp3p(np); }
|
||||
void rhsp(AstNode* np) { setOp1p(np); }
|
||||
void lhsp(AstNode* np) { setOp2p(np); }
|
||||
virtual bool hasDType() const override { return true; }
|
||||
@@ -2531,7 +2690,7 @@ public:
|
||||
AstNode* bodysp() const { return op4p(); } // op4 = body of loop
|
||||
virtual bool isGateOptimizable() const override { return false; }
|
||||
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
class AstNodeIf VL_NOT_FINAL : public AstNodeStmt {
|
||||
@@ -2558,11 +2717,14 @@ public:
|
||||
virtual bool isGateOptimizable() const override { return false; }
|
||||
virtual bool isGateDedupable() const override { return true; }
|
||||
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
void branchPred(VBranchPred flag) { m_branchPred = flag; }
|
||||
VBranchPred branchPred() const { return m_branchPred; }
|
||||
void isBoundsCheck(bool flag) { m_isBoundsCheck = flag; }
|
||||
bool isBoundsCheck() const { return m_isBoundsCheck; }
|
||||
bool isFirstInMyListOfStatements(AstNode* n) const override {
|
||||
return n == ifsp() || n == elsesp();
|
||||
}
|
||||
};
|
||||
|
||||
class AstNodeCase VL_NOT_FINAL : public AstNodeStmt {
|
||||
@@ -2600,7 +2762,7 @@ protected:
|
||||
: AstNodeMath{t, fl}
|
||||
, m_access{access}
|
||||
, m_name{name} {
|
||||
this->varp(nullptr);
|
||||
varp(nullptr);
|
||||
}
|
||||
AstNodeVarRef(VNType t, FileLine* fl, const string& name, AstVar* varp, const VAccess& access)
|
||||
: AstNodeMath{t, fl}
|
||||
@@ -3089,6 +3251,7 @@ public:
|
||||
bool isVirtual() const { return m_virtual; }
|
||||
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
|
||||
VLifetime lifetime() const { return m_lifetime; }
|
||||
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
|
||||
};
|
||||
|
||||
class AstNodeFTaskRef VL_NOT_FINAL : public AstNodeStmt {
|
||||
|
||||
+30
-14
@@ -481,7 +481,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 +489,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 {
|
||||
@@ -659,7 +659,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 +673,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 + ">";
|
||||
@@ -1309,7 +1312,8 @@ void AstClassPackage::cloneRelink() {
|
||||
}
|
||||
void AstClass::insertCache(AstNode* nodep) {
|
||||
const bool doit = (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef)
|
||||
|| (VN_IS(nodep, NodeFTask) && !VN_AS(nodep, NodeFTask)->isExternProto()));
|
||||
|| (VN_IS(nodep, NodeFTask) && !VN_AS(nodep, NodeFTask)->isExternProto())
|
||||
|| VN_IS(nodep, CFunc));
|
||||
if (doit) {
|
||||
if (m_members.find(nodep->name()) != m_members.end()) {
|
||||
nodep->v3error("Duplicate declaration of member name: " << nodep->prettyNameQ());
|
||||
@@ -1320,7 +1324,15 @@ void AstClass::insertCache(AstNode* nodep) {
|
||||
}
|
||||
void AstClass::repairCache() {
|
||||
clearCache();
|
||||
for (AstNode* itemp = membersp(); itemp; itemp = itemp->nextp()) { insertCache(itemp); }
|
||||
for (auto* itemp = membersp(); itemp; itemp = itemp->nextp()) {
|
||||
if (const auto* const scopep = VN_CAST(itemp, Scope)) {
|
||||
for (auto* blockp = scopep->blocksp(); blockp; blockp = blockp->nextp()) {
|
||||
insertCache(blockp);
|
||||
}
|
||||
} else {
|
||||
insertCache(itemp);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool AstClass::isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp) {
|
||||
// TAIL RECURSIVE
|
||||
@@ -1343,7 +1355,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 << " -> ";
|
||||
@@ -1374,7 +1386,7 @@ void AstEnumItemRef::dump(std::ostream& str) const {
|
||||
}
|
||||
}
|
||||
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();
|
||||
@@ -1423,7 +1435,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 {
|
||||
@@ -1436,7 +1448,7 @@ void AstMemberSel::dump(std::ostream& str) const {
|
||||
}
|
||||
}
|
||||
void AstMethodCall::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
this->AstNodeFTaskRef::dump(str);
|
||||
if (isStatement()) str << " [STMT]";
|
||||
str << " -> ";
|
||||
if (taskp()) {
|
||||
@@ -1522,7 +1534,7 @@ void AstRefDType::dump(std::ostream& str) const {
|
||||
}
|
||||
}
|
||||
void AstNodeUOrStructDType::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
this->AstNodeDType::dump(str);
|
||||
if (packed()) str << " [PACKED]";
|
||||
if (isFourstate()) str << " [4STATE]";
|
||||
}
|
||||
@@ -1613,7 +1625,7 @@ void AstPackageImport::dump(std::ostream& str) const {
|
||||
str << " -> " << packagep();
|
||||
}
|
||||
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); }
|
||||
@@ -1675,6 +1687,10 @@ string AstQueueDType::prettyDTypeName() const {
|
||||
if (boundConst()) str += ":" + cvtToStr(boundConst());
|
||||
return str + "]";
|
||||
}
|
||||
void AstWildcardArrayDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "[*]";
|
||||
}
|
||||
void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "[]";
|
||||
@@ -1758,7 +1774,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]";
|
||||
}
|
||||
@@ -1825,7 +1841,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]";
|
||||
@@ -1851,7 +1867,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 {
|
||||
|
||||
+252
-96
@@ -77,10 +77,11 @@ public:
|
||||
, m_num(this, width, value) {
|
||||
initWithNumber();
|
||||
}
|
||||
class DtypedValue {}; // for creator type-overload selection
|
||||
AstConst(FileLine* fl, DtypedValue, AstNodeDType* nodedtypep, uint32_t value)
|
||||
class DTyped {}; // for creator type-overload selection
|
||||
// Zero/empty constant with a type matching nodetypep
|
||||
AstConst(FileLine* fl, DTyped, const AstNodeDType* nodedtypep)
|
||||
: ASTGEN_SUPER_Const(fl)
|
||||
, m_num(this, nodedtypep->width(), value, nodedtypep->widthSized()) {
|
||||
, m_num(this, nodedtypep) {
|
||||
initWithNumber();
|
||||
}
|
||||
class StringToParse {}; // for creator type-overload selection
|
||||
@@ -186,20 +187,20 @@ public:
|
||||
class AstEmpty final : public AstNode {
|
||||
// Represents something missing, e.g. a missing argument in FOREACH
|
||||
public:
|
||||
AstEmpty(FileLine* fl)
|
||||
explicit AstEmpty(FileLine* fl)
|
||||
: ASTGEN_SUPER_Empty(fl) {}
|
||||
ASTNODE_NODE_FUNCS(Empty)
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
class AstEmptyQueue final : public AstNodeMath {
|
||||
public:
|
||||
AstEmptyQueue(FileLine* fl)
|
||||
explicit AstEmptyQueue(FileLine* fl)
|
||||
: ASTGEN_SUPER_EmptyQueue(fl) {}
|
||||
ASTNODE_NODE_FUNCS(EmptyQueue)
|
||||
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
virtual string emitVerilog() override { return "{}"; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
virtual bool cleanOut() const override { return true; }
|
||||
};
|
||||
|
||||
@@ -246,7 +247,7 @@ public:
|
||||
bool littleEndian() const { return leftConst() < rightConst(); }
|
||||
virtual void dump(std::ostream& str) const override;
|
||||
virtual string emitC() { V3ERROR_NA_RETURN(""); }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
class AstBracketRange final : public AstNodeRange {
|
||||
@@ -260,7 +261,7 @@ public:
|
||||
ASTNODE_NODE_FUNCS(BracketRange)
|
||||
virtual string emitC() { V3ERROR_NA_RETURN(""); }
|
||||
virtual string emitVerilog() { V3ERROR_NA_RETURN(""); }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
// Will be removed in V3Width, which relies on this
|
||||
// being a child not a dtype pointed node
|
||||
virtual bool maybePointedTo() const override { return false; }
|
||||
@@ -275,7 +276,18 @@ public:
|
||||
ASTNODE_NODE_FUNCS(UnsizedRange)
|
||||
virtual string emitC() { V3ERROR_NA_RETURN(""); }
|
||||
virtual string emitVerilog() { return "[]"; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
class AstWildcardRange final : public AstNodeRange {
|
||||
// Wildcard range specification, for wildcard index type associative arrays
|
||||
public:
|
||||
explicit AstWildcardRange(FileLine* fl)
|
||||
: ASTGEN_SUPER_WildcardRange(fl) {}
|
||||
ASTNODE_NODE_FUNCS(WildcardRange)
|
||||
virtual string emitC() { V3ERROR_NA_RETURN(""); }
|
||||
virtual string emitVerilog() { return "[*]"; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
class AstGatePin final : public AstNodeMath {
|
||||
@@ -649,7 +661,7 @@ class AstDynArrayDType final : public AstNodeDType {
|
||||
// Dynamic array data type, ie "[]"
|
||||
// Children: DTYPE (moved to refDTypep() in V3Width)
|
||||
private:
|
||||
AstNodeDType* m_refDTypep; // Elements of this type (after widthing)
|
||||
AstNodeDType* m_refDTypep = nullptr; // Elements of this type (after widthing)
|
||||
public:
|
||||
AstDynArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_DynArrayDType(fl) {
|
||||
@@ -819,6 +831,62 @@ public:
|
||||
virtual bool isCompound() const override { return true; }
|
||||
};
|
||||
|
||||
class AstWildcardArrayDType final : public AstNodeDType {
|
||||
// Wildcard index type associative array data type, ie "some_dtype var_name [*]"
|
||||
// Children: DTYPE (moved to refDTypep() in V3Width)
|
||||
private:
|
||||
AstNodeDType* m_refDTypep; // Elements of this type (after widthing)
|
||||
public:
|
||||
AstWildcardArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_WildcardArrayDType(fl) {
|
||||
childDTypep(dtp); // Only for parser
|
||||
refDTypep(nullptr);
|
||||
dtypep(nullptr); // V3Width will resolve
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(WildcardArrayDType)
|
||||
virtual const char* broken() const override {
|
||||
BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|
||||
|| (!m_refDTypep && childDTypep())));
|
||||
return nullptr;
|
||||
}
|
||||
virtual void cloneRelink() override {
|
||||
if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep();
|
||||
}
|
||||
virtual bool same(const AstNode* samep) const override {
|
||||
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
if (!asamep->subDTypep()) return false;
|
||||
return (subDTypep() == asamep->subDTypep());
|
||||
}
|
||||
virtual bool similarDType(AstNodeDType* samep) const override {
|
||||
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
return type() == samep->type() && asamep->subDTypep()
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
||||
}
|
||||
virtual void dumpSmall(std::ostream& str) const override;
|
||||
virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
// op1 = Range of variable
|
||||
AstNodeDType* childDTypep() const { return VN_AS(op1p(), NodeDType); }
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
virtual AstNodeDType* subDTypep() const override {
|
||||
return m_refDTypep ? m_refDTypep : childDTypep();
|
||||
}
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||
virtual AstNodeDType* virtRefDTypep() const override { return m_refDTypep; }
|
||||
virtual void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
|
||||
// METHODS
|
||||
virtual AstBasicDType* basicp() const override { return subDTypep()->basicp(); }
|
||||
virtual AstNodeDType* skipRefp() const override { return (AstNodeDType*)this; }
|
||||
virtual AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
virtual int widthAlignBytes() const override {
|
||||
return sizeof(std::map<std::string, std::string>);
|
||||
}
|
||||
virtual int widthTotalBytes() const override {
|
||||
return sizeof(std::map<std::string, std::string>);
|
||||
}
|
||||
virtual bool isCompound() const override { return true; }
|
||||
};
|
||||
|
||||
class AstBasicDType final : public AstNodeDType {
|
||||
// Builtin atomic/vectored data type
|
||||
// Children: RANGE (converted to constant in V3Width)
|
||||
@@ -975,7 +1043,7 @@ class AstConstDType final : public AstNodeDType {
|
||||
// ConstDType are removed in V3LinkLValue and become AstVar::isConst.
|
||||
// When more generic types are supported AstConstDType will be propagated further.
|
||||
private:
|
||||
AstNodeDType* m_refDTypep; // Inherit from this base data type
|
||||
AstNodeDType* m_refDTypep = nullptr; // Inherit from this base data type
|
||||
public:
|
||||
AstConstDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_ConstDType(fl) {
|
||||
@@ -1134,7 +1202,7 @@ class AstQueueDType final : public AstNodeDType {
|
||||
// Queue array data type, ie "[ $ ]"
|
||||
// Children: DTYPE (moved to refDTypep() in V3Width)
|
||||
private:
|
||||
AstNodeDType* m_refDTypep; // Elements of this type (after widthing)
|
||||
AstNodeDType* m_refDTypep = nullptr; // Elements of this type (after widthing)
|
||||
public:
|
||||
AstQueueDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstNode* boundp)
|
||||
: ASTGEN_SUPER_QueueDType(fl) {
|
||||
@@ -1328,7 +1396,7 @@ class AstMemberDType final : public AstNodeDType {
|
||||
// A member of a struct/union
|
||||
// PARENT: AstNodeUOrStructDType
|
||||
private:
|
||||
AstNodeDType* m_refDTypep; // Elements of this type (after widthing)
|
||||
AstNodeDType* m_refDTypep = nullptr; // Elements of this type (after widthing)
|
||||
string m_name; // Name of variable
|
||||
string m_tag; // Holds the string of the verilator tag -- used in XML output.
|
||||
int m_lsb = -1; // Within this level's packed struct, the LSB of the first bit of the member
|
||||
@@ -1517,8 +1585,8 @@ class AstEnumDType final : public AstNodeDType {
|
||||
// Children: ENUMVALUEs
|
||||
private:
|
||||
string m_name; // Name from upper typedef, if any
|
||||
AstNodeDType* m_refDTypep; // Elements are of this type after V3Width
|
||||
const int m_uniqueNum;
|
||||
AstNodeDType* m_refDTypep = nullptr; // Elements are of this type after V3Width
|
||||
const int m_uniqueNum = 0;
|
||||
|
||||
public:
|
||||
AstEnumDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstNode* itemsp)
|
||||
@@ -1641,7 +1709,7 @@ public:
|
||||
return true;
|
||||
} // esp for V3Const::ifSameAssign
|
||||
virtual bool isPredictOptimizable() const override { return true; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
// Special operators
|
||||
// Return base var (or const) nodep dereferences
|
||||
@@ -1682,7 +1750,45 @@ public:
|
||||
return true;
|
||||
} // esp for V3Const::ifSameAssign
|
||||
virtual bool isPredictOptimizable() const override { return false; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
};
|
||||
|
||||
class AstWildcardSel final : public AstNodeSel {
|
||||
// Parents: math|stmt
|
||||
// Children: varref|arraysel, math
|
||||
private:
|
||||
void init(AstNode* fromp) {
|
||||
if (fromp && VN_IS(fromp->dtypep()->skipRefp(), WildcardArrayDType)) {
|
||||
// Strip off array to find what array references
|
||||
dtypeFrom(VN_AS(fromp->dtypep()->skipRefp(), WildcardArrayDType)->subDTypep());
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
AstWildcardSel(FileLine* fl, AstNode* fromp, AstNode* bitp)
|
||||
: ASTGEN_SUPER_WildcardSel(fl, fromp, bitp) {
|
||||
init(fromp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(WildcardSel)
|
||||
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) override {
|
||||
return new AstWildcardSel{this->fileline(), lhsp, rhsp};
|
||||
}
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
|
||||
V3ERROR_NA;
|
||||
}
|
||||
virtual string emitVerilog() override { return "%k(%l%f[%r])"; }
|
||||
virtual string emitC() override { return "%li%k[%ri]"; }
|
||||
virtual bool cleanOut() const override { return true; }
|
||||
virtual bool cleanLhs() const override { return false; }
|
||||
virtual bool cleanRhs() const override { return true; }
|
||||
virtual bool sizeMattersLhs() const override { return false; }
|
||||
virtual bool sizeMattersRhs() const override { return false; }
|
||||
virtual bool isGateOptimizable() const override {
|
||||
return true;
|
||||
} // esp for V3Const::ifSameAssign
|
||||
virtual bool isPredictOptimizable() const override { return false; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
};
|
||||
|
||||
@@ -1709,7 +1815,7 @@ public:
|
||||
virtual bool cleanRhs() const override { return true; }
|
||||
virtual bool sizeMattersLhs() const override { return false; }
|
||||
virtual bool sizeMattersRhs() const override { return false; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
class AstSelLoopVars final : public AstNode {
|
||||
@@ -1722,7 +1828,7 @@ public:
|
||||
addNOp2p(elementsp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(SelLoopVars)
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
virtual bool maybePointedTo() const override { return false; }
|
||||
AstNode* fromp() const { return op1p(); }
|
||||
void fromp(AstNode* nodep) { setOp1p(nodep); }
|
||||
@@ -2142,6 +2248,9 @@ public:
|
||||
virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
// op1 = Range of variable
|
||||
AstNodeDType* childDTypep() const { return VN_AS(op1p(), NodeDType); }
|
||||
// op2 = Net delay
|
||||
AstNode* delayp() const { return op2p(); }
|
||||
void delayp(AstNode* const nodep) { setNOp2p(nodep); }
|
||||
AstNodeDType* dtypeSkipRefp() const { return subDTypep()->skipRefp(); }
|
||||
// (Slow) recurse down to find basic data type (Note don't need virtual -
|
||||
// AstVar isn't a NodeDType)
|
||||
@@ -3126,8 +3235,7 @@ public:
|
||||
AstNodeModule* classOrPackagep() const {
|
||||
AstNode* foundp = m_classOrPackageNodep;
|
||||
while (auto* const anodep = VN_CAST(foundp, Typedef)) foundp = anodep->subDTypep();
|
||||
while (auto* const anodep = VN_CAST(foundp, ClassRefDType))
|
||||
foundp = anodep->classOrPackagep();
|
||||
if (auto* const anodep = VN_CAST(foundp, ClassRefDType)) foundp = anodep->classp();
|
||||
return VN_CAST(foundp, NodeModule);
|
||||
}
|
||||
AstPackage* packagep() const { return VN_CAST(classOrPackageNodep(), Package); }
|
||||
@@ -3249,7 +3357,7 @@ public:
|
||||
addNOp2p(exprp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(WithParse)
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
//
|
||||
AstNode* funcrefp() const { return op1p(); }
|
||||
AstNode* exprp() const { return op2p(); }
|
||||
@@ -3269,7 +3377,7 @@ public:
|
||||
, m_name{name}
|
||||
, m_index(index) {}
|
||||
ASTNODE_NODE_FUNCS(LambdaArgRef)
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
virtual string emitVerilog() override { return name(); }
|
||||
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
virtual bool cleanOut() const override { return true; }
|
||||
@@ -3296,7 +3404,7 @@ public:
|
||||
addNOp3p(exprp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(With)
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
virtual bool hasDType() const override { return true; }
|
||||
virtual const char* broken() const override {
|
||||
BROKEN_RTN(!indexArgRefp()); // varp needed to know lambda's arg dtype
|
||||
@@ -3470,19 +3578,20 @@ public:
|
||||
addNOp2p(bodysp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(AlwaysPublic)
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
//
|
||||
AstSenTree* sensesp() const { return VN_AS(op1p(), SenTree); } // op1 = Sensitivity list
|
||||
AstNode* bodysp() const { return op2p(); } // op2 = Statements to evaluate
|
||||
void addStmtp(AstNode* nodep) { addOp2p(nodep); }
|
||||
// Special accessors
|
||||
bool isJustOneBodyStmt() const { return bodysp() && !bodysp()->nextp(); }
|
||||
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == bodysp(); }
|
||||
};
|
||||
|
||||
class AstAssign final : public AstNodeAssign {
|
||||
public:
|
||||
AstAssign(FileLine* fl, AstNode* lhsp, AstNode* rhsp)
|
||||
: ASTGEN_SUPER_Assign(fl, lhsp, rhsp) {
|
||||
AstAssign(FileLine* fl, AstNode* lhsp, AstNode* rhsp, AstNode* timingControlp = nullptr)
|
||||
: ASTGEN_SUPER_Assign(fl, lhsp, rhsp, timingControlp) {
|
||||
dtypeFrom(lhsp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Assign)
|
||||
@@ -3507,8 +3616,8 @@ public:
|
||||
|
||||
class AstAssignDly final : public AstNodeAssign {
|
||||
public:
|
||||
AstAssignDly(FileLine* fl, AstNode* lhsp, AstNode* rhsp)
|
||||
: ASTGEN_SUPER_AssignDly(fl, lhsp, rhsp) {}
|
||||
AstAssignDly(FileLine* fl, AstNode* lhsp, AstNode* rhsp, AstNode* timingControlp = nullptr)
|
||||
: ASTGEN_SUPER_AssignDly(fl, lhsp, rhsp, timingControlp) {}
|
||||
ASTNODE_NODE_FUNCS(AssignDly)
|
||||
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) override {
|
||||
return new AstAssignDly(this->fileline(), lhsp, rhsp);
|
||||
@@ -3801,7 +3910,7 @@ public:
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(CoverToggle)
|
||||
virtual int instrCount() const override { return 3 + INSTR_COUNT_BRANCH + INSTR_COUNT_LD; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
virtual bool isGateOptimizable() const override { return false; }
|
||||
virtual bool isPredictOptimizable() const override { return true; }
|
||||
virtual bool isOutputter() const override {
|
||||
@@ -3817,15 +3926,18 @@ public:
|
||||
class AstDelay final : public AstNodeStmt {
|
||||
// Delay statement
|
||||
public:
|
||||
AstDelay(FileLine* fl, AstNode* lhsp)
|
||||
AstDelay(FileLine* fl, AstNode* lhsp, AstNode* stmtsp)
|
||||
: ASTGEN_SUPER_Delay(fl) {
|
||||
setOp1p(lhsp);
|
||||
setNOp2p(stmtsp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Delay)
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
//
|
||||
AstNode* lhsp() const { return op1p(); } // op2 = Statements to evaluate
|
||||
AstNode* lhsp() const { return op1p(); } // op1 = delay value
|
||||
void lhsp(AstNode* nodep) { setOp1p(nodep); }
|
||||
void stmtsp(AstNode* nodep) { setOp2p(nodep); } // op2 = statements under delay
|
||||
AstNode* stmtsp() const { return op2p(); }
|
||||
};
|
||||
|
||||
class AstGenCase final : public AstNodeCase {
|
||||
@@ -3897,6 +4009,7 @@ public:
|
||||
void condsp(AstNode* nodep) { setOp1p(nodep); }
|
||||
void addBodysp(AstNode* newp) { addOp2p(newp); }
|
||||
bool isDefault() const { return condsp() == nullptr; }
|
||||
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == bodysp(); }
|
||||
};
|
||||
|
||||
class AstSFormatF final : public AstNode {
|
||||
@@ -4026,7 +4139,7 @@ public:
|
||||
virtual bool isPredictOptimizable() const override { return false; }
|
||||
virtual bool isOutputter() const override { return true; }
|
||||
virtual bool cleanOut() const { return true; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
VDumpCtlType ctlType() const { return m_ctlType; }
|
||||
AstNode* exprp() const { return op1p(); } // op2 = Expressions to output
|
||||
void exprp(AstNode* nodep) { setOp1p(nodep); }
|
||||
@@ -4097,7 +4210,7 @@ public:
|
||||
virtual bool isOutputter() const override { return false; }
|
||||
virtual bool cleanOut() const { return false; }
|
||||
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
void fmtp(AstSFormatF* nodep) { addOp1p(nodep); } // op1 = To-String formatter
|
||||
AstSFormatF* fmtp() const { return VN_AS(op1p(), SFormatF); }
|
||||
AstNode* lhsp() const { return op3p(); }
|
||||
@@ -4120,7 +4233,7 @@ public:
|
||||
virtual bool isPure() const override { return true; }
|
||||
virtual bool isOutputter() const override { return false; }
|
||||
virtual int instrCount() const override { return 0; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
AstNode* lhsp() const { return op1p(); } // op1 = Expressions to eval
|
||||
void lhsp(AstNode* nodep) { addOp1p(nodep); } // op1 = Expressions to eval
|
||||
};
|
||||
@@ -4161,7 +4274,7 @@ public:
|
||||
virtual bool isPure() const override { return false; }
|
||||
virtual bool isOutputter() const override { return true; }
|
||||
virtual bool isUnlikely() const override { return true; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
AstNode* filep() const { return op2p(); }
|
||||
void filep(AstNodeVarRef* nodep) { setNOp2p(nodep); }
|
||||
};
|
||||
@@ -4182,7 +4295,7 @@ public:
|
||||
virtual bool isPure() const override { return false; }
|
||||
virtual bool isOutputter() const override { return true; }
|
||||
virtual bool isUnlikely() const override { return true; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
AstNode* filep() const { return op1p(); }
|
||||
AstNode* filenamep() const { return op2p(); }
|
||||
AstNode* modep() const { return op3p(); }
|
||||
@@ -4203,7 +4316,7 @@ public:
|
||||
virtual bool isPure() const override { return false; }
|
||||
virtual bool isOutputter() const override { return true; }
|
||||
virtual bool isUnlikely() const override { return true; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
AstNode* filep() const { return op1p(); }
|
||||
AstNode* filenamep() const { return op2p(); }
|
||||
};
|
||||
@@ -4223,7 +4336,7 @@ public:
|
||||
virtual bool isPure() const override { return false; }
|
||||
virtual bool isOutputter() const override { return true; }
|
||||
virtual bool isUnlikely() const override { return true; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
AstNode* filep() const { return op2p(); }
|
||||
void filep(AstNodeVarRef* nodep) { setNOp2p(nodep); }
|
||||
};
|
||||
@@ -4251,7 +4364,7 @@ public:
|
||||
virtual bool isPure() const override { return false; } // SPECIAL: has 'visual' ordering
|
||||
virtual bool isOutputter() const override { return true; } // SPECIAL: makes output
|
||||
virtual bool cleanOut() const override { return false; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
AstNode* memp() const { return op1p(); }
|
||||
void memp(AstNode* nodep) { setOp1p(nodep); }
|
||||
AstNode* filep() const { return op2p(); }
|
||||
@@ -4280,7 +4393,7 @@ public:
|
||||
virtual bool isOutputter() const override { return true; }
|
||||
virtual bool isUnlikely() const override { return true; }
|
||||
virtual bool cleanOut() const override { return false; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
AstNode* filep() const { return op2p(); }
|
||||
void filep(AstNodeVarRef* nodep) { setNOp2p(nodep); }
|
||||
};
|
||||
@@ -4303,7 +4416,7 @@ public:
|
||||
virtual bool isOutputter() const override { return true; }
|
||||
virtual bool isUnlikely() const override { return true; }
|
||||
virtual bool cleanOut() const override { return false; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
AstNode* filep() const { return op2p(); }
|
||||
void filep(AstNodeVarRef* nodep) { setNOp2p(nodep); }
|
||||
};
|
||||
@@ -4329,7 +4442,7 @@ public:
|
||||
virtual bool isPure() const override { return false; } // SPECIAL: has 'visual' ordering
|
||||
virtual bool isOutputter() const override { return true; } // SPECIAL: makes output
|
||||
virtual bool cleanOut() const override { return false; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
AstNode* filep() const { return op2p(); }
|
||||
void filep(AstNode* nodep) { setOp2p(nodep); }
|
||||
AstNode* offset() const { return op3p(); }
|
||||
@@ -4493,7 +4606,7 @@ public:
|
||||
virtual bool isPure() const override { return false; }
|
||||
virtual bool isOutputter() const override { return true; }
|
||||
virtual bool isUnlikely() const override { return true; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
AstNode* lhsp() const { return op1p(); }
|
||||
};
|
||||
|
||||
@@ -4514,7 +4627,7 @@ public:
|
||||
virtual bool isOutputter() const override { return true; }
|
||||
virtual bool isUnlikely() const override { return true; }
|
||||
virtual bool cleanOut() const override { return true; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
AstNode* lhsp() const { return op1p(); }
|
||||
};
|
||||
|
||||
@@ -4535,7 +4648,7 @@ public:
|
||||
virtual bool isPredictOptimizable() const override { return false; }
|
||||
virtual bool isPure() const override { return !outp(); }
|
||||
virtual bool cleanOut() const override { return true; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
AstNode* searchp() const { return op1p(); } // op1 = Search expression
|
||||
void searchp(AstNode* nodep) { setOp1p(nodep); }
|
||||
AstNode* outp() const { return op2p(); } // op2 = Expressions to output
|
||||
@@ -4545,26 +4658,21 @@ public:
|
||||
class AstTestPlusArgs final : public AstNodeMath {
|
||||
// Parents: expr
|
||||
// Child: variable to set. If nullptr then this is a $test$plusargs instead of $value$plusargs
|
||||
private:
|
||||
string m_text;
|
||||
|
||||
public:
|
||||
AstTestPlusArgs(FileLine* fl, const string& text)
|
||||
: ASTGEN_SUPER_TestPlusArgs(fl)
|
||||
, m_text{text} {}
|
||||
AstTestPlusArgs(FileLine* fl, AstNode* searchp)
|
||||
: ASTGEN_SUPER_TestPlusArgs(fl) {
|
||||
setOp1p(searchp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(TestPlusArgs)
|
||||
virtual string name() const override { return m_text; }
|
||||
virtual string verilogKwd() const override { return "$test$plusargs"; }
|
||||
virtual string emitVerilog() override { return verilogKwd(); }
|
||||
virtual string emitC() override { return "VL_VALUEPLUSARGS_%nq(%lw, %P, nullptr)"; }
|
||||
virtual bool isGateOptimizable() const override { return false; }
|
||||
virtual bool isPredictOptimizable() const override { return false; }
|
||||
virtual bool cleanOut() const override { return true; }
|
||||
virtual bool same(const AstNode* samep) const override {
|
||||
return text() == static_cast<const AstTestPlusArgs*>(samep)->text();
|
||||
}
|
||||
string text() const { return m_text; } // * = Text to display
|
||||
void text(const string& text) { m_text = text; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
AstNode* searchp() const { return op1p(); } // op1 = Search expression
|
||||
void searchp(AstNode* nodep) { setOp1p(nodep); }
|
||||
};
|
||||
|
||||
class AstGenFor final : public AstNodeFor {
|
||||
@@ -4586,7 +4694,8 @@ public:
|
||||
AstNode* bodysp() const { return op4p(); } // op4 = body of loop
|
||||
virtual bool isGateOptimizable() const override { return false; }
|
||||
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == bodysp(); }
|
||||
};
|
||||
|
||||
class AstRepeat final : public AstNodeStmt {
|
||||
@@ -4603,7 +4712,8 @@ public:
|
||||
return false;
|
||||
} // Not relevant - converted to FOR
|
||||
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == bodysp(); }
|
||||
};
|
||||
|
||||
class AstWait final : public AstNodeStmt {
|
||||
@@ -4615,6 +4725,7 @@ public:
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Wait)
|
||||
AstNode* bodysp() const { return op3p(); } // op3 = body of loop
|
||||
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == bodysp(); }
|
||||
};
|
||||
|
||||
class AstWhile final : public AstNodeStmt {
|
||||
@@ -4636,11 +4747,12 @@ public:
|
||||
void addIncsp(AstNode* newp) { addOp4p(newp); }
|
||||
virtual bool isGateOptimizable() const override { return false; }
|
||||
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
// Stop statement searchback here
|
||||
virtual void addBeforeStmt(AstNode* newp, AstNode* belowp) override;
|
||||
// Stop statement searchback here
|
||||
virtual void addNextStmt(AstNode* newp, AstNode* belowp) override;
|
||||
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == bodysp(); }
|
||||
};
|
||||
|
||||
class AstBreak final : public AstNodeStmt {
|
||||
@@ -4756,7 +4868,7 @@ public:
|
||||
ASTNODE_NODE_FUNCS(JumpBlock)
|
||||
virtual int instrCount() const override { return 0; }
|
||||
virtual bool maybePointedTo() const override { return true; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
// op1 = Statements
|
||||
AstNode* stmtsp() const { return op1p(); } // op1 = List of statements
|
||||
void addStmtsp(AstNode* nodep) { addNOp1p(nodep); }
|
||||
@@ -4842,7 +4954,7 @@ public:
|
||||
virtual bool isGateOptimizable() const override { return false; }
|
||||
virtual bool isPredictOptimizable() const override { return false; }
|
||||
virtual int instrCount() const override { return widthInstrs() * 2; } // xor, or/logor
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
class AstConsAssoc final : public AstNodeMath {
|
||||
@@ -4861,7 +4973,7 @@ public:
|
||||
virtual bool cleanOut() const override { return true; }
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
AstNode* defaultp() const { return op1p(); }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstSetAssoc final : public AstNodeMath {
|
||||
// Set an assoc array element and return object, '{}
|
||||
@@ -4883,7 +4995,48 @@ public:
|
||||
AstNode* lhsp() const { return op1p(); }
|
||||
AstNode* keyp() const { return op2p(); }
|
||||
AstNode* valuep() const { return op3p(); }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
class AstConsWildcard final : public AstNodeMath {
|
||||
// Construct a wildcard assoc array and return object, '{}
|
||||
// Parents: math
|
||||
// Children: expression (elements or other queues)
|
||||
public:
|
||||
AstConsWildcard(FileLine* fl, AstNode* defaultp)
|
||||
: ASTGEN_SUPER_ConsWildcard(fl) {
|
||||
setNOp1p(defaultp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(ConsWildcard)
|
||||
virtual string emitVerilog() override { return "'{}"; }
|
||||
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
virtual string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||
virtual bool cleanOut() const override { return true; }
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
AstNode* defaultp() const { return op1p(); }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstSetWildcard final : public AstNodeMath {
|
||||
// Set a wildcard assoc array element and return object, '{}
|
||||
// Parents: math
|
||||
// Children: expression (elements or other queues)
|
||||
public:
|
||||
AstSetWildcard(FileLine* fl, AstNode* lhsp, AstNode* keyp, AstNode* valuep)
|
||||
: ASTGEN_SUPER_SetWildcard(fl) {
|
||||
setOp1p(lhsp);
|
||||
setNOp2p(keyp);
|
||||
setOp3p(valuep);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(SetWildcard)
|
||||
virtual string emitVerilog() override { return "'{}"; }
|
||||
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
virtual string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||
virtual bool cleanOut() const override { return true; }
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
AstNode* lhsp() const { return op1p(); }
|
||||
AstNode* keyp() const { return op2p(); }
|
||||
AstNode* valuep() const { return op3p(); }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
class AstConsDynArray final : public AstNodeMath {
|
||||
@@ -4904,7 +5057,7 @@ public:
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
AstNode* lhsp() const { return op1p(); } // op1 = expression
|
||||
AstNode* rhsp() const { return op2p(); } // op2 = expression
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
class AstConsQueue final : public AstNodeMath {
|
||||
@@ -4925,7 +5078,7 @@ public:
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
AstNode* lhsp() const { return op1p(); } // op1 = expression
|
||||
AstNode* rhsp() const { return op2p(); } // op2 = expression
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
class AstBegin final : public AstNodeBlock {
|
||||
@@ -5102,7 +5255,7 @@ public:
|
||||
: ASTGEN_SUPER_New(fl, false, "new", pinsp) {}
|
||||
ASTNODE_NODE_FUNCS(New)
|
||||
virtual bool cleanOut() const { return true; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
virtual bool hasDType() const override { return true; }
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
};
|
||||
@@ -5121,7 +5274,7 @@ public:
|
||||
virtual string emitVerilog() override { return "new"; }
|
||||
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
virtual bool cleanOut() const override { return true; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
AstNode* rhsp() const { return op1p(); }
|
||||
};
|
||||
@@ -5141,7 +5294,7 @@ public:
|
||||
virtual string emitVerilog() override { return "new"; }
|
||||
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
virtual bool cleanOut() const override { return true; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
AstNode* sizep() const { return op1p(); }
|
||||
AstNode* rhsp() const { return op2p(); }
|
||||
@@ -5243,15 +5396,15 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class AstTimingControl final : public AstNodeStmt {
|
||||
class AstEventControl final : public AstNodeStmt {
|
||||
// Parents: stmtlist
|
||||
public:
|
||||
AstTimingControl(FileLine* fl, AstSenTree* sensesp, AstNode* stmtsp)
|
||||
: ASTGEN_SUPER_TimingControl(fl) {
|
||||
AstEventControl(FileLine* fl, AstSenTree* sensesp, AstNode* stmtsp)
|
||||
: ASTGEN_SUPER_EventControl(fl) {
|
||||
setNOp1p(sensesp);
|
||||
setNOp2p(stmtsp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(TimingControl)
|
||||
ASTNODE_NODE_FUNCS(EventControl)
|
||||
virtual string verilogKwd() const override { return "@(%l) %r"; }
|
||||
virtual bool isGateOptimizable() const override { return false; }
|
||||
virtual bool isPredictOptimizable() const override { return false; }
|
||||
@@ -5574,7 +5727,7 @@ public:
|
||||
virtual bool isGateOptimizable() const override { return false; }
|
||||
virtual bool isPredictOptimizable() const override { return false; }
|
||||
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
bool combinable(const AstRand* samep) const {
|
||||
return !seedp() && !samep->seedp() && reset() == samep->reset()
|
||||
&& urandom() == samep->urandom();
|
||||
@@ -5625,7 +5778,7 @@ public:
|
||||
virtual bool isGateOptimizable() const override { return false; }
|
||||
virtual bool isPredictOptimizable() const override { return false; }
|
||||
virtual int instrCount() const override { return INSTR_COUNT_TIME; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
virtual void dump(std::ostream& str = std::cout) const override;
|
||||
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
|
||||
VTimescale timeunit() const { return m_timeunit; }
|
||||
@@ -5646,7 +5799,7 @@ public:
|
||||
virtual bool isGateOptimizable() const override { return false; }
|
||||
virtual bool isPredictOptimizable() const override { return false; }
|
||||
virtual int instrCount() const override { return INSTR_COUNT_TIME; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
virtual void dump(std::ostream& str = std::cout) const override;
|
||||
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
|
||||
VTimescale timeunit() const { return m_timeunit; }
|
||||
@@ -5671,7 +5824,7 @@ public:
|
||||
virtual bool isSubstOptimizable() const override { return false; }
|
||||
virtual bool isPredictOptimizable() const override { return false; }
|
||||
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
@@ -5992,7 +6145,9 @@ public:
|
||||
class AstCLog2 final : public AstNodeUniop {
|
||||
public:
|
||||
AstCLog2(FileLine* fl, AstNode* lhsp)
|
||||
: ASTGEN_SUPER_CLog2(fl, lhsp) {}
|
||||
: ASTGEN_SUPER_CLog2(fl, lhsp) {
|
||||
dtypeSetSigned32();
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(CLog2)
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs) override { out.opCLog2(lhs); }
|
||||
virtual string emitVerilog() override { return "%f$clog2(%l)"; }
|
||||
@@ -6257,7 +6412,7 @@ public:
|
||||
virtual bool cleanOut() const override { return true; }
|
||||
virtual bool cleanLhs() const override { return true; }
|
||||
virtual bool sizeMattersLhs() const override { return false; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
class AstFEof final : public AstNodeUniop {
|
||||
@@ -6299,7 +6454,7 @@ public:
|
||||
AstNode* filep() const { return op1p(); }
|
||||
void strp(AstNode* nodep) { setOp2p(nodep); }
|
||||
AstNode* strp() const { return op2p(); }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
class AstFGetC final : public AstNodeUniop {
|
||||
@@ -8424,7 +8579,7 @@ public:
|
||||
AstNode* exprp() const { return op1p(); } // op1 = expression
|
||||
AstSenTree* sentreep() const { return VN_AS(op2p(), SenTree); } // op2 = clock domain
|
||||
void sentreep(AstSenTree* sentreep) { addOp2p(sentreep); } // op2 = clock domain
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
class AstPast final : public AstNodeMath {
|
||||
@@ -8447,7 +8602,7 @@ public:
|
||||
AstNode* ticksp() const { return op2p(); } // op2 = ticks or nullptr means 1
|
||||
AstSenTree* sentreep() const { return VN_AS(op4p(), SenTree); } // op4 = clock domain
|
||||
void sentreep(AstSenTree* sentreep) { addOp4p(sentreep); } // op4 = clock domain
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
class AstRose final : public AstNodeMath {
|
||||
@@ -8468,7 +8623,7 @@ public:
|
||||
AstNode* exprp() const { return op1p(); } // op1 = expression
|
||||
AstSenTree* sentreep() const { return VN_AS(op2p(), SenTree); } // op2 = clock domain
|
||||
void sentreep(AstSenTree* sentreep) { addOp2p(sentreep); } // op2 = clock domain
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
class AstSampled final : public AstNodeMath {
|
||||
@@ -8487,7 +8642,7 @@ public:
|
||||
virtual bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
|
||||
virtual int instrCount() const override { return 0; }
|
||||
AstNode* exprp() const { return op1p(); } // op1 = expression
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
class AstStable final : public AstNodeMath {
|
||||
@@ -8508,7 +8663,7 @@ public:
|
||||
AstNode* exprp() const { return op1p(); } // op1 = expression
|
||||
AstSenTree* sentreep() const { return VN_AS(op2p(), SenTree); } // op2 = clock domain
|
||||
void sentreep(AstSenTree* sentreep) { addOp2p(sentreep); } // op2 = clock domain
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
class AstPattern final : public AstNodeMath {
|
||||
@@ -8532,6 +8687,7 @@ public:
|
||||
AstNodeDType* childDTypep() const { return VN_AS(op1p(), NodeDType); }
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
AstNode* itemsp() const { return op2p(); } // op2 = AstPatReplicate, AstPatMember, etc
|
||||
void addItemsp(AstNode* nodep) { addOp2p(nodep); }
|
||||
};
|
||||
class AstPatMember final : public AstNodeMath {
|
||||
// Verilog '{a} or '{a{b}}
|
||||
@@ -8582,7 +8738,7 @@ public:
|
||||
void rhsp(AstNode* nodep) { return setOp2p(nodep); }
|
||||
AstSenTree* sentreep() const { return VN_AS(op4p(), SenTree); } // op4 = clock domain
|
||||
void sentreep(AstSenTree* sentreep) { addOp4p(sentreep); } // op4 = clock domain
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
@@ -8803,7 +8959,7 @@ public:
|
||||
virtual bool isPredictOptimizable() const override { return false; }
|
||||
virtual bool isPure() const override { return false; }
|
||||
virtual bool isOutputter() const override { return true; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
@@ -8823,7 +8979,7 @@ public:
|
||||
ASTNODE_BASE_FUNCS(NodeFile)
|
||||
virtual void dump(std::ostream& str) const override;
|
||||
virtual string name() const override { return m_name; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
void tblockp(AstTextBlock* tblockp) { setOp1p(tblockp); }
|
||||
AstTextBlock* tblockp() { return VN_AS(op1p(), TextBlock); }
|
||||
};
|
||||
@@ -9091,7 +9247,7 @@ public:
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(CReturn)
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
//
|
||||
AstNode* lhsp() const { return op1p(); }
|
||||
};
|
||||
@@ -9122,7 +9278,7 @@ public:
|
||||
virtual bool cleanOut() const override { return m_cleanOut; }
|
||||
virtual string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
void addBodysp(AstNode* nodep) { addNOp1p(nodep); }
|
||||
AstNode* bodysp() const { return op1p(); } // op1 = expressions to print
|
||||
bool pure() const { return m_pure; }
|
||||
@@ -9139,7 +9295,7 @@ public:
|
||||
ASTNODE_NODE_FUNCS(CReset)
|
||||
virtual bool isGateOptimizable() const override { return false; }
|
||||
virtual bool isPredictOptimizable() const override { return false; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
AstVarRef* varrefp() const { return VN_AS(op1p(), VarRef); } // op1 = varref to reset
|
||||
};
|
||||
|
||||
@@ -9157,7 +9313,7 @@ public:
|
||||
ASTNODE_NODE_FUNCS(CStmt)
|
||||
virtual bool isGateOptimizable() const override { return false; }
|
||||
virtual bool isPredictOptimizable() const override { return false; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
virtual bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
void addBodysp(AstNode* nodep) { addNOp1p(nodep); }
|
||||
AstNode* bodysp() const { return op1p(); } // op1 = expressions to print
|
||||
};
|
||||
@@ -9253,7 +9409,7 @@ class AstTypeTable final : public AstNode {
|
||||
AstEmptyQueueDType* m_emptyQueuep = nullptr;
|
||||
AstQueueDType* m_queueIndexp = nullptr;
|
||||
AstVoidDType* m_voidp = nullptr;
|
||||
AstBasicDType* m_basicps[VBasicDTypeKwd::_ENUM_MAX];
|
||||
AstBasicDType* m_basicps[VBasicDTypeKwd::_ENUM_MAX]{};
|
||||
//
|
||||
using DetailedMap = std::map<VBasicTypeKey, AstBasicDType*>;
|
||||
DetailedMap m_detailedMap;
|
||||
|
||||
+16
-18
@@ -27,7 +27,8 @@
|
||||
#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");
|
||||
|
||||
@@ -35,17 +36,16 @@ 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) {
|
||||
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 {
|
||||
@@ -55,14 +55,13 @@ 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) {
|
||||
if VL_CONSTEXPR_CXX17 (T_UserN == 1) {
|
||||
nodep->user1u(VNUser(userp));
|
||||
} else if (T_UserN == 2) {
|
||||
} else if VL_CONSTEXPR_CXX17 (T_UserN == 2) {
|
||||
nodep->user2u(VNUser(userp));
|
||||
} else if (T_UserN == 3) {
|
||||
} else if VL_CONSTEXPR_CXX17 (T_UserN == 3) {
|
||||
nodep->user3u(VNUser(userp));
|
||||
} else if (T_UserN == 4) {
|
||||
} else if VL_CONSTEXPR_CXX17 (T_UserN == 4) {
|
||||
nodep->user4u(VNUser(userp));
|
||||
} else {
|
||||
nodep->user5u(VNUser(userp));
|
||||
@@ -71,14 +70,13 @@ private:
|
||||
|
||||
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) {
|
||||
@@ -106,7 +104,7 @@ public:
|
||||
}
|
||||
|
||||
// Get a reference to the user data
|
||||
T_Data& operator()(const T_Node* nodep) {
|
||||
T_Data& operator()(const T_Node* nodep) const {
|
||||
T_Data* const userp = getUserp(nodep);
|
||||
UASSERT_OBJ(userp, nodep, "Missing User data on const AstNode");
|
||||
return *userp;
|
||||
|
||||
+30
-24
@@ -28,9 +28,10 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Begin.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -76,6 +77,33 @@ private:
|
||||
return a + "__DOT__" + b;
|
||||
}
|
||||
|
||||
void dotNames(const AstNodeBlock* const nodep, const char* const blockName) {
|
||||
UINFO(8, "nname " << m_namedScope << endl);
|
||||
if (nodep->name() != "") { // Else unneeded unnamed block
|
||||
// Create data for dotted variable resolution
|
||||
string dottedname = nodep->name() + "__DOT__"; // So always found
|
||||
string::size_type pos;
|
||||
while ((pos = dottedname.find("__DOT__")) != string::npos) {
|
||||
const string ident = dottedname.substr(0, pos);
|
||||
dottedname = dottedname.substr(pos + strlen("__DOT__"));
|
||||
if (nodep->name() != "") {
|
||||
m_displayScope = dot(m_displayScope, ident);
|
||||
m_namedScope = dot(m_namedScope, ident);
|
||||
}
|
||||
m_unnamedScope = dot(m_unnamedScope, ident);
|
||||
// Create CellInline for dotted var resolution
|
||||
if (!m_ftaskp) {
|
||||
AstCellInline* const inlinep = new AstCellInline(
|
||||
nodep->fileline(), m_unnamedScope, blockName, m_modp->timeunit());
|
||||
m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remap var names and replace lower Begins
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
}
|
||||
|
||||
void liftNode(AstNode* nodep) {
|
||||
nodep->unlinkFrBack();
|
||||
if (m_ftaskp) {
|
||||
@@ -141,30 +169,8 @@ private:
|
||||
VL_RESTORER(m_namedScope);
|
||||
VL_RESTORER(m_unnamedScope);
|
||||
{
|
||||
UINFO(8, "nname " << m_namedScope << endl);
|
||||
if (nodep->name() != "") { // Else unneeded unnamed block
|
||||
// Create data for dotted variable resolution
|
||||
string dottedname = nodep->name() + "__DOT__"; // So always found
|
||||
string::size_type pos;
|
||||
while ((pos = dottedname.find("__DOT__")) != string::npos) {
|
||||
const string ident = dottedname.substr(0, pos);
|
||||
dottedname = dottedname.substr(pos + strlen("__DOT__"));
|
||||
if (nodep->name() != "") {
|
||||
m_displayScope = dot(m_displayScope, ident);
|
||||
m_namedScope = dot(m_namedScope, ident);
|
||||
}
|
||||
m_unnamedScope = dot(m_unnamedScope, ident);
|
||||
// Create CellInline for dotted var resolution
|
||||
if (!m_ftaskp) {
|
||||
AstCellInline* const inlinep = new AstCellInline(
|
||||
nodep->fileline(), m_unnamedScope, "__BEGIN__", m_modp->timeunit());
|
||||
m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells
|
||||
}
|
||||
}
|
||||
}
|
||||
dotNames(nodep, "__BEGIN__");
|
||||
|
||||
// Remap var names and replace lower Begins
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
UASSERT_OBJ(!nodep->genforp(), nodep, "GENFORs should have been expanded earlier");
|
||||
|
||||
// Cleanup
|
||||
|
||||
+2
-1
@@ -26,9 +26,10 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Branch.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
|
||||
+3
-2
@@ -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"
|
||||
@@ -48,7 +49,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;
|
||||
}
|
||||
|
||||
+3
-2
@@ -27,10 +27,11 @@
|
||||
#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>
|
||||
|
||||
|
||||
+2
-1
@@ -25,9 +25,10 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3CUse.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
|
||||
+3
-2
@@ -37,9 +37,10 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Case.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -496,7 +497,7 @@ private:
|
||||
V3Case::caseLint(nodep);
|
||||
iterateChildren(nodep);
|
||||
if (debug() >= 9) nodep->dumpTree(cout, " case_old: ");
|
||||
if (isCaseTreeFast(nodep) && v3Global.opt.oCase()) {
|
||||
if (isCaseTreeFast(nodep) && v3Global.opt.fCase()) {
|
||||
// It's a simple priority encoder or complete statement
|
||||
// we can make a tree of statements to avoid extra comparisons
|
||||
++m_statCaseFast;
|
||||
|
||||
+2
-1
@@ -40,9 +40,10 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Cast.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
+3
-2
@@ -24,13 +24,14 @@
|
||||
#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>
|
||||
|
||||
+3
-2
@@ -29,10 +29,11 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3Changed.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
//######################################################################
|
||||
|
||||
+13
-7
@@ -23,9 +23,10 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Class.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
//######################################################################
|
||||
|
||||
@@ -97,6 +98,7 @@ private:
|
||||
m_prefix = nodep->name() + "__02e"; // .
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
nodep->repairCache();
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
// Visit for NodeModules that are not AstClass (AstClass is-a AstNodeModule)
|
||||
@@ -114,13 +116,13 @@ private:
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -136,7 +138,7 @@ private:
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,12 +153,16 @@ private:
|
||||
virtual 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 {
|
||||
// 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
|
||||
|
||||
+3
-1
@@ -26,9 +26,10 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Clean.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -88,6 +89,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)
|
||||
|
||||
+2
-1
@@ -30,9 +30,10 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Clock.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
+7
-4
@@ -22,12 +22,13 @@
|
||||
#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>
|
||||
@@ -220,7 +221,9 @@ class CombineVisitor final : VNVisitor {
|
||||
|
||||
// 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}; }
|
||||
|
||||
+10
-9
@@ -24,9 +24,10 @@
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Common.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
//######################################################################
|
||||
// Common component builders
|
||||
@@ -50,7 +51,7 @@ 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);
|
||||
@@ -85,11 +86,11 @@ 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});
|
||||
@@ -103,13 +104,13 @@ 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);
|
||||
|
||||
+120
-19
@@ -17,9 +17,10 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Config.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3String.h"
|
||||
#include "V3Config.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
@@ -33,7 +34,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
|
||||
@@ -44,13 +46,8 @@ public:
|
||||
|
||||
/// Update into maps from other
|
||||
void update(const V3ConfigWildcardResolver& other) {
|
||||
typename Map::const_iterator it;
|
||||
for (it = other.m_mapResolved.begin(); it != other.m_mapResolved.end(); ++it) {
|
||||
m_mapResolved[it->first].update(it->second);
|
||||
}
|
||||
for (it = other.m_mapWildcard.begin(); it != other.m_mapWildcard.end(); ++it) {
|
||||
m_mapWildcard[it->first].update(it->second);
|
||||
}
|
||||
for (const auto& itr : other.m_mapResolved) m_mapResolved[itr.first].update(itr.second);
|
||||
for (const auto& itr : other.m_mapWildcard) m_mapWildcard[itr.first].update(itr.second);
|
||||
}
|
||||
|
||||
// Access and create a (wildcard) entity
|
||||
@@ -68,12 +65,12 @@ public:
|
||||
// Cannot be resolved, create if matched
|
||||
|
||||
// Update this entity with all matches in the wildcards
|
||||
for (it = m_mapWildcard.begin(); it != m_mapWildcard.end(); ++it) {
|
||||
if (VString::wildmatch(name, it->first)) {
|
||||
for (const auto& wildent : m_mapWildcard) {
|
||||
if (VString::wildmatch(name, wildent.first)) {
|
||||
if (!newp) {
|
||||
newp = &m_mapResolved[name]; // Emplace and get pointer
|
||||
}
|
||||
newp->update(it->second);
|
||||
newp->update(wildent.second);
|
||||
}
|
||||
}
|
||||
return newp;
|
||||
@@ -198,8 +195,8 @@ public:
|
||||
AstNode* const nodep = new AstPragma(modp->fileline(), type);
|
||||
modp->addStmtp(nodep);
|
||||
}
|
||||
for (auto it = m_modPragmas.cbegin(); it != m_modPragmas.cend(); ++it) {
|
||||
AstNode* const nodep = new AstPragma(modp->fileline(), *it);
|
||||
for (const auto& itr : m_modPragmas) {
|
||||
AstNode* const nodep = new AstPragma{modp->fileline(), itr};
|
||||
modp->addStmtp(nodep);
|
||||
}
|
||||
}
|
||||
@@ -344,25 +341,124 @@ public:
|
||||
|
||||
using V3ConfigFileResolver = V3ConfigWildcardResolver<V3ConfigFile>;
|
||||
|
||||
//######################################################################
|
||||
// ScopeTrace tracking
|
||||
|
||||
class V3ConfigScopeTraceEntry final {
|
||||
public:
|
||||
const string m_scope; // Scope or regexp to match
|
||||
const bool m_on = false; // True to enable message
|
||||
int m_levels = 0; // # levels, 0 = all, 1 = only this, ...
|
||||
// CONSTRUCTORS
|
||||
V3ConfigScopeTraceEntry(const string& scope, bool on, int levels)
|
||||
: m_scope{scope}
|
||||
, m_on{on}
|
||||
, m_levels{levels} {}
|
||||
~V3ConfigScopeTraceEntry() = default;
|
||||
bool operator<(const V3ConfigScopeTraceEntry& other) const {
|
||||
if (m_on < other.m_on) return true;
|
||||
if (m_on > other.m_on) return false;
|
||||
if (m_levels < other.m_levels) return true;
|
||||
if (m_levels > other.m_levels) return false;
|
||||
return m_scope < other.m_scope;
|
||||
}
|
||||
};
|
||||
|
||||
// Tracks what matches are known to hit against V3ConfigScopeTraceEntries
|
||||
class V3ConfigScopeTraceEntryMatch final {
|
||||
public:
|
||||
const V3ConfigScopeTraceEntry* m_entryp;
|
||||
const string m_scopepart;
|
||||
V3ConfigScopeTraceEntryMatch(const V3ConfigScopeTraceEntry* entryp, const string& scopepart)
|
||||
: m_entryp{entryp}
|
||||
, m_scopepart{scopepart} {}
|
||||
bool operator<(const V3ConfigScopeTraceEntryMatch& other) const {
|
||||
if (m_entryp < other.m_entryp) return true;
|
||||
if (m_entryp > other.m_entryp) return false;
|
||||
return m_scopepart < other.m_scopepart;
|
||||
}
|
||||
};
|
||||
|
||||
class V3ConfigScopeTraceResolver final {
|
||||
std::vector<V3ConfigScopeTraceEntry> m_entries; // User specified on/offs and levels
|
||||
std::map<V3ConfigScopeTraceEntryMatch, bool> m_matchCache; // Matching entries for speed
|
||||
|
||||
public:
|
||||
void addScopeTraceOn(bool on, const string& scope, int levels) {
|
||||
UINFO(9, "addScopeTraceOn " << on << " '" << scope << "' "
|
||||
<< " levels=" << levels << endl);
|
||||
m_entries.emplace_back(V3ConfigScopeTraceEntry{scope, on, levels});
|
||||
m_matchCache.clear();
|
||||
}
|
||||
|
||||
bool getEntryMatch(const V3ConfigScopeTraceEntry* entp, const string& scopepart) {
|
||||
// Return if a entry matches the scopepart, with memoization
|
||||
const auto& key = V3ConfigScopeTraceEntryMatch{entp, scopepart};
|
||||
const auto& it = m_matchCache.find(key);
|
||||
if (it != m_matchCache.end()) return it->second; // Cached
|
||||
const bool matched = VString::wildmatch(scopepart, entp->m_scope);
|
||||
m_matchCache.emplace(key, matched);
|
||||
return matched;
|
||||
}
|
||||
|
||||
bool getScopeTraceOn(const string& scope) {
|
||||
// Apply in the order the user provided them, so they can choose on/off preferencing
|
||||
int maxLevel = 1;
|
||||
for (const auto& ch : scope) {
|
||||
if (ch == '.') ++maxLevel;
|
||||
}
|
||||
UINFO(9, "getScopeTraceOn " << scope << " maxLevel=" << maxLevel << endl);
|
||||
|
||||
bool enabled = true;
|
||||
for (const auto& ent : m_entries) {
|
||||
// We apply shortest match first for each rule component
|
||||
// (Otherwise the levels would be useless as "--scope top* --levels 1" would
|
||||
// always match at every scopepart, and we wound't know how to count levels)
|
||||
int partLevel = 1;
|
||||
for (string::size_type partEnd = 0; true;) {
|
||||
partEnd = scope.find('.', partEnd + 1);
|
||||
if (partEnd == string::npos) partEnd = scope.length();
|
||||
const std::string scopepart = scope.substr(0, partEnd);
|
||||
if (getEntryMatch(&ent, scopepart)) {
|
||||
const bool levelMatch
|
||||
= !ent.m_levels || (ent.m_levels >= maxLevel - partLevel);
|
||||
if (levelMatch) enabled = ent.m_on;
|
||||
UINFO(9, "getScopeTraceOn-part " << scope << " enabled=" << enabled
|
||||
<< " @ lev=" << partLevel
|
||||
<< (levelMatch ? "[match]" : "[miss]")
|
||||
<< " from scopepart=" << scopepart << endl);
|
||||
break;
|
||||
}
|
||||
if (partEnd == scope.length()) break;
|
||||
++partLevel;
|
||||
}
|
||||
}
|
||||
return enabled;
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Resolve modules and files in the design
|
||||
|
||||
class V3ConfigResolver final {
|
||||
V3ConfigModuleResolver m_modules; // Access to module names (with wildcards)
|
||||
V3ConfigFileResolver m_files; // Access to file names (with wildcards)
|
||||
V3ConfigScopeTraceResolver m_scopeTraces; // Regexp to trace enables
|
||||
std::unordered_map<string, std::unordered_map<string, uint64_t>>
|
||||
m_profileData; // Access to profile_data records
|
||||
FileLine* m_profileFileLine = nullptr;
|
||||
|
||||
static V3ConfigResolver s_singleton; // Singleton (not via local static, as that's slow)
|
||||
V3ConfigResolver() = default;
|
||||
~V3ConfigResolver() = default;
|
||||
|
||||
public:
|
||||
static V3ConfigResolver& s() { return s_singleton; }
|
||||
|
||||
static V3ConfigResolver& s() {
|
||||
static V3ConfigResolver s_singleton;
|
||||
return s_singleton;
|
||||
}
|
||||
V3ConfigModuleResolver& modules() { return m_modules; }
|
||||
V3ConfigFileResolver& files() { return m_files; }
|
||||
V3ConfigScopeTraceResolver& scopeTraces() { return m_scopeTraces; }
|
||||
|
||||
void addProfileData(FileLine* fl, const string& model, const string& key, uint64_t cost) {
|
||||
if (!m_profileFileLine) m_profileFileLine = fl;
|
||||
@@ -379,8 +475,6 @@ public:
|
||||
FileLine* getProfileDataFileLine() const { return m_profileFileLine; } // Maybe null
|
||||
};
|
||||
|
||||
V3ConfigResolver V3ConfigResolver::s_singleton;
|
||||
|
||||
//######################################################################
|
||||
// V3Config
|
||||
|
||||
@@ -434,6 +528,10 @@ void V3Config::addProfileData(FileLine* fl, const string& model, const string& k
|
||||
V3ConfigResolver::s().addProfileData(fl, model, key, cost);
|
||||
}
|
||||
|
||||
void V3Config::addScopeTraceOn(bool on, const string& scope, int levels) {
|
||||
V3ConfigResolver::s().scopeTraces().addScopeTraceOn(on, scope, levels);
|
||||
}
|
||||
|
||||
void V3Config::addVarAttr(FileLine* fl, const string& module, const string& ftask,
|
||||
const string& var, VAttrType attr, AstSenTree* sensep) {
|
||||
// Semantics: sensep only if public_flat_rw
|
||||
@@ -540,6 +638,9 @@ uint64_t V3Config::getProfileData(const string& model, const string& key) {
|
||||
FileLine* V3Config::getProfileDataFileLine() {
|
||||
return V3ConfigResolver::s().getProfileDataFileLine();
|
||||
}
|
||||
bool V3Config::getScopeTraceOn(const string& scope) {
|
||||
return V3ConfigResolver::s().scopeTraces().getScopeTraceOn(scope);
|
||||
}
|
||||
|
||||
bool V3Config::waive(FileLine* filelinep, V3ErrorCode code, const string& message) {
|
||||
V3ConfigFile* filep = V3ConfigResolver::s().files().resolve(filelinep->filename());
|
||||
|
||||
+5
-3
@@ -20,9 +20,9 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Error.h"
|
||||
#include "V3FileLine.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
//######################################################################
|
||||
|
||||
@@ -37,19 +37,21 @@ public:
|
||||
static void addModulePragma(const string& module, VPragmaType pragma);
|
||||
static void addProfileData(FileLine* fl, const string& model, const string& key,
|
||||
uint64_t cost);
|
||||
static void addWaiver(V3ErrorCode code, const string& filename, const string& message);
|
||||
static void addScopeTraceOn(bool on, const string& scope, int levels);
|
||||
static void addVarAttr(FileLine* fl, const string& module, const string& ftask,
|
||||
const string& signal, VAttrType type, AstSenTree* nodep);
|
||||
static void addWaiver(V3ErrorCode code, const string& filename, const string& message);
|
||||
|
||||
static void applyCase(AstCase* nodep);
|
||||
static void applyCoverageBlock(AstNodeModule* modulep, AstBegin* nodep);
|
||||
static void applyFTask(AstNodeModule* modulep, AstNodeFTask* ftaskp);
|
||||
static void applyIgnores(FileLine* filelinep);
|
||||
static void applyModule(AstNodeModule* modulep);
|
||||
static void applyFTask(AstNodeModule* modulep, AstNodeFTask* ftaskp);
|
||||
static void applyVarAttr(AstNodeModule* modulep, AstNodeFTask* ftaskp, AstVar* varp);
|
||||
|
||||
static uint64_t getProfileData(const string& model, const string& key);
|
||||
static FileLine* getProfileDataFileLine();
|
||||
static bool getScopeTraceOn(const string& scope);
|
||||
static bool waive(FileLine* filelinep, V3ErrorCode code, const string& message);
|
||||
};
|
||||
|
||||
|
||||
+200
-89
@@ -23,14 +23,15 @@
|
||||
#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>
|
||||
@@ -79,14 +80,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()) {
|
||||
@@ -111,6 +148,15 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
|
||||
BitPolarityEntry() = default;
|
||||
};
|
||||
|
||||
struct FrozenNodeInfo final { // Context when a frozen node is found
|
||||
bool m_polarity;
|
||||
int m_lsb;
|
||||
bool operator<(const FrozenNodeInfo& other) const {
|
||||
if (m_lsb != other.m_lsb) return m_lsb < other.m_lsb;
|
||||
return m_polarity < other.m_polarity;
|
||||
}
|
||||
};
|
||||
|
||||
class Restorer final { // Restore the original state unless disableRestore() is called
|
||||
ConstBitOpTreeVisitor& m_visitor;
|
||||
const size_t m_polaritiesSize;
|
||||
@@ -280,7 +326,6 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
|
||||
return ResultTerm{resultp, ops, clean};
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VarInfo(ConstBitOpTreeVisitor* parent, AstVarRef* refp, int width)
|
||||
: m_parentp{parent}
|
||||
@@ -299,7 +344,8 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
|
||||
LeafInfo* m_leafp = nullptr; // AstConst or AstVarRef that currently looking for
|
||||
const AstNode* const m_rootp; // Root of this AST subtree
|
||||
|
||||
std::vector<AstNode*> m_frozenNodes; // Nodes that cannot be optimized
|
||||
std::vector<std::pair<AstNode*, FrozenNodeInfo>>
|
||||
m_frozenNodes; // Nodes that cannot be optimized
|
||||
std::vector<BitPolarityEntry> m_bitPolarities; // Polarity of bits found during iterate()
|
||||
std::vector<std::unique_ptr<VarInfo>> m_varInfos; // VarInfo for each variable, [0] is nullptr
|
||||
|
||||
@@ -329,32 +375,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;
|
||||
@@ -363,9 +409,9 @@ 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{};
|
||||
}
|
||||
@@ -374,7 +420,10 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
CONST_BITOP_SET_FAILED("Hit unexpected op", nodep);
|
||||
}
|
||||
virtual void visit(AstCCast* nodep) override { iterateChildren(nodep); }
|
||||
virtual void visit(AstCCast* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (m_leafp) m_leafp->updateBitRange(nodep);
|
||||
}
|
||||
virtual void visit(AstShiftR* nodep) override {
|
||||
CONST_BITOP_RETURN_IF(!m_leafp, nodep);
|
||||
AstConst* const constp = VN_CAST(nodep->rhsp(), Const);
|
||||
@@ -382,12 +431,14 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
|
||||
m_lsb += constp->toUInt();
|
||||
incrOps(nodep, __LINE__);
|
||||
iterate(nodep->lhsp());
|
||||
m_leafp->updateBitRange(nodep);
|
||||
m_lsb -= constp->toUInt();
|
||||
}
|
||||
virtual 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);
|
||||
@@ -396,68 +447,68 @@ 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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -477,29 +528,29 @@ 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
|
||||
if (const AstCCast* const castp = VN_CAST(opp, CCast)) opp = castp->lhsp();
|
||||
m_frozenNodes.push_back(opp);
|
||||
m_frozenNodes.emplace_back(opp, FrozenNodeInfo{m_polarity, m_lsb});
|
||||
m_failed = origFailed;
|
||||
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);
|
||||
@@ -515,46 +566,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);
|
||||
@@ -652,17 +716,21 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
std::map<FrozenNodeInfo, std::vector<AstNode*>> frozenNodes; // Group by FrozenNodeInfo
|
||||
// Check if frozen terms are clean or not
|
||||
for (AstNode* const termp : visitor.m_frozenNodes) {
|
||||
for (const auto& frozenInfo : visitor.m_frozenNodes) {
|
||||
AstNode* const termp = frozenInfo.first;
|
||||
// Comparison operators are clean
|
||||
if (VN_IS(termp, Eq) || VN_IS(termp, Neq) || VN_IS(termp, Lt) || VN_IS(termp, Lte)
|
||||
|| VN_IS(termp, Gt) || VN_IS(termp, Gte)) {
|
||||
if ((VN_IS(termp, Eq) || VN_IS(termp, Neq) || VN_IS(termp, Lt) || VN_IS(termp, Lte)
|
||||
|| VN_IS(termp, Gt) || VN_IS(termp, Gte))
|
||||
&& frozenInfo.second.m_lsb == 0) {
|
||||
hasCleanTerm = true;
|
||||
} else {
|
||||
// Otherwise, conservatively assume the frozen term is dirty
|
||||
hasDirtyTerm = true;
|
||||
UINFO(9, "Dirty frozen term: " << termp << endl);
|
||||
}
|
||||
frozenNodes[frozenInfo.second].push_back(termp);
|
||||
}
|
||||
|
||||
// Figure out if a final negation is required
|
||||
@@ -672,7 +740,12 @@ public:
|
||||
const bool needsCleaning = visitor.isAndTree() ? !hasCleanTerm : hasDirtyTerm;
|
||||
|
||||
// Add size of reduction tree to op count
|
||||
resultOps += termps.size() + visitor.m_frozenNodes.size() - 1;
|
||||
resultOps += termps.size() - 1;
|
||||
for (const auto& lsbAndNodes : frozenNodes) {
|
||||
if (lsbAndNodes.first.m_lsb > 0) ++resultOps; // Needs AstShiftR
|
||||
if (!lsbAndNodes.first.m_polarity) ++resultOps; // Needs AstNot
|
||||
resultOps += lsbAndNodes.second.size();
|
||||
}
|
||||
// Add final polarity flip in Xor tree
|
||||
if (needsFlip) ++resultOps;
|
||||
// Add final cleaning AND
|
||||
@@ -681,7 +754,11 @@ public:
|
||||
if (debug() >= 9) { // LCOV_EXCL_START
|
||||
cout << "Bitop tree considered: " << endl;
|
||||
for (AstNode* const termp : termps) termp->dumpTree("Reduced term: ");
|
||||
for (AstNode* const termp : visitor.m_frozenNodes) termp->dumpTree("Frozen term: ");
|
||||
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;
|
||||
@@ -724,8 +801,25 @@ public:
|
||||
resultp = reduce(resultp, termp);
|
||||
}
|
||||
// Add any frozen terms to the reduction
|
||||
for (AstNode* const frozenp : visitor.m_frozenNodes) {
|
||||
resultp = reduce(resultp, frozenp->unlinkFrBack());
|
||||
for (auto&& nodes : frozenNodes) {
|
||||
// nodes.second has same lsb and polarity
|
||||
AstNode* termp = nullptr;
|
||||
for (AstNode* const itemp : nodes.second) {
|
||||
termp = reduce(termp, itemp->unlinkFrBack());
|
||||
}
|
||||
if (nodes.first.m_lsb > 0) { // LSB is not 0, so shiftR
|
||||
AstNodeDType* const dtypep = termp->dtypep();
|
||||
termp = new AstShiftR{termp->fileline(), termp,
|
||||
new AstConst(termp->fileline(), AstConst::WidthedValue{},
|
||||
termp->width(), nodes.first.m_lsb)};
|
||||
termp->dtypep(dtypep);
|
||||
}
|
||||
if (!nodes.first.m_polarity) { // Polarity is inverted, so append Not
|
||||
AstNodeDType* const dtypep = termp->dtypep();
|
||||
termp = new AstNot{termp->fileline(), termp};
|
||||
termp->dtypep(dtypep);
|
||||
}
|
||||
resultp = reduce(resultp, termp);
|
||||
}
|
||||
|
||||
// Set width of masks to expected result width. This is required to prevent later removal
|
||||
@@ -769,6 +863,9 @@ public:
|
||||
|
||||
class ConstVisitor final : public VNVisitor {
|
||||
private:
|
||||
// CONSTANTS
|
||||
static constexpr unsigned CONCAT_MERGABLE_MAX_DEPTH = 10; // Limit alg recursion
|
||||
|
||||
// NODE STATE
|
||||
// ** only when m_warn/m_doExpensive is set. If state is needed other times,
|
||||
// ** must track down everywhere V3Const is called and make sure no overlaps.
|
||||
@@ -992,7 +1089,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) {
|
||||
@@ -1048,7 +1145,7 @@ private:
|
||||
|
||||
bool matchBitOpTree(AstNode* nodep) {
|
||||
if (nodep->widthMin() != 1) return false;
|
||||
if (!v3Global.opt.oConstBitOpTree()) return false;
|
||||
if (!v3Global.opt.fConstBitOpTree()) return false;
|
||||
|
||||
string debugPrefix;
|
||||
if (debug() >= 9) { // LCOV_EXCL_START
|
||||
@@ -1370,7 +1467,7 @@ private:
|
||||
return (VN_IS(nodep, And) || VN_IS(nodep, Or) || VN_IS(nodep, Xor));
|
||||
}
|
||||
bool ifAdjacentSel(const AstSel* lhsp, const AstSel* rhsp) {
|
||||
if (!v3Global.opt.oAssemble()) return false; // opt disabled
|
||||
if (!v3Global.opt.fAssemble()) return false; // opt disabled
|
||||
if (!lhsp || !rhsp) return false;
|
||||
const AstNode* const lfromp = lhsp->fromp();
|
||||
const AstNode* const rfromp = rhsp->fromp();
|
||||
@@ -1385,7 +1482,7 @@ private:
|
||||
}
|
||||
bool ifMergeAdjacent(AstNode* lhsp, AstNode* rhsp) {
|
||||
// called by concatmergeable to determine if {lhsp, rhsp} make sense
|
||||
if (!v3Global.opt.oAssemble()) return false; // opt disabled
|
||||
if (!v3Global.opt.fAssemble()) return false; // opt disabled
|
||||
// two same varref
|
||||
if (operandsSame(lhsp, rhsp)) return true;
|
||||
const AstSel* lselp = VN_CAST(lhsp, Sel);
|
||||
@@ -1420,11 +1517,12 @@ private:
|
||||
if (rend == rfromp->width() && lstart->toSInt() == 0) return true;
|
||||
return false;
|
||||
}
|
||||
bool concatMergeable(const AstNode* lhsp, const AstNode* rhsp) {
|
||||
bool concatMergeable(const AstNode* lhsp, const AstNode* rhsp, unsigned depth) {
|
||||
// determine if {a OP b, c OP d} => {a, c} OP {b, d} is advantageous
|
||||
if (!v3Global.opt.oAssemble()) return false; // opt disabled
|
||||
if (!v3Global.opt.fAssemble()) return false; // opt disabled
|
||||
if (lhsp->type() != rhsp->type()) return false;
|
||||
if (!ifConcatMergeableBiop(lhsp)) return false;
|
||||
if (depth > CONCAT_MERGABLE_MAX_DEPTH) return false; // As worse case O(n^2) algorithm
|
||||
|
||||
const AstNodeBiop* const lp = VN_CAST(lhsp, NodeBiop);
|
||||
const AstNodeBiop* const rp = VN_CAST(rhsp, NodeBiop);
|
||||
@@ -1434,11 +1532,12 @@ private:
|
||||
const bool rad = ifMergeAdjacent(lp->rhsp(), rp->rhsp());
|
||||
if (lad && rad) return true;
|
||||
// {a[] & b[]&c[], a[] & b[]&c[]}
|
||||
if (lad && concatMergeable(lp->rhsp(), rp->rhsp())) return true;
|
||||
if (lad && concatMergeable(lp->rhsp(), rp->rhsp(), depth + 1)) return true;
|
||||
// {a[]&b[] & c[], a[]&b[] & c[]}
|
||||
if (rad && concatMergeable(lp->lhsp(), rp->lhsp())) return true;
|
||||
if (rad && concatMergeable(lp->lhsp(), rp->lhsp(), depth + 1)) return true;
|
||||
// {(a[]&b[])&(c[]&d[]), (a[]&b[])&(c[]&d[])}
|
||||
if (concatMergeable(lp->lhsp(), rp->lhsp()) && concatMergeable(lp->rhsp(), rp->rhsp())) {
|
||||
if (concatMergeable(lp->lhsp(), rp->lhsp(), depth + 1)
|
||||
&& concatMergeable(lp->rhsp(), rp->rhsp(), depth + 1)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1666,6 +1765,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: ");
|
||||
@@ -1698,7 +1798,7 @@ private:
|
||||
AstNode* const lrp = lp->rhsp()->cloneTree(false);
|
||||
AstNode* const rlp = rp->lhsp()->cloneTree(false);
|
||||
AstNode* const rrp = rp->rhsp()->cloneTree(false);
|
||||
if (concatMergeable(lp, rp)) {
|
||||
if (concatMergeable(lp, rp, 0)) {
|
||||
AstConcat* const newlp = new AstConcat(rlp->fileline(), llp, rlp);
|
||||
AstConcat* const newrp = new AstConcat(rrp->fileline(), lrp, rrp);
|
||||
// use the lhs to replace the parent concat
|
||||
@@ -2279,6 +2379,7 @@ private:
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstClassOrPackageRef* nodep) override { iterateChildren(nodep); }
|
||||
virtual void visit(AstPin* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
void replaceLogEq(AstLogEq* nodep) {
|
||||
@@ -2506,7 +2607,7 @@ private:
|
||||
if (nodep->access().isReadOnly()
|
||||
&& ((!m_params // Can reduce constant wires into equations
|
||||
&& m_doNConst
|
||||
&& v3Global.opt.oConst()
|
||||
&& v3Global.opt.fConst()
|
||||
// Default value, not a "known" constant for this usage
|
||||
&& !nodep->varp()->isClassMember()
|
||||
&& !(nodep->varp()->isFuncLocal() && nodep->varp()->isNonOutput())
|
||||
@@ -3368,7 +3469,7 @@ private:
|
||||
TREEOPV("AstConcat{$lhsp.isZero, $rhsp}", "replaceExtend(nodep, nodep->rhsp())");
|
||||
// CONCAT(a[1],a[0]) -> a[1:0]
|
||||
TREEOPV("AstConcat{$lhsp.castSel, $rhsp.castSel, ifAdjacentSel(VN_AS($lhsp,,Sel),,VN_AS($rhsp,,Sel))}", "replaceConcatSel(nodep)");
|
||||
TREEOPV("AstConcat{ifConcatMergeableBiop($lhsp), concatMergeable($lhsp,,$rhsp)}", "replaceConcatMerge(nodep)");
|
||||
TREEOPV("AstConcat{ifConcatMergeableBiop($lhsp), concatMergeable($lhsp,,$rhsp,,0)}", "replaceConcatMerge(nodep)");
|
||||
// Common two-level operations that can be simplified
|
||||
TREEOP ("AstAnd {$lhsp.castConst,matchAndCond(nodep)}", "DONE");
|
||||
TREEOP ("AstAnd {$lhsp.castConst, $rhsp.castOr, matchMaskedOr(nodep)}", "DONE");
|
||||
@@ -3484,7 +3585,11 @@ public:
|
||||
}
|
||||
virtual ~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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3577,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
|
||||
|
||||
@@ -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
|
||||
|
||||
+29
-27
@@ -27,9 +27,10 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Coverage.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
@@ -278,7 +279,7 @@ 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);
|
||||
@@ -288,9 +289,9 @@ private:
|
||||
// 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();
|
||||
}
|
||||
@@ -313,11 +314,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 +329,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 +342,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 +357,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 +369,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();
|
||||
}
|
||||
|
||||
@@ -20,9 +20,10 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3CoverageJoin.h"
|
||||
|
||||
#include "V3DupFinder.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
+4
-3
@@ -36,9 +36,10 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Dead.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
@@ -85,7 +86,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) {
|
||||
@@ -316,7 +317,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;
|
||||
|
||||
+11
-1
@@ -51,9 +51,10 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Delayed.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -94,6 +95,7 @@ private:
|
||||
bool m_inDly = false; // True in delayed assignments
|
||||
bool m_inLoop = false; // True in for loops
|
||||
bool m_inInitial = false; // True in initial blocks
|
||||
bool m_ignoreBlkAndNBlk = false; // Suppress delayed assignment BLKANDNBLK
|
||||
using VarMap = std::map<const std::pair<AstNodeModule*, std::string>, AstVar*>;
|
||||
VarMap m_modVarMap; // Table of new var names created under module
|
||||
VDouble0 m_statSharedSet; // Statistic tracking
|
||||
@@ -105,6 +107,7 @@ private:
|
||||
void markVarUsage(AstNodeVarRef* nodep, bool blocking) {
|
||||
// Ignore if warning is disabled on this reference (used by V3Force).
|
||||
if (nodep->fileline()->warnIsOff(V3ErrorCode::BLKANDNBLK)) return;
|
||||
if (m_ignoreBlkAndNBlk) return;
|
||||
if (blocking) nodep->user5(true);
|
||||
AstVarScope* const vscp = nodep->varScopep();
|
||||
// UINFO(4, " MVU " << blocking << " " << nodep << endl);
|
||||
@@ -485,6 +488,13 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
virtual 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
|
||||
nodep->v3fatalSrc(
|
||||
"For statements should have been converted to while statements in V3Begin");
|
||||
|
||||
+3
-5
@@ -26,9 +26,10 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Depth.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -68,10 +69,7 @@ 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
|
||||
|
||||
@@ -23,10 +23,11 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3DepthBlock.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
+4
-3
@@ -25,10 +25,11 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Descope.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
@@ -248,8 +249,8 @@ private:
|
||||
VL_RESTORER(m_funcp);
|
||||
if (!nodep->user1()) {
|
||||
// Static functions should have been moved under the corresponding AstClassPackage
|
||||
UASSERT(!(nodep->isStatic() && VN_IS(m_modp, Class)),
|
||||
"Static function under AstClass");
|
||||
UASSERT_OBJ(!(nodep->isStatic() && VN_IS(m_modp, Class)), nodep,
|
||||
"Static function under AstClass");
|
||||
m_funcp = nodep;
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(true);
|
||||
|
||||
+2
-1
@@ -17,10 +17,11 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3DupFinder.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3File.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
|
||||
+4
-5
@@ -23,8 +23,8 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3Error.h"
|
||||
#include "V3Hasher.h"
|
||||
|
||||
#include <map>
|
||||
@@ -44,7 +44,7 @@ class V3DupFinder final : private std::multimap<V3Hash, AstNode*> {
|
||||
using Super = std::multimap<V3Hash, AstNode*>;
|
||||
|
||||
// MEMBERS
|
||||
const V3Hasher* const m_hasherp; // Pointer to owned hasher
|
||||
const V3Hasher* const m_hasherp = nullptr; // Pointer to owned hasher
|
||||
const V3Hasher& m_hasher; // Reference to hasher
|
||||
|
||||
public:
|
||||
@@ -52,9 +52,8 @@ public:
|
||||
V3DupFinder()
|
||||
: m_hasherp{new V3Hasher}
|
||||
, m_hasher{*m_hasherp} {}
|
||||
V3DupFinder(const V3Hasher& hasher)
|
||||
: m_hasherp{nullptr}
|
||||
, m_hasher{hasher} {}
|
||||
explicit V3DupFinder(const V3Hasher& hasher)
|
||||
: m_hasher{hasher} {}
|
||||
~V3DupFinder() {
|
||||
if (m_hasherp) delete m_hasherp;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user