mirror of
https://github.com/verilator/verilator.git
synced 2026-10-03 16:44:11 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ca45df9c7 | ||
|
|
8db9c1d227 | ||
|
|
930f35acc9 | ||
|
|
13e0fc7c27 | ||
|
|
a730daabef | ||
|
|
3c28b72897 | ||
|
|
05e5f1955e | ||
|
|
378800ee4a | ||
|
|
43a57da950 | ||
|
|
99a43b7695 | ||
|
|
59d2eea302 | ||
|
|
6f3f64e63f | ||
|
|
48c71ef76c | ||
|
|
563faeb33f | ||
|
|
9c5c77c69c | ||
|
|
4573e04488 | ||
|
|
6cb0a41857 | ||
|
|
124e3463fc | ||
|
|
1b15509a9c | ||
|
|
eb3f10399a | ||
|
|
4660806c8f | ||
|
|
6f5d1037b4 | ||
|
|
24b097b228 | ||
|
|
a0ea96b355 | ||
|
|
a663da7393 | ||
|
|
c812a9b18f | ||
|
|
367249ec84 | ||
|
|
f4acc59b82 | ||
|
|
d1da1664f0 | ||
|
|
b100615726 | ||
|
|
ec0815e9ac | ||
|
|
97e9996f0b | ||
|
|
b7af859ba3 | ||
|
|
701fa5438a | ||
|
|
004865a8b2 | ||
|
|
3e5859e5da | ||
|
|
78555b683c | ||
|
|
c2e44dbd99 | ||
|
|
18fc3e6089 | ||
|
|
f4f8ea0dcf | ||
|
|
f78c4e8490 | ||
|
|
e6fe367bdb | ||
|
|
3426ee5170 | ||
|
|
a32b8d80f9 | ||
|
|
3b81654c72 | ||
|
|
6abd556f5a | ||
|
|
31c1df638b | ||
|
|
f4cb2c8cf2 | ||
|
|
7d5e19365e | ||
|
|
6fa1ade4e0 | ||
|
|
99c619a4c9 | ||
|
|
37a4002098 | ||
|
|
45ee949cc4 | ||
|
|
54f9f4b6a9 | ||
|
|
a61178bd89 | ||
|
|
2f5c58b345 | ||
|
|
403a197e23 | ||
|
|
3a659d460d | ||
|
|
8707c88787 | ||
|
|
167cba723e | ||
|
|
7f40dd8b5b | ||
|
|
1609c23fea | ||
|
|
216fc8a212 | ||
|
|
5136ea1082 | ||
|
|
5b931faf2b | ||
|
|
a2496e041e | ||
|
|
fab268f772 | ||
|
|
550d47b7f4 | ||
|
|
a01a21db86 | ||
|
|
2082f1aa9e | ||
|
|
50a5a1ff5b | ||
|
|
1600cc01a4 | ||
|
|
b9e1d55262 | ||
|
|
0d43c14b93 | ||
|
|
87d1c10fa7 | ||
|
|
a2506a6f96 | ||
|
|
20dba7464d | ||
|
|
cb5d03ff0b | ||
|
|
2727c33219 | ||
|
|
0419ed0430 | ||
|
|
1e3c7a5496 | ||
|
|
d2dfcbca53 | ||
|
|
e779934443 | ||
|
|
a3b255b054 | ||
|
|
f5caa4b7dc | ||
|
|
8f4490628f | ||
|
|
df566bdf4d | ||
|
|
981d3ce782 | ||
|
|
b7345eb5d5 | ||
|
|
2bd2b9324f | ||
|
|
298faa84ee | ||
|
|
43377ed8b0 | ||
|
|
b18037b7e7 | ||
|
|
095b1ccb67 | ||
|
|
2a30a87580 | ||
|
|
e11bc6beeb | ||
|
|
0a9b31bb30 | ||
|
|
34e37d7bd0 | ||
|
|
164a7a7c5d | ||
|
|
67ea819d82 | ||
|
|
60f9e21d8c | ||
|
|
dc037db1cd | ||
|
|
0658af90f5 | ||
|
|
131623de34 | ||
|
|
74ae879c2f | ||
|
|
8c3b754bac | ||
|
|
e080f5c0cb | ||
|
|
2284cf595d | ||
|
|
b1cc0c54c1 | ||
|
|
4cf017d7fe | ||
|
|
25f5db4b5f | ||
|
|
3cf9606ea9 | ||
|
|
6882f8c55e | ||
|
|
2696a9a5bd | ||
|
|
abb46c9cb2 | ||
|
|
bbb223f217 | ||
|
|
570e1bc35a | ||
|
|
11da07d3b9 | ||
|
|
2cfec0ecc3 | ||
|
|
e3e7b3f2b5 | ||
|
|
fe0b210a0c | ||
|
|
c383f4868f | ||
|
|
b66fdd7421 | ||
|
|
83878325a7 | ||
|
|
490bc63eb5 | ||
|
|
947b6fd23f | ||
|
|
de44ca8df3 | ||
|
|
3cee54dfdf | ||
|
|
dace2c0a9a | ||
|
|
ca4858eb7f | ||
|
|
a6d438d111 | ||
|
|
955ed3f193 | ||
|
|
8bffb8c391 | ||
|
|
85356f464f | ||
|
|
72993ec3dd | ||
|
|
d6a294b784 | ||
|
|
864a852bca | ||
|
|
b0992406aa | ||
|
|
9e2c8aefc8 | ||
|
|
4c584c17e6 | ||
|
|
3315a6e431 | ||
|
|
ba0183131c | ||
|
|
e1580b9c3d | ||
|
|
aadd5ea509 | ||
|
|
833df6d9ea | ||
|
|
607c19a67d | ||
|
|
d5cfe1a379 | ||
|
|
6e557791c8 |
+1
-1
@@ -1,4 +1,4 @@
|
||||
Checks: '*,-hicpp*,-android-cloexec-fopen,-cert-dcl50-cpp,-cert-env33-c,-cert-err34-c,-cert-err58-cpp,-clang-analyzer-core.UndefinedBinaryOperatorResult,-clang-analyzer-security*,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-no-malloc,-cppcoreguidelines-owning-memory,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-type-const-cast,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-type-static-cast-downcast,-cppcoreguidelines-pro-type-union-access,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-special-member-functions,-fuchsia-*,-google-default-arguments,-google-readability-todo,-google-runtime-references,-llvm-header-guard,-llvm-include-order,-misc-string-integer-assignment,-misc-string-literal-with-embedded-nul,-modernize-use-auto,-modernize-use-trailing-return-type,-readability-braces-around-statements,-readability-container-size-empty,-readability-delete-null-pointer,-readability-else-after-return,-readability-implicit-bool-conversion,-readability-named-parameter,-readability-static-accessed-through-instance'
|
||||
Checks: '*,-hicpp*,-android-cloexec-fopen,-cert-dcl50-cpp,-cert-env33-c,-cert-err34-c,-cert-err58-cpp,-clang-analyzer-core.UndefinedBinaryOperatorResult,-clang-analyzer-security*,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-no-malloc,-cppcoreguidelines-owning-memory,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-type-const-cast,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-type-static-cast-downcast,-cppcoreguidelines-pro-type-union-access,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-special-member-functions,-fuchsia-*,-google-default-arguments,-google-readability-todo,-google-runtime-references,-llvm-header-guard,-llvm-include-order,-misc-string-integer-assignment,-misc-string-literal-with-embedded-nul,-modernize-use-auto,-modernize-use-trailing-return-type,-readability-braces-around-statements,-readability-container-size-empty,-readability-delete-null-pointer,-readability-else-after-return,-readability-implicit-bool-conversion,-readability-named-parameter,-readability-static-accessed-through-instance,-llvmlibc-*,-altera-*'
|
||||
WarningsAsErrors: ''
|
||||
HeaderFilterRegex: ''
|
||||
FormatStyle: none
|
||||
|
||||
@@ -11,6 +11,8 @@ Thanks for taking the time to report this.
|
||||
|
||||
What would you like added/supported?
|
||||
|
||||
What 'verilator --version' are you using? Did you try it with the git master version?
|
||||
|
||||
Can you attach an example that runs on other simulators? (Must be openly licensed, ideally in test_regress format.)
|
||||
|
||||
May we assist you in trying to fix this in Verilator yourself?
|
||||
|
||||
@@ -8,3 +8,5 @@ assignees: ''
|
||||
---
|
||||
|
||||
How may we help - what is your question?
|
||||
|
||||
(If reporting a bug or requesting a feature please use one of the other issue templates.)
|
||||
|
||||
@@ -96,3 +96,10 @@ jobs:
|
||||
env:
|
||||
TESTS: coverage-${{ matrix.test }}${{ matrix.num }}
|
||||
run: ./ci/ci-script.bash
|
||||
|
||||
- name: Upload coverage data to Codecov
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
run: |
|
||||
find . -name '*.gcno' -exec rm {} \;
|
||||
./ci/codecov -v upload-process -Z --sha ${{ github.sha }} -f nodist/obj_dir/coverage/app_total.info
|
||||
|
||||
@@ -46,3 +46,6 @@ verilator-config-version.cmake
|
||||
/test_regress/snapshot/
|
||||
xmverilog.*
|
||||
xrun.history
|
||||
|
||||
# Normal CMake build directory
|
||||
/build
|
||||
|
||||
+8
-3
@@ -15,7 +15,7 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
|
||||
project(Verilator
|
||||
VERSION 5.026
|
||||
VERSION 5.028
|
||||
HOMEPAGE_URL https://verilator.org
|
||||
LANGUAGES CXX
|
||||
)
|
||||
@@ -24,12 +24,13 @@ option(DEBUG_AND_RELEASE_AND_COVERAGE
|
||||
"Builds both the debug and release binaries, overriding CMAKE_BUILD_TYPE. Not supported under MSBuild.")
|
||||
|
||||
find_package(Python3 COMPONENTS Interpreter)
|
||||
set(PYTHON3 Python3::Interpreter)
|
||||
set(PYTHON3 ${Python3_EXECUTABLE})
|
||||
set(CMAKE_INSTALL_DATADIR ${CMAKE_INSTALL_PREFIX})
|
||||
include(GNUInstallDirs)
|
||||
include(CMakePackageConfigHelpers)
|
||||
include(CheckStructHasMember)
|
||||
include(ExternalProject)
|
||||
include(FindThreads)
|
||||
|
||||
if (NOT WIN32)
|
||||
message(WARNING "CMake support on Linux/OSX is experimental.")
|
||||
@@ -95,10 +96,14 @@ endif()
|
||||
|
||||
set(PACKAGE_NAME ${PROJECT_NAME})
|
||||
set(PACKAGE_VERSION ${PROJECT_VERSION})
|
||||
set(CXX ${CMAKE_CXX_COMPILER})
|
||||
set(AR ${CMAKE_AR})
|
||||
|
||||
configure_file(include/verilated_config.h.in include/verilated_config.h @ONLY)
|
||||
configure_file(include/verilated.mk.in include/verilated.mk @ONLY)
|
||||
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/include/verilated_config.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/include/verilated.mk DESTINATION ${CMAKE_INSTALL_PREFIX}/include)
|
||||
|
||||
configure_package_config_file(verilator-config.cmake.in verilator-config.cmake
|
||||
INSTALL_DESTINATION ${CMAKE_INSTALL_PREFIX}
|
||||
@@ -118,6 +123,7 @@ foreach (program
|
||||
verilator_ccache_report
|
||||
verilator_difftree
|
||||
verilator_profcfunc
|
||||
verilator_includer
|
||||
)
|
||||
install(PROGRAMS bin/${program} TYPE BIN)
|
||||
endforeach()
|
||||
@@ -130,7 +136,6 @@ install(DIRECTORY examples TYPE DATA FILES_MATCHING
|
||||
|
||||
install(DIRECTORY include TYPE DATA FILES_MATCHING
|
||||
PATTERN "include/verilated_config.h"
|
||||
PATTERN "include/verilated.mk"
|
||||
PATTERN "include/*.[chv]"
|
||||
PATTERN "include/*.cpp"
|
||||
PATTERN "include/*.sv"
|
||||
|
||||
@@ -8,6 +8,86 @@ The changes in each Verilator version are described below. The
|
||||
contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
|
||||
Verilator 5.028 2024-08-21
|
||||
==========================
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Support state-dependent constraints (#5217). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Support cross-module clocking variable access (#5184). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Support inline constraints for class randomization methods (#5234). [Krzysztof Boroński]
|
||||
* Support clocking blocks in virtual interfaces (#5235). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Support `$assertcontrol` assertion_type (#5236). [Bartłomiej Chmiel, Antmicro Ltd.]
|
||||
* Support conditional constraints (#5245). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Support`--compiler-include` headers in user-supplied cpp files (#5271). [Bartłomiej Chmiel, Antmicro Ltd.]
|
||||
* Support `rand_mode` (#5273). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Support `this.randomize with` (#5282). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Support foreach constraints (#5302). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Support `parameter type` in hierarchical blocks (#5309) (#5333). [Bartłomiej Chmiel, Antmicro Ltd.]
|
||||
* Support assertcontrol directive type (#5310). [Bartłomiej Chmiel, Antmicro Ltd.]
|
||||
* Support inline random variable control (#5317). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Support streaming operator on arrays and wide data (#5326). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Support streams to/from arrays of wide data (#5334). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Support `constraint_mode` (#5338). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Support constraining AstSel (#5344). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Support default value on module input (#5358) (#5373). [Drew Ranck]
|
||||
* Add `--compiler-include` for additional C++ includes (#5139) (#5202). [Bartłomiej Chmiel, Antmicro Ltd.]
|
||||
* Add `--emit-accessors` (#5182) (#5227). [Ryan Ziegler]
|
||||
* Add suggestions on misspelled PLI functions.
|
||||
* Add warning on dist in constraints (#5264). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Add more `rand_mode` unsupported errors (#5329). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Add parsing but otherwise ignore std::randomize (#5354). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Add Verilated cc define when `--timing` used (#5383). [Kaleb Barrett]
|
||||
* Improve emitted code to use a reference for VlSelf (#5254). [Yangyu Chen]
|
||||
* Fix monitor block sensitivity items (#4400) (#5294). [Udaya Raj Subedi]
|
||||
* Fix fusing macro arguments to not ignore whitespace (#5061). [Tudor Timi]
|
||||
* Fix optimized-out sensitivity trees with `--timing` (#5080) (#5349). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Fix classes/modules of case-similar names (#5109). [Arkadiusz Kozdra]
|
||||
* Fix mis-removing $value$plusargs calls (#5127) (#5137). [Seth Pellegrino]
|
||||
* Fix incorrect result of width mismatch (#5186) (#5189). [Yutetsu TAKATSUKASA]
|
||||
* Fix compiler coroutine check (#5190) (#5300). [Ricardo Barbedo]
|
||||
* Fix shortened module names when searching for files (#5196) (#5246). [Tim Hutt]
|
||||
* Fix `--x-assign` to be independent from `+verilator+rand+reset` (#5214). [Andrew Nolte]
|
||||
* Fix splitting if statements with impure conditions (#5219). [Bartłomiej Chmiel, Antmicro Ltd.]
|
||||
* Fix unknown conversion on queues (#5220). [Alex Solomatnikov]
|
||||
* Fix top-level unpacked structure resets (#5221).
|
||||
* Fix concurrency for mailbox and semaphores (#5222). [Liam Braun]
|
||||
* Fix forks capturing non-input ports in tasks (#5237) (#5343). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Fix toggle coverage aggregation on same line (#5248). [Krzysztof Obłonczek]
|
||||
* Fix error on empty generate with -O0 (#5250).
|
||||
* Fix unconstrained randomization of unpacked structs (#5252). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Fix inlining of variables driven from forced vars (#5259). [Geza Lore]
|
||||
* Fix tracing with `--main-top-name -` (#5261). [Ethan Sifferman]
|
||||
* Fix randomization when used with inheritance (#5268). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Fix inline constraints creating class random generator (#5280). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Fix WIDTHEXPAND on left shift of intuitive amount (#5284). [Greg Taylor]
|
||||
* Fix elaborating foreach loops (#5285). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Fix initializing static array in dynamic arrays and queues (#5287). [Baruch Sterin]
|
||||
* Fix static variable initializers in procedures (#5296). [Bartłomiej Chmiel, Antmicro Ltd.]
|
||||
* Fix randomizing current object with `rand` class instance member (#5292). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Fix handling of rand fields not referenced in constraints (#5305). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Fix Python3 path discovery in make flows to avoid mixing system and user python interpreters (#5307) [Markus Krause]
|
||||
* Fix make flows to pass PYTHON3 (like PERL) (#5307) (#5308). [Markus Krause]
|
||||
* Fix assert on wide expression (#5319) (#5324). [Varun Koyyalagunta]
|
||||
* Fix output clock variable overwriting signal (#5320) (#5347). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Fix stringify in nested preprocessor macros (#5323). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Fix $sformat with array arguments (#5330). [Abe Jordan]
|
||||
* Fix -Wunused-but-set-variable clang warning (#5331). [Bartłomiej Chmiel, Antmicro Ltd.]
|
||||
* Fix purity of functions with AstJumpBlock or AstStmtExpr (#5332). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Fix compilation error on unreachable disable fork / wait fork (#5339). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Fix missing type coercion in 'inside {array}' (#5340). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Fix +: and -: unpacked array slicing when array has nonzero low index (#5345) (#5387). [James Bailey]
|
||||
* Fix tracing_{on,off} in the presence of non-inlined modules (#5346). [Geza Lore]
|
||||
* Fix NBAs in suspendables (#5348). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Fix lint_off on Errors (#5351) (#5363). [Ethan Sifferman]
|
||||
* Fix cache config file resolution performance (#5369). [Geza Lore]
|
||||
* Fix capturing fields from superclass in `randomize() with` (#5389). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Fix virtual interface null checks (#5391). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Fix ==? and !=? with X values.
|
||||
* Fix CPU time being zero.
|
||||
* Fix inline function ref port persistence.
|
||||
|
||||
|
||||
Verilator 5.026 2024-06-15
|
||||
==========================
|
||||
|
||||
@@ -34,6 +114,7 @@ Verilator 5.026 2024-06-15
|
||||
* Add traceCapable indication to model header (#5053). [Vito Gamberini]
|
||||
* Add increasing of stack size when possible (#5071) (#5104). [Yinan Xu]
|
||||
* Add assertion on reusing VerilatedContext (#5167).
|
||||
* Add `--pins-sc-uint-bool` to force SystemC uint type (#5192). [Bartłomiej Chmiel, Antmicro Ltd.]
|
||||
* Improve DFG regularization in cyclic graphs (#5142). [Geza Lore]
|
||||
* Improve VerilatedVpiPutHolder storage requirements (#5144). [Kaleb Barrett]
|
||||
* Fix coroutines without awaits to have a co_return (#4208) (#5175). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
@@ -72,7 +153,11 @@ Verilator 5.026 2024-06-15
|
||||
* Fix SystemC BITS_PER_DIGIT in VL_ASSIGN_SBW (#5170). [Bartłomiej Chmiel, Antmicro Ltd.]
|
||||
* Fix non-constant replication in concats (#5171). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Fix table optimization when applied on real data type (#5172) (#5173). [Arthur Rosa]
|
||||
* Fix signed types emitted in hierarchical Verilation (#5178). [Bartłomiej Chmiel, Antmicro Ltd.]
|
||||
* Fix DPI import of null C-string (#5179).
|
||||
* Fix CMake installation missing verilated.mk (#5187) (#5188). [Philip Axer]
|
||||
* Fix linking with pthreads on CMake (#5194). [Tim Hutt]
|
||||
* Fix clang-17 coroutines configuration with -std=gnu++20 (#5200). [Gus Smith]
|
||||
|
||||
|
||||
Verilator 5.024 2024-04-05
|
||||
@@ -620,7 +705,7 @@ Verilator 5.006 2023-01-22
|
||||
**Minor:**
|
||||
|
||||
* Support clocking blocks (#3674). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Support packed structs (#3802). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Support unpacked structs (#3802). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Support Windows-native builds using cmake (#3814). [Kritik Bhimani]
|
||||
* Support p format for UnpackArray (#3877). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Support property calls without parenthesis (#3879) (#3893). [Ryszard Rozak, Antmicro Ltd]
|
||||
@@ -1511,6 +1596,7 @@ Verilator 4.026 2020-01-11
|
||||
* Fix expand optimization slowing --lint-only. Closes #2091. [Thomas Watts]
|
||||
* Fix %{number}s with strings. #2093. [agrobman]
|
||||
* Fix shebang breaking some shells. Closes #2067. [zdave]
|
||||
* Fix errors on using string in incorrect format (#5240). [John Demme]
|
||||
|
||||
|
||||
Verilator 4.024 2019-12-08
|
||||
|
||||
@@ -447,6 +447,7 @@ PY_PROGRAMS = \
|
||||
nodist/fuzzer/actual_fail \
|
||||
nodist/fuzzer/generate_dictionary \
|
||||
nodist/install_test \
|
||||
nodist/log_changes \
|
||||
|
||||
PY_FILES = \
|
||||
$(PY_PROGRAMS) \
|
||||
|
||||
+6
-2
@@ -324,6 +324,7 @@ detailed descriptions of these arguments.
|
||||
--clk <signal-name> Mark specified signal as clock
|
||||
--no-clk <signal-name> Prevent marking specified signal as clock
|
||||
--compiler <compiler-name> Tune for specified C++ compiler
|
||||
--compiler-include Include additional header in the precompiled one
|
||||
--converge-limit <loops> Tune convergence settle time
|
||||
--coverage Enable all coverage
|
||||
--coverage-line Enable line coverage
|
||||
@@ -356,6 +357,7 @@ detailed descriptions of these arguments.
|
||||
--dumpi-tree-json <level> Enable dumping Ast .tree.json files at level
|
||||
--dumpi-<srcfile> <level> Enable dumping everything in source file at level
|
||||
-E Preprocess, but do not compile
|
||||
--emit-accessors Emit getter and setter methods for model top class
|
||||
--error-limit <value> Abort after this number of errors
|
||||
--exe Link to create executable
|
||||
--expand-limit <value> Set expand optimization limit
|
||||
@@ -375,6 +377,7 @@ detailed descriptions of these arguments.
|
||||
--get-supported <feature> Get if feature is supported
|
||||
--help Show this help
|
||||
--hierarchical Enable hierarchical Verilation
|
||||
--hierarchical-params-file <name> Internal option that specifies parameters file for hier blocks
|
||||
-I<dir> Directory to search for includes
|
||||
--if-depth <value> Tune IFDEPTH warning
|
||||
+incdir+<dir> Directory to search for includes
|
||||
@@ -410,6 +413,7 @@ detailed descriptions of these arguments.
|
||||
--pins-inout-enables Specify that __en and __out signals be created for inouts
|
||||
--pins-sc-biguint Specify types for top-level ports
|
||||
--pins-sc-uint Specify types for top-level ports
|
||||
--pins-sc-uint-bool 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
|
||||
@@ -459,8 +463,8 @@ detailed descriptions of these arguments.
|
||||
--trace-coverage Enable tracing of coverage
|
||||
--trace-depth <levels> Depth of tracing
|
||||
--trace-fst Enable FST waveform creation
|
||||
--trace-max-array <depth> Maximum bit width for tracing
|
||||
--trace-max-width <width> Maximum array depth for tracing
|
||||
--trace-max-array <depth> Maximum array depth for tracing
|
||||
--trace-max-width <width> Maximum bit width for tracing
|
||||
--trace-params Enable tracing of parameters
|
||||
--trace-structs Enable tracing structure names
|
||||
--trace-threads <threads> Enable FST waveform creation on separate threads
|
||||
|
||||
+1
-4
@@ -192,13 +192,10 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
||||
fatal "Unknown test: $TESTS"
|
||||
;;
|
||||
esac
|
||||
|
||||
# 22.04: ccache -s -v
|
||||
ccache -s
|
||||
|
||||
# Upload coverage data
|
||||
if [[ $TESTS == coverage-* ]]; then
|
||||
bash <(cat ci/coverage-upload.sh) -f nodist/obj_dir/coverage/app_total.info
|
||||
fi
|
||||
else
|
||||
##############################################################################
|
||||
# Unknown build stage
|
||||
|
||||
Executable
BIN
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -11,10 +11,11 @@ easily as a docker image, e.g.:
|
||||
|
||||
docker run -ti verilator/verilator:latest --version
|
||||
|
||||
This will install the container, run the latest Verilator and print
|
||||
This will pull the container from `docker hub
|
||||
<https://hub.docker.com/r/verilator/verilator>`_, run the latest Verilator and print
|
||||
Verilator's version.
|
||||
|
||||
Containers are automatically built for all released versions, so you may
|
||||
Containers are automatically built and pushed to docker hub for all released versions, so you may
|
||||
easily compare results across versions, e.g.:
|
||||
|
||||
::
|
||||
|
||||
+43
-26
@@ -10,7 +10,7 @@
|
||||
# Then 'make maintainer-dist'
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[5.026 2024-06-15],
|
||||
AC_INIT([Verilator],[5.028 2024-08-21],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
|
||||
@@ -288,6 +288,43 @@ AC_DEFUN([_MY_CXX_CHECK_OPT],
|
||||
_MY_CXX_CHECK_IFELSE($2, $1="$$1 $2")
|
||||
])
|
||||
|
||||
AC_DEFUN([_MY_CXX_CHECK_CORO_SET],
|
||||
[# _MY_CXX_CHECK_CORO_SET(variable,option)
|
||||
# Check if compiler supports coroutines with specific option. If it does,
|
||||
# set variable to option, only if not previously set.
|
||||
# Define HAVE_COROUTINES if supported.
|
||||
if test "$$1" = ""; then
|
||||
if test "$2" != ""; then
|
||||
_my_msg_opt=" with $2"
|
||||
else
|
||||
_my_msg_opt=""
|
||||
fi
|
||||
AC_MSG_CHECKING([whether coroutines are supported by $CXX$_my_msg_opt])
|
||||
ACO_SAVE_CXXFLAGS="$CXXFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $2"
|
||||
AC_LINK_IFELSE(
|
||||
[AC_LANG_PROGRAM([
|
||||
#if defined(__clang__) && !defined(__cpp_impl_coroutine)
|
||||
#define __cpp_impl_coroutine 1
|
||||
#endif
|
||||
#include <coroutine>
|
||||
],[[std::coroutine_handle<> h;]])],
|
||||
[_my_result=yes],
|
||||
[AC_LINK_IFELSE(
|
||||
[AC_LANG_PROGRAM(
|
||||
[#include <experimental/coroutine>],
|
||||
[[std::experimental::coroutine_handle<> h;]])],
|
||||
[_my_result=yes],
|
||||
[_my_result=no])])
|
||||
AC_MSG_RESULT($_my_result)
|
||||
CXXFLAGS="$ACO_SAVE_CXXFLAGS"
|
||||
if test "$_my_result" = "yes" ; then
|
||||
$1="$2"
|
||||
AC_DEFINE([HAVE_COROUTINES],[1],[Defined if coroutines are supported by $CXX])
|
||||
fi
|
||||
fi
|
||||
])
|
||||
|
||||
AC_DEFUN([_MY_LDLIBS_CHECK_FLAG],
|
||||
[# _MY_LDLIBS_CHECK_FLAG(flag) -- Check if linker supports specific options
|
||||
# Set $_my_result appropriately
|
||||
@@ -373,32 +410,12 @@ _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_WEXTRA,-Wthread-safety)
|
||||
AC_SUBST(CFG_CXXFLAGS_WEXTRA)
|
||||
|
||||
# Flags for coroutine support for dynamic scheduling
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_COROUTINES,-fcoroutines-ts)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_COROUTINES,-fcoroutines)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_COROUTINES,-fcoroutines-ts -Wno-deprecated-experimental-coroutine)
|
||||
_MY_CXX_CHECK_CORO_SET(CFG_CXXFLAGS_COROUTINES, "")
|
||||
_MY_CXX_CHECK_CORO_SET(CFG_CXXFLAGS_COROUTINES,-fcoroutines-ts)
|
||||
_MY_CXX_CHECK_CORO_SET(CFG_CXXFLAGS_COROUTINES,-fcoroutines)
|
||||
_MY_CXX_CHECK_CORO_SET(CFG_CXXFLAGS_COROUTINES,-fcoroutines-ts -Wno-deprecated-experimental-coroutine)
|
||||
_MY_CXX_CHECK_CORO_SET(CFG_CXXFLAGS_COROUTINES,-std=gnu++20)
|
||||
AC_SUBST(CFG_CXXFLAGS_COROUTINES)
|
||||
|
||||
# HAVE_COROUTINES
|
||||
# Check if coroutines are supported at all
|
||||
AC_MSG_CHECKING([whether coroutines are supported by $CXX])
|
||||
ACO_SAVE_CXXFLAGS="$CXXFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $CFG_CXXFLAGS_COROUTINES"
|
||||
AC_LINK_IFELSE(
|
||||
[AC_LANG_PROGRAM([
|
||||
#ifdef __clang__
|
||||
#define __cpp_impl_coroutine 1
|
||||
#endif
|
||||
#include <coroutine>
|
||||
],[[]])],
|
||||
[_my_result=yes
|
||||
AC_DEFINE([HAVE_COROUTINES],[1],[Defined if coroutines are supported by $CXX])],
|
||||
[AC_LINK_IFELSE(
|
||||
[AC_LANG_PROGRAM([#include <experimental/coroutine>],[[]])],
|
||||
[_my_result=yes
|
||||
AC_DEFINE([HAVE_COROUTINES],[1],[Defined if coroutines are supported by $CXX])],
|
||||
[_my_result=no])])
|
||||
AC_MSG_RESULT($_my_result)
|
||||
CXXFLAGS="$ACO_SAVE_CXXFLAGS"
|
||||
AC_SUBST(HAVE_COROUTINES)
|
||||
|
||||
# Flags for compiling Verilator internals including parser always
|
||||
|
||||
@@ -25,6 +25,7 @@ Aylon Chaim Porat
|
||||
Bartłomiej Chmiel
|
||||
Cameron Kirk
|
||||
Chih-Mao Chen
|
||||
Chris Bachhuber
|
||||
Chris Randall
|
||||
Christopher Taylor
|
||||
Chuxuan Wang
|
||||
@@ -76,6 +77,7 @@ Iru Cai
|
||||
Ivan Vnučec
|
||||
Iztok Jeras
|
||||
Jake Merdich
|
||||
James Bailey
|
||||
James Hanlon
|
||||
James Hutchinson
|
||||
James Pallister
|
||||
@@ -118,8 +120,11 @@ Kritik Bhimani
|
||||
Krzysztof Bieganski
|
||||
Krzysztof Boronski
|
||||
Krzysztof Boroński
|
||||
Krzysztof Obłonczek
|
||||
Kuba Ober
|
||||
Larry Doolittle
|
||||
Liam Braun
|
||||
Luca Colagrande
|
||||
Ludwig Rogiers
|
||||
Lukasz Dalek
|
||||
Maarten De Braekeleer
|
||||
@@ -157,6 +162,7 @@ Pengcheng Xu
|
||||
Peter Debacker
|
||||
Peter Horvath
|
||||
Peter Monsson
|
||||
Philip Axer
|
||||
Philipp Wagner
|
||||
Pierre-Henri Horrein
|
||||
Pieter Kapsenberg
|
||||
@@ -166,15 +172,18 @@ Quentin Corradi
|
||||
Rafal Kapuscik
|
||||
Rasfunk
|
||||
Raynard Qiao
|
||||
Ricardo Barbedo
|
||||
Richard Myers
|
||||
Risto Pejašinović
|
||||
Robert Balas
|
||||
Rupert Swarbrick
|
||||
Ryan Ziegler
|
||||
Ryszard Rozak
|
||||
Samuel Riedel
|
||||
Sean Cross
|
||||
Sebastien Van Cauwenberghe
|
||||
Sergi Granell
|
||||
Seth Pellegrino
|
||||
Srinivasan Venkataramanan
|
||||
Stefan Wallentowitz
|
||||
Stephen Henry
|
||||
@@ -194,6 +203,7 @@ Tudor Timi
|
||||
Tymoteusz Blazejczyk
|
||||
Udi Finkelstein
|
||||
Unai Martinez-Corral
|
||||
Udaya Raj Subedi
|
||||
Valentin Atepalikhin
|
||||
Varun Koyyalagunta
|
||||
Vassilis Papaefstathiou
|
||||
@@ -204,6 +214,7 @@ William D. Jones
|
||||
Wilson Snyder
|
||||
Xi Zhang
|
||||
Yan Xu
|
||||
Yangyu Chen
|
||||
Yinan Xu
|
||||
Yoda Lee
|
||||
Yossi Nivin
|
||||
@@ -215,3 +226,4 @@ Zhanglei Wang
|
||||
Zhou Shen
|
||||
Zixi Li
|
||||
أحمد المحمودي
|
||||
404allen404
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
.. comment: generated by t_lint_pinmissing_bad
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 2
|
||||
|
||||
module t;
|
||||
sub sub(); // <--- Warning
|
||||
endmodule
|
||||
module sub
|
||||
(output port);
|
||||
endmodule
|
||||
@@ -0,0 +1,4 @@
|
||||
.. comment: generated by t_lint_pinmissing_bad
|
||||
.. code-block::
|
||||
|
||||
%Warning-PINMISSING: example.v:1:8 Cell has missing pin: 'port'
|
||||
@@ -0,0 +1,11 @@
|
||||
.. comment: generated by t_lint_pkgnodecl_bad
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 2
|
||||
|
||||
module t;
|
||||
initial Pkg::hello(); //<--- Warning
|
||||
endmodule
|
||||
package Pkg;
|
||||
function void hello(); endfunction
|
||||
endpackage
|
||||
@@ -0,0 +1,4 @@
|
||||
.. comment: generated by t_lint_pkgnodecl_bad
|
||||
.. code-block::
|
||||
|
||||
%Error-PKGNODECL: example.v:1:12 Package/class 'Pkg' not found, and needs to be predeclared (IEEE 1800-2023 26.3)
|
||||
@@ -222,6 +222,13 @@ Summary:
|
||||
expressions into sub-expressions to avoid error C1009, and breaking
|
||||
deep blocks into functions to avoid error C1061.
|
||||
|
||||
.. option:: --compiler-include <header-path>
|
||||
|
||||
Specifies additional headers to be included in the final PCH header.
|
||||
It is required to add them to this header, due to compilers'
|
||||
limitation that allow only one precompiled header per compilation.
|
||||
Use this instead of ::vlopt:`-CFLAGS` with `-include <header-path>`.
|
||||
|
||||
.. option:: --converge-limit <loops>
|
||||
|
||||
Rarely needed. Specifies the maximum number of runtime iterations
|
||||
@@ -476,6 +483,12 @@ Summary:
|
||||
See also :vlopt:`--dump-defines`, :vlopt:`-P`, and
|
||||
:vlopt:`--pp-comments` options.
|
||||
|
||||
.. option:: --emit-accessors
|
||||
|
||||
Emit getter and setter methods for each top-level signal in the
|
||||
model top class. Signals are still available as public members,
|
||||
but with the `__Vm_sig_` prefix.
|
||||
|
||||
.. option:: --error-limit <value>
|
||||
|
||||
After this number of errors are encountered during Verilator run, exit.
|
||||
@@ -710,6 +723,13 @@ Summary:
|
||||
:option:`/*verilator&32;hier_block*/` metacomment is ignored. See
|
||||
:ref:`Hierarchical Verilation`.
|
||||
|
||||
.. option:: --hierarchical-params-file <filename>
|
||||
|
||||
Internal flag inserted used during :vlopt:`--hierarchical`; specifies
|
||||
name of hierarchical parameters file for deparametrized modules with
|
||||
:option:`/*verilator&32;hier_block*/` metacomment. See
|
||||
:ref:`Hierarchical Verilation`.
|
||||
|
||||
.. option:: -I<dir>
|
||||
|
||||
See :vlopt:`-y`.
|
||||
@@ -1004,6 +1024,10 @@ Summary:
|
||||
:vlopt:`--pins-sc-biguint` combination, it results in sc_uint being used
|
||||
between 2 and 64 and sc_biguint being used between 65 and 512.
|
||||
|
||||
.. option:: --pins-sc-uint-bool
|
||||
|
||||
Specifies SystemC inputs/outputs one bit wide should use sc_uint<1>.
|
||||
|
||||
.. option:: --pins-sc-biguint
|
||||
|
||||
Specifies SystemC inputs/outputs greater than 65 bits wide should use
|
||||
@@ -1979,6 +2003,13 @@ The grammar of configuration commands is as follows:
|
||||
that the setting is ignored unless the :vlopt:`--hierarchical` option is
|
||||
specified. See :ref:`Hierarchical Verilation`.
|
||||
|
||||
.. option:: hier_params -module "<modulename>"
|
||||
|
||||
Specifies that the module contains parameters a :vlopt:`--hierarchical` block. This option
|
||||
is used internally to specify parameters for deparametrized hier block instances.
|
||||
This option should not be used directly.
|
||||
See :ref:`Hierarchical Verilation`.
|
||||
|
||||
.. option:: inline -module "<modulename>"
|
||||
|
||||
Specifies the module may be inlined into any modules that use this
|
||||
|
||||
+5
-3
@@ -259,9 +259,11 @@ How do I view waveforms (aka dumps or traces)?
|
||||
""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Verilator creates standard VCD (Value Change Dump) and FST files. VCD
|
||||
files are viewable with the open-source GTKWave (recommended), or Dinotrace
|
||||
(legacy) programs, or any of the many closed-source offerings; FST is
|
||||
supported only by GTKWave.
|
||||
files are viewable with the open-source
|
||||
`GTKWave <http://gtkwave.sourceforge.net/>`_,
|
||||
`Surfer <https://surfer-project.org/>`_, Dinotrace (legacy), or any of the
|
||||
many closed-source viewer offerings; FST is supported only by GTKWave and
|
||||
Surfer.
|
||||
|
||||
|
||||
How do I speed up writing large waveform (trace) files?
|
||||
|
||||
+12
-10
@@ -48,12 +48,6 @@ For --cc/--sc, it creates:
|
||||
- Make include file for compiling (from --make gmake)
|
||||
* - *{prefix}*\ _classes.mk
|
||||
- Make include file with class names (from --make gmake)
|
||||
* - *{prefix}*\ _hier.mk
|
||||
- Make file for hierarchy blocks (from --make gmake)
|
||||
* - *{prefix}*\ __hierMkArgs.f
|
||||
- Arguments for hierarchical Verilation (from --make gmake)
|
||||
* - *{prefix}*\ __hierCMakeArgs.f
|
||||
- Arguments for hierarchical Verilation (from --make cmake)
|
||||
* - *{prefix}*\ .h
|
||||
- Model header
|
||||
* - *{prefix}*\ .cpp
|
||||
@@ -95,16 +89,24 @@ For --cc/--sc, it creates:
|
||||
* - *{prefix}{each_verilog_module}{__DepSet_hash__n}*\ .cpp
|
||||
- Additional lower C++ files (hashed to reduce build times)
|
||||
|
||||
For --hierarchy mode, it creates:
|
||||
For --hierarchical mode, it creates:
|
||||
|
||||
.. list-table::
|
||||
|
||||
* - V\ *{hier_block}*\ /
|
||||
- Directory to Verilate each hierarchy block (from --hierarchy)
|
||||
- Directory to Verilate each hierarchical block (from --hierarchical)
|
||||
* - *{prefix}*\ __hierVer.d
|
||||
- Make dependencies of the top module (from --hierarchy)
|
||||
- Make dependencies of the top module (from --hierarchical)
|
||||
* - *{prefix}*\ _hier.mk
|
||||
- Make file for hierarchical blocks (from --make gmake)
|
||||
* - *{prefix}*\ __hierCMakeArgs.f
|
||||
- Arguments for hierarchical Verilation (from --make cmake)
|
||||
* - *{prefix}*\ __hierMkArgs.f
|
||||
- Arguments for hierarchical Verilation (from --make gmake)
|
||||
* - *{prefix}*\ __hierParameters.v
|
||||
- Module parameters for hierarchical blocks
|
||||
* - *{prefix}*\ __hier.dir
|
||||
- Directory to store .dot, .vpp, .tree of top module (from --hierarchy)
|
||||
- Directory to store .dot, .vpp, .tree of top module (from --hierarchical)
|
||||
|
||||
In specific debug and other modes, it also creates:
|
||||
|
||||
|
||||
@@ -304,9 +304,9 @@ GNU Make
|
||||
========
|
||||
|
||||
Verilator defaults to creating GNU Make makefiles for the model. Verilator
|
||||
will call make automatically when the :vlopt:'--build' option is used.
|
||||
will call make automatically when the :vlopt:`--build` option is used.
|
||||
|
||||
If calling Verilator from a makefile, the :vlopt:'-MMD' option will create
|
||||
If calling Verilator from a makefile, the :vlopt:`--MMD` option will create
|
||||
a dependency file, allowing Make to only run Verilator if input Verilog
|
||||
files change.
|
||||
|
||||
|
||||
+58
-23
@@ -419,9 +419,9 @@ List Of Warnings
|
||||
|
||||
.. option:: CONSTRAINTIGN
|
||||
|
||||
Warns that Verilator does not support :code:`constraint`,
|
||||
:code:`constraint_mode`, or :code:`rand_mode`, and the construct was are
|
||||
ignored.
|
||||
Warns that Verilator does not support certain forms of
|
||||
:code:`constraint`, :code:`constraint_mode`, or :code:`rand_mode`, and
|
||||
the construct was are ignored.
|
||||
|
||||
Ignoring this warning may make Verilator randomize() simulations differ
|
||||
from other simulators.
|
||||
@@ -1264,7 +1264,7 @@ List Of Warnings
|
||||
|
||||
.. option:: PINMISSING
|
||||
|
||||
.. TODO better example
|
||||
.. TODO better example (frequent)
|
||||
|
||||
Warns that a module has a pin that is not mentioned in an instance. If
|
||||
a pin is not missing it should still be specified on the instance
|
||||
@@ -1273,6 +1273,22 @@ List Of Warnings
|
||||
Ignoring this warning will only suppress the lint check; it will
|
||||
simulate correctly.
|
||||
|
||||
Faulty example:
|
||||
|
||||
.. include:: ../../docs/gen/ex_PKGNODECL_faulty.rst
|
||||
|
||||
Results in:
|
||||
|
||||
.. include:: ../../docs/gen/ex_PKGNODECL_msg.rst
|
||||
|
||||
Repaired example:
|
||||
|
||||
.. code-block:: sv
|
||||
:emphasize-lines: 2
|
||||
|
||||
sub sub (
|
||||
.port());
|
||||
|
||||
Other tools with similar warnings: Icarus Verilog's portbind, "warning:
|
||||
Instantiating module ... with dangling input port (...)". Slang's
|
||||
unconnected-port, "port '...' has no connection".
|
||||
@@ -1304,7 +1320,7 @@ List Of Warnings
|
||||
generate
|
||||
if (A==0) begin
|
||||
b b_inst1 (.x(1'b0)); //<--- error nonexistent port
|
||||
b #(.PX(1'b0)) b_inst2 (); //<--- error nonexistent parameter
|
||||
b #(.PX(1'b0)) b_inst2 (); //<--- error nonexistent parameter
|
||||
end
|
||||
endgenerate
|
||||
endmodule
|
||||
@@ -1346,11 +1362,22 @@ List Of Warnings
|
||||
|
||||
.. option:: PKGNODECL
|
||||
|
||||
.. TODO better example
|
||||
An error that a package/class appears to have been referenced that has
|
||||
not yet been declared. According to IEEE 1800-2023 26.3, all packages
|
||||
must be declared before being used.
|
||||
|
||||
An error that a package/class appears to have been referenced that has not
|
||||
yet been declared. According to IEEE 1800-2023 26.3, all packages must
|
||||
be declared before being used.
|
||||
Faulty example:
|
||||
|
||||
.. include:: ../../docs/gen/ex_PKGNODECL_faulty.rst
|
||||
|
||||
Results in:
|
||||
|
||||
.. include:: ../../docs/gen/ex_PKGNODECL_msg.rst
|
||||
|
||||
Often the package is declared in its own header file. In this case add
|
||||
an include of that package header file to the referencing file. (And
|
||||
make sure you have header guards in the package's header file to prevent
|
||||
multiple declarations of the package.)
|
||||
|
||||
|
||||
.. option:: PREPROCZERO
|
||||
@@ -1375,7 +1402,7 @@ List Of Warnings
|
||||
`endif
|
||||
|
||||
The portable way to suppress this warning is to use a define value other
|
||||
than zero to when used in a preprocessor expression.
|
||||
than zero, when it is to be used in a preprocessor expression.
|
||||
|
||||
|
||||
.. option:: PROCASSWIRE
|
||||
@@ -1897,7 +1924,8 @@ List Of Warnings
|
||||
|
||||
.. option:: UNPACKED
|
||||
|
||||
Warns that unpacked structs and unions are not supported.
|
||||
Warns that unpacked structs and unions are not supported because
|
||||
:vlopt:`--structs-packed` was used, or by up through version 5.004.
|
||||
|
||||
Ignoring this warning will make Verilator treat the structure as packed,
|
||||
which may make Verilator simulations differ from other simulators. This
|
||||
@@ -1948,14 +1976,16 @@ List Of Warnings
|
||||
|
||||
.. TODO better example
|
||||
|
||||
Warns that the specified genvar is never used/consumed.
|
||||
Warns that the specified genvar is never used/consumed. See similar
|
||||
:option:`UNUSEDSIGNAL`.
|
||||
|
||||
|
||||
.. option:: UNUSEDPARAM
|
||||
|
||||
.. TODO better example
|
||||
|
||||
Warns that the specified parameter is never used/consumed.
|
||||
Warns that the specified parameter is never used/consumed. See similar
|
||||
:option:`UNUSEDSIGNAL`.
|
||||
|
||||
|
||||
.. option:: UNUSEDSIGNAL
|
||||
@@ -2078,10 +2108,6 @@ List Of Warnings
|
||||
|
||||
.. option:: WAITCONST
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
wait(1); // Blocks forever
|
||||
|
||||
Warns that a `wait` statement awaits a constant condition, which means it
|
||||
either blocks forever or never blocks.
|
||||
|
||||
@@ -2089,6 +2115,12 @@ List Of Warnings
|
||||
something that elaborates to zero), does not warn, as it is presumed the
|
||||
code is making the intent clear.
|
||||
|
||||
Faulty example:
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
wait(1); // Blocks forever
|
||||
|
||||
|
||||
.. option:: WIDTH
|
||||
|
||||
@@ -2125,7 +2157,7 @@ List Of Warnings
|
||||
|
||||
.. include:: ../../docs/gen/ex_WIDTHEXPAND_1_faulty.rst
|
||||
|
||||
Results in a WIDTHEXPAND warning:
|
||||
Results in a :option:`WIDTHEXPAND` warning:
|
||||
|
||||
.. include:: ../../docs/gen/ex_WIDTHEXPAND_1_msg.rst
|
||||
|
||||
@@ -2136,15 +2168,18 @@ List Of Warnings
|
||||
|
||||
.. option:: WIDTHTRUNC
|
||||
|
||||
A more granular WIDTH warning, for when a value is truncated.
|
||||
A more granular :option:`WIDTH` warning, for when a value is
|
||||
truncated. See :option:`WIDTH`.
|
||||
|
||||
.. option:: WIDTHEXPAND
|
||||
|
||||
A more granular WIDTH warning, for when a value is zero expanded.
|
||||
A more granular :option:`WIDTH` warning, for when a value is zero
|
||||
expanded. See :option:`WIDTH`.
|
||||
|
||||
.. option:: WIDTHXZEXPAND
|
||||
|
||||
A more granular WIDTH warning, for when a value is X/Z expanded.
|
||||
A more granular :option:`WIDTH` warning, for when a value is X/Z
|
||||
expanded. See :option:`WIDTH`.
|
||||
|
||||
.. option:: WIDTHCONCAT
|
||||
|
||||
@@ -2193,7 +2228,7 @@ List Of Warnings
|
||||
#(parameter int MY_PARAM = 0);
|
||||
reg [7:0] data;
|
||||
always @* begin
|
||||
data = {MY_PARAM{1'b1}}; //<--- WARNING
|
||||
data = {MY_PARAM{1'b1}}; //<--- Warning
|
||||
end
|
||||
endmodule
|
||||
|
||||
@@ -2214,7 +2249,7 @@ List Of Warnings
|
||||
:emphasize-lines: 2
|
||||
|
||||
module dut
|
||||
#(parameter int MY_PARAM = 1); //<--- REPAIRED
|
||||
#(parameter int MY_PARAM = 1); //<--- REPAIRED
|
||||
reg [7:0] data;
|
||||
always @* begin
|
||||
data = {MY_PARAM{1'b1}};
|
||||
|
||||
@@ -379,6 +379,7 @@ Srini
|
||||
Srinivasan
|
||||
Stamness
|
||||
Stephane
|
||||
Sterin
|
||||
Stoddard
|
||||
Stroebel
|
||||
Strouble
|
||||
@@ -461,6 +462,7 @@ Wojciech
|
||||
Wolfel
|
||||
Wouters
|
||||
Wpedantic
|
||||
Wunused
|
||||
Wwarn
|
||||
XSim
|
||||
Xcelium
|
||||
@@ -497,6 +499,7 @@ architected
|
||||
args
|
||||
arrarys
|
||||
assertOn
|
||||
assertcontrol
|
||||
astgen
|
||||
async
|
||||
atClone
|
||||
@@ -589,6 +592,7 @@ defname
|
||||
defparam
|
||||
demangling
|
||||
dep
|
||||
deparametrized
|
||||
der
|
||||
dereference
|
||||
desassign
|
||||
@@ -701,6 +705,7 @@ genvar
|
||||
genvars
|
||||
getenv
|
||||
getline
|
||||
getter
|
||||
ggdb
|
||||
glibc
|
||||
gmake
|
||||
@@ -714,6 +719,7 @@ hh
|
||||
hier
|
||||
hierCMakeArgs
|
||||
hierMkArgs
|
||||
hierParameters
|
||||
hierVer
|
||||
hx
|
||||
hyperthreading
|
||||
@@ -845,6 +851,7 @@ parallelized
|
||||
param
|
||||
parameterization
|
||||
parameterized
|
||||
parameterized
|
||||
parameterless
|
||||
params
|
||||
parens
|
||||
@@ -891,6 +898,7 @@ prototyptes
|
||||
ps
|
||||
psprintf
|
||||
pthread
|
||||
pthreads
|
||||
ptr
|
||||
pulldown
|
||||
pulldowns
|
||||
@@ -973,7 +981,9 @@ submakes
|
||||
submodule
|
||||
submodules
|
||||
substring
|
||||
superclass
|
||||
suspendable
|
||||
suspendables
|
||||
sv
|
||||
svBitVal
|
||||
svBitVecVal
|
||||
@@ -1102,3 +1112,4 @@ zdave
|
||||
Øyvind
|
||||
Алексеевич
|
||||
Исаак
|
||||
|
||||
|
||||
@@ -130,6 +130,7 @@ void **JenkinsIns(void *base_i, const unsigned char *mem, uint32_t length, uint3
|
||||
#define FST_HDR_TIMEZERO_SIZE (8)
|
||||
#define FST_GZIO_LEN (32768)
|
||||
#define FST_HDR_FOURPACK_DUO_SIZE (4*1024*1024)
|
||||
#define FST_ZWRAPPER_HDR_SIZE (1+8+8)
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#define FST_MACOSX
|
||||
@@ -4633,9 +4634,14 @@ if(sectype == FST_BL_ZWRAPPER)
|
||||
}
|
||||
#endif
|
||||
|
||||
fstReaderFseeko(xc, xc->f, 1+8+8, SEEK_SET);
|
||||
fstReaderFseeko(xc, xc->f, FST_ZWRAPPER_HDR_SIZE, SEEK_SET);
|
||||
#ifndef __MINGW32__
|
||||
fflush(xc->f);
|
||||
#else
|
||||
/* Windows UCRT runtime library reads one byte ahead in the file
|
||||
even with buffering disabled and does not synchronise the
|
||||
file position after fseek. */
|
||||
_lseek(fileno(xc->f), FST_ZWRAPPER_HDR_SIZE, SEEK_SET);
|
||||
#endif
|
||||
|
||||
zfd = dup(fileno(xc->f));
|
||||
@@ -6185,7 +6191,9 @@ struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
|
||||
fst_off_t blkpos = 0, prev_blkpos;
|
||||
uint64_t beg_tim, end_tim, beg_tim2, end_tim2;
|
||||
int sectype;
|
||||
#ifdef FST_DEBUG
|
||||
unsigned int secnum = 0;
|
||||
#endif
|
||||
uint64_t seclen;
|
||||
uint64_t tsec_uclen = 0, tsec_clen = 0;
|
||||
uint64_t tsec_nitems;
|
||||
@@ -6277,7 +6285,9 @@ for(;;)
|
||||
}
|
||||
|
||||
blkpos += seclen;
|
||||
#ifdef FST_DEBUG
|
||||
secnum++;
|
||||
#endif
|
||||
}
|
||||
|
||||
xc->rvat_beg_tim = beg_tim;
|
||||
|
||||
+50
-2
@@ -412,6 +412,8 @@ IData VL_RAND_RESET_I(int obits) VL_MT_SAFE {
|
||||
data &= VL_MASK_I(obits);
|
||||
return data;
|
||||
}
|
||||
IData VL_RAND_RESET_ASSIGN_I(int obits) VL_MT_SAFE { return VL_RANDOM_I() & VL_MASK_I(obits); }
|
||||
|
||||
QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE {
|
||||
if (Verilated::threadContextp()->randReset() == 0) return 0;
|
||||
QData data = ~0ULL;
|
||||
@@ -421,11 +423,19 @@ QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE {
|
||||
data &= VL_MASK_Q(obits);
|
||||
return data;
|
||||
}
|
||||
|
||||
QData VL_RAND_RESET_ASSIGN_Q(int obits) VL_MT_SAFE { return VL_RANDOM_Q() & VL_MASK_Q(obits); }
|
||||
|
||||
WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
||||
for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = VL_RAND_RESET_I(32);
|
||||
outwp[VL_WORDS_I(obits) - 1] = VL_RAND_RESET_I(32) & VL_MASK_E(obits);
|
||||
return outwp;
|
||||
}
|
||||
WDataOutP VL_RAND_RESET_ASSIGN_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
||||
for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = VL_RAND_RESET_ASSIGN_I(32);
|
||||
outwp[VL_WORDS_I(obits) - 1] = VL_RAND_RESET_ASSIGN_I(32) & VL_MASK_E(obits);
|
||||
return outwp;
|
||||
}
|
||||
WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
||||
// Not inlined to speed up compilation of slowpath code
|
||||
return VL_ZERO_W(obits, outwp);
|
||||
@@ -2494,9 +2504,47 @@ VerilatedContext::Serialized::Serialized() {
|
||||
m_timeprecision = picosecond; // Initial value until overridden by _Vconfigure
|
||||
}
|
||||
|
||||
bool VerilatedContext::assertOn() const VL_MT_SAFE { return m_s.m_assertOn; }
|
||||
void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_assertOn = flag;
|
||||
// Set all assert and directive types when true, clear otherwise.
|
||||
m_s.m_assertOn = VL_MASK_I(ASSERT_ON_WIDTH) * flag;
|
||||
}
|
||||
bool VerilatedContext::assertOnGet(VerilatedAssertType_t type,
|
||||
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE {
|
||||
// Check if selected directive type bit in the assertOn is enabled for assertion type.
|
||||
// Note: it is assumed that this is checked only for one type at the time.
|
||||
|
||||
// Flag unspecified assertion types as disabled.
|
||||
if (type == 0) return false;
|
||||
|
||||
// Get index of 3-bit group guarding assertion type status.
|
||||
// Since the assertOnGet is generated __always__ for a single assert type, we assume that only
|
||||
// a single bit will be set. Thus, ceil log2 will work fine.
|
||||
VL_DEBUG_IFDEF(assert((type & (type - 1)) == 0););
|
||||
const IData typeMaskPosition = VL_CLOG2_I(type);
|
||||
|
||||
// Check if directive type bit is enabled in corresponding assertion type bits.
|
||||
return m_s.m_assertOn & (directive << (typeMaskPosition * ASSERT_DIRECTIVE_TYPE_MASK_WIDTH));
|
||||
}
|
||||
void VerilatedContext::assertOnSet(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
||||
// For each assertion type, set directive bits.
|
||||
|
||||
// Iterate through all positions of assertion type bits. If bit for this assertion type is set,
|
||||
// set directive type bits mask at this group index.
|
||||
for (int i = 0; i < std::numeric_limits<VerilatedAssertType_t>::digits; ++i) {
|
||||
if (VL_BITISSET_I(types, i))
|
||||
m_s.m_assertOn |= directives << (i * ASSERT_DIRECTIVE_TYPE_MASK_WIDTH);
|
||||
}
|
||||
}
|
||||
void VerilatedContext::assertOnClear(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
||||
// Iterate through all positions of assertion type bits. If bit for this assertion type is set,
|
||||
// clear directive type bits mask at this group index.
|
||||
for (int i = 0; i < std::numeric_limits<VerilatedAssertType_t>::digits; ++i) {
|
||||
if (VL_BITISSET_I(types, i))
|
||||
m_s.m_assertOn &= ~(directives << (i * ASSERT_DIRECTIVE_TYPE_MASK_WIDTH));
|
||||
}
|
||||
}
|
||||
void VerilatedContext::calcUnusedSigs(bool flag) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
|
||||
+88
-41
@@ -55,6 +55,7 @@
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
@@ -153,6 +154,27 @@ enum VerilatedVarFlags {
|
||||
VLVF_DPI_CLAY = (1 << 10) // DPI compatible C standard layout
|
||||
};
|
||||
|
||||
// IEEE 1800-2023 Table 20-6
|
||||
enum class VerilatedAssertType : uint8_t {
|
||||
ASSERT_TYPE_CONCURRENT = (1 << 0),
|
||||
ASSERT_TYPE_SIMPLE_IMMEDIATE = (1 << 1),
|
||||
ASSERT_TYPE_OBSERVED_DEFERRED_IMMEDIATE = (1 << 2),
|
||||
ASSERT_TYPE_FINAL_DEFERRED_IMMEDIATE = (1 << 3),
|
||||
ASSERT_TYPE_EXPECT = (1 << 4),
|
||||
ASSERT_TYPE_UNIQUE = (1 << 5),
|
||||
ASSERT_TYPE_UNIQUE0 = (1 << 6),
|
||||
ASSERT_TYPE_PRIORITY = (1 << 7),
|
||||
};
|
||||
|
||||
// IEEE 1800-2023 Table 20-7
|
||||
enum class VerilatedAssertDirectiveType : uint8_t {
|
||||
DIRECTIVE_TYPE_ASSERT = (1 << 0),
|
||||
DIRECTIVE_TYPE_COVER = (1 << 1),
|
||||
DIRECTIVE_TYPE_ASSUME = (1 << 2),
|
||||
};
|
||||
using VerilatedAssertType_t = std::underlying_type<VerilatedAssertType>::type;
|
||||
using VerilatedAssertDirectiveType_t = std::underlying_type<VerilatedAssertDirectiveType>::type;
|
||||
|
||||
//=============================================================================
|
||||
// Utility functions
|
||||
|
||||
@@ -347,6 +369,19 @@ public:
|
||||
class VerilatedContext VL_NOT_FINAL {
|
||||
friend class VerilatedContextImp;
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
// Numer of assertion directive type members. Then each of them will represented as 1-bit in a
|
||||
// mask.
|
||||
static constexpr size_t ASSERT_DIRECTIVE_TYPE_MASK_WIDTH = 3;
|
||||
// Specifies how many groups of directive type bit groups there are based on a number of
|
||||
// assertion types.
|
||||
// Note: we add one bit to store information whether Verilator's internal
|
||||
// directive types are enabled, for example `violation if`s.
|
||||
static constexpr size_t ASSERT_ON_WIDTH
|
||||
= ASSERT_DIRECTIVE_TYPE_MASK_WIDTH * std::numeric_limits<VerilatedAssertType_t>::digits
|
||||
+ 1;
|
||||
|
||||
protected:
|
||||
// TYPES
|
||||
using traceBaseModelCb_t
|
||||
@@ -360,7 +395,11 @@ protected:
|
||||
// No std::strings or pointers or will serialize badly!
|
||||
// Fast path
|
||||
uint64_t m_time = 0; // Current $time (unscaled), 0=at zero, or legacy
|
||||
bool m_assertOn = true; // Assertions are enabled
|
||||
std::atomic<uint32_t> m_assertOn{
|
||||
std::numeric_limits<uint32_t>::max()}; // Enabled assertions,
|
||||
// for each VerilatedAssertType we store
|
||||
// 3-bits, one for each directive type. Last
|
||||
// bit guards internal directive types.
|
||||
bool m_calcUnusedSigs = false; // Waves file on, need all signals calculated
|
||||
bool m_fatalOnError = true; // Fatal on $stop/non-fatal error
|
||||
bool m_fatalOnVpiError = true; // Fatal on vpi error/unsupported
|
||||
@@ -448,14 +487,23 @@ public:
|
||||
|
||||
// METHODS - User called
|
||||
|
||||
/// Enable assertions
|
||||
void assertOn(bool flag) VL_MT_SAFE;
|
||||
/// Return if assertions enabled
|
||||
bool assertOn() const VL_MT_SAFE { return m_s.m_assertOn; }
|
||||
/// Enable calculation of unused signals (for traces)
|
||||
void calcUnusedSigs(bool flag) VL_MT_SAFE;
|
||||
bool assertOn() const VL_MT_SAFE;
|
||||
/// Enable all assertion types
|
||||
void assertOn(bool flag) VL_MT_SAFE;
|
||||
/// Get enabled status for given assertion types
|
||||
bool assertOnGet(VerilatedAssertType_t type,
|
||||
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE;
|
||||
/// Set enabled status for given assertion types
|
||||
void assertOnSet(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
|
||||
/// Clear enabled status for given assertion types
|
||||
void assertOnClear(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
|
||||
/// Return if calculating of unused signals (for traces)
|
||||
bool calcUnusedSigs() const VL_MT_SAFE { return m_s.m_calcUnusedSigs; }
|
||||
/// Enable calculation of unused signals (for traces)
|
||||
void calcUnusedSigs(bool flag) VL_MT_SAFE;
|
||||
/// Record command-line arguments, for retrieval by $test$plusargs/$value$plusargs,
|
||||
/// and for parsing +verilator+ run-time arguments.
|
||||
/// This should be called before the first model is created.
|
||||
@@ -470,52 +518,51 @@ public:
|
||||
/// Return VerilatedCovContext, allocate if needed
|
||||
/// Note if get unresolved reference then likely forgot to link verilated_cov.cpp
|
||||
VerilatedCovContext* coveragep() VL_MT_SAFE;
|
||||
/// Set debug level
|
||||
/// Return debug level
|
||||
static inline int debug() VL_MT_SAFE; /// Set debug level
|
||||
/// Debug is currently global, but for forward compatibility have a per-context method
|
||||
static inline void debug(int val) VL_MT_SAFE;
|
||||
/// Return debug level
|
||||
static inline int debug() VL_MT_SAFE;
|
||||
/// Return current number of errors/assertions
|
||||
int errorCount() const VL_MT_SAFE { return m_s.m_errorCount; }
|
||||
/// Set current number of errors/assertions
|
||||
void errorCount(int val) VL_MT_SAFE;
|
||||
/// Increment current number of errors/assertions
|
||||
void errorCountInc() VL_MT_SAFE;
|
||||
/// Return current number of errors/assertions
|
||||
int errorCount() const VL_MT_SAFE { return m_s.m_errorCount; }
|
||||
/// Set number of errors/assertions before stop
|
||||
void errorLimit(int val) VL_MT_SAFE;
|
||||
/// Return number of errors/assertions before stop
|
||||
int errorLimit() const VL_MT_SAFE { return m_s.m_errorLimit; }
|
||||
/// Set to throw fatal error on $stop/non-fatal error
|
||||
void fatalOnError(bool flag) VL_MT_SAFE;
|
||||
/// Set number of errors/assertions before stop
|
||||
void errorLimit(int val) VL_MT_SAFE;
|
||||
/// Return if to throw fatal error on $stop/non-fatal
|
||||
bool fatalOnError() const VL_MT_SAFE { return m_s.m_fatalOnError; }
|
||||
/// Set to throw fatal error on VPI errors
|
||||
void fatalOnVpiError(bool flag) VL_MT_SAFE;
|
||||
/// Set to throw fatal error on $stop/non-fatal error
|
||||
void fatalOnError(bool flag) VL_MT_SAFE;
|
||||
/// Return if to throw fatal error on VPI errors
|
||||
bool fatalOnVpiError() const VL_MT_SAFE { return m_s.m_fatalOnVpiError; }
|
||||
/// Set if got a $stop or non-fatal error
|
||||
void gotError(bool flag) VL_MT_SAFE;
|
||||
/// Set to throw fatal error on VPI errors
|
||||
void fatalOnVpiError(bool flag) VL_MT_SAFE;
|
||||
/// Return if got a $stop or non-fatal error
|
||||
bool gotError() const VL_MT_SAFE { return m_s.m_gotError; }
|
||||
/// Set if got a $finish or $stop/error
|
||||
void gotFinish(bool flag) VL_MT_SAFE;
|
||||
/// Set if got a $stop or non-fatal error
|
||||
void gotError(bool flag) VL_MT_SAFE;
|
||||
/// Return if got a $finish or $stop/error
|
||||
bool gotFinish() const VL_MT_SAFE { return m_s.m_gotFinish; }
|
||||
/// Enable quiet (also prevents need for OS calls to get CPU time)
|
||||
void quiet(bool flag) VL_MT_SAFE;
|
||||
/// Set if got a $finish or $stop/error
|
||||
void gotFinish(bool flag) VL_MT_SAFE;
|
||||
/// Return if quiet enabled
|
||||
bool quiet() const VL_MT_SAFE { return m_s.m_quiet; }
|
||||
/// Enable quiet (also prevents need for OS calls to get CPU time)
|
||||
void quiet(bool flag) VL_MT_SAFE;
|
||||
/// Return randReset value
|
||||
int randReset() VL_MT_SAFE { return m_s.m_randReset; }
|
||||
/// Select initial value of otherwise uninitialized signals.
|
||||
/// 0 = Set to zeros
|
||||
/// 1 = Set all bits to one
|
||||
/// 2 = Randomize all bits
|
||||
void randReset(int val) VL_MT_SAFE;
|
||||
/// Return randReset value
|
||||
int randReset() VL_MT_SAFE { return m_s.m_randReset; }
|
||||
/// Return default random seed
|
||||
void randSeed(int val) VL_MT_SAFE;
|
||||
/// Set default random seed, 0 = seed it automatically
|
||||
int randSeed() const VL_MT_SAFE { return m_s.m_randSeed; }
|
||||
/// Return default random seed
|
||||
void randSeed(int val) VL_MT_SAFE;
|
||||
|
||||
/// Return statistic: CPU time delta from model created until now
|
||||
double statCpuTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
@@ -607,27 +654,27 @@ public:
|
||||
enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&));
|
||||
|
||||
// Internal: coverage
|
||||
void coverageFilename(const std::string& flag) VL_MT_SAFE;
|
||||
std::string coverageFilename() const VL_MT_SAFE;
|
||||
void coverageFilename(const std::string& flag) VL_MT_SAFE;
|
||||
|
||||
// Internal: $dumpfile
|
||||
void dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
|
||||
std::string dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
|
||||
void dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
|
||||
std::string dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
|
||||
|
||||
// Internal: --prof-exec related settings
|
||||
void profExecStart(uint64_t flag) VL_MT_SAFE;
|
||||
uint64_t profExecStart() const VL_MT_SAFE { return m_ns.m_profExecStart; }
|
||||
void profExecWindow(uint64_t flag) VL_MT_SAFE;
|
||||
void profExecStart(uint64_t flag) VL_MT_SAFE;
|
||||
uint32_t profExecWindow() const VL_MT_SAFE { return m_ns.m_profExecWindow; }
|
||||
void profExecFilename(const std::string& flag) VL_MT_SAFE;
|
||||
void profExecWindow(uint64_t flag) VL_MT_SAFE;
|
||||
std::string profExecFilename() const VL_MT_SAFE;
|
||||
void profVltFilename(const std::string& flag) VL_MT_SAFE;
|
||||
void profExecFilename(const std::string& flag) VL_MT_SAFE;
|
||||
std::string profVltFilename() const VL_MT_SAFE;
|
||||
void profVltFilename(const std::string& flag) VL_MT_SAFE;
|
||||
|
||||
// Internal: SMT solver program
|
||||
void solverProgram(const std::string& flag) VL_MT_SAFE;
|
||||
std::string solverProgram() const VL_MT_SAFE;
|
||||
void solverProgram(const std::string& flag) VL_MT_SAFE;
|
||||
|
||||
// Internal: Find scope
|
||||
const VerilatedScope* scopeFind(const char* namep) const VL_MT_SAFE;
|
||||
@@ -804,18 +851,18 @@ public:
|
||||
}
|
||||
|
||||
#ifndef VL_NO_LEGACY
|
||||
/// Call VerilatedContext::assertOn using current thread's VerilatedContext
|
||||
static void assertOn(bool flag) VL_MT_SAFE { Verilated::threadContextp()->assertOn(flag); }
|
||||
/// Return VerilatedContext::assertOn() using current thread's VerilatedContext
|
||||
static bool assertOn() VL_MT_SAFE { return Verilated::threadContextp()->assertOn(); }
|
||||
/// Call VerilatedContext::calcUnusedSigs using current thread's VerilatedContext
|
||||
static void calcUnusedSigs(bool flag) VL_MT_SAFE {
|
||||
Verilated::threadContextp()->calcUnusedSigs(flag);
|
||||
}
|
||||
/// Call VerilatedContext::assertOn using current thread's VerilatedContext
|
||||
static void assertOn(bool flag) VL_MT_SAFE { Verilated::threadContextp()->assertOn(flag); }
|
||||
/// Return VerilatedContext::calcUnusedSigs using current thread's VerilatedContext
|
||||
static bool calcUnusedSigs() VL_MT_SAFE {
|
||||
return Verilated::threadContextp()->calcUnusedSigs();
|
||||
}
|
||||
/// Call VerilatedContext::calcUnusedSigs using current thread's VerilatedContext
|
||||
static void calcUnusedSigs(bool flag) VL_MT_SAFE {
|
||||
Verilated::threadContextp()->calcUnusedSigs(flag);
|
||||
}
|
||||
/// Call VerilatedContext::commandArgs using current thread's VerilatedContext
|
||||
static void commandArgs(int argc, const char** argv) VL_MT_SAFE {
|
||||
Verilated::threadContextp()->commandArgs(argc, argv);
|
||||
@@ -958,8 +1005,8 @@ public:
|
||||
|
||||
// Internal: Set the mtaskId, called when an mtask starts
|
||||
// Per thread, so no need to be in VerilatedContext
|
||||
static void mtaskId(uint32_t id) VL_MT_SAFE { t_s.t_mtaskId = id; }
|
||||
static uint32_t mtaskId() VL_MT_SAFE { return t_s.t_mtaskId; }
|
||||
static void mtaskId(uint32_t id) VL_MT_SAFE { t_s.t_mtaskId = id; }
|
||||
static void endOfEvalReqdInc() VL_MT_SAFE { ++t_s.t_endOfEvalReqd; }
|
||||
static void endOfEvalReqdDec() VL_MT_SAFE { --t_s.t_endOfEvalReqd; }
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ VK_CPPFLAGS_ALWAYS += \
|
||||
-I$(VERILATOR_ROOT)/include/vltstd \
|
||||
-DVM_COVERAGE=$(VM_COVERAGE) \
|
||||
-DVM_SC=$(VM_SC) \
|
||||
-DVM_TIMING=$(VM_TIMING) \
|
||||
-DVM_TRACE=$(VM_TRACE) \
|
||||
-DVM_TRACE_FST=$(VM_TRACE_FST) \
|
||||
-DVM_TRACE_VCD=$(VM_TRACE_VCD) \
|
||||
|
||||
@@ -92,9 +92,9 @@ public:
|
||||
std::string defaultFilename() VL_MT_SAFE;
|
||||
/// Make all data per_instance, overriding point's per_instance
|
||||
void forcePerInstance(bool flag) VL_MT_SAFE;
|
||||
void write() VL_MT_SAFE { write(defaultFilename()); }
|
||||
/// Write all coverage data to a file
|
||||
void write(const std::string& filename) VL_MT_SAFE;
|
||||
void write() VL_MT_SAFE { write(defaultFilename()); }
|
||||
/// Clear coverage points (and call delete on all items)
|
||||
void clear() VL_MT_SAFE;
|
||||
/// Clear items not matching the provided string
|
||||
|
||||
+334
-26
@@ -106,6 +106,14 @@ extern IData VL_RAND_RESET_I(int obits) VL_MT_SAFE;
|
||||
extern QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE;
|
||||
/// Random reset a signal of given width (init time only)
|
||||
extern WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE;
|
||||
|
||||
/// Random reset a signal of given width (assign time only)
|
||||
extern IData VL_RAND_RESET_ASSIGN_I(int obits) VL_MT_SAFE;
|
||||
/// Random reset a signal of given width (assign time only)
|
||||
extern QData VL_RAND_RESET_ASSIGN_Q(int obits) VL_MT_SAFE;
|
||||
/// Random reset a signal of given width (assign time only)
|
||||
extern WDataOutP VL_RAND_RESET_ASSIGN_W(int obits, WDataOutP outwp) VL_MT_SAFE;
|
||||
|
||||
/// Zero reset a signal (slow - else use VL_ZERO_W)
|
||||
extern WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE;
|
||||
|
||||
@@ -1577,49 +1585,184 @@ static inline WDataOutP VL_STREAML_WWI(int lbits, WDataOutP owp, WDataInP const
|
||||
return owp;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void VL_ASSIGN_DYN_Q(VlQueue<T>& q, int elem_size, int lbits, QData from) {
|
||||
const int size = (lbits + elem_size - 1) / elem_size;
|
||||
q.renew(size);
|
||||
const QData mask = VL_MASK_Q(elem_size);
|
||||
for (int i = 0; i < size; ++i) q.at(i) = (T)((from >> (i * elem_size)) & mask);
|
||||
static inline IData VL_PACK_II(int obits, int lbits, const VlQueue<CData>& q) {
|
||||
IData ret = 0;
|
||||
for (size_t i = 0; i < q.size(); ++i) ret |= static_cast<IData>(q.at(i)) << (i * lbits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t T_Depth>
|
||||
static inline void VL_ASSIGN_UNPACK_Q(VlUnpacked<T, T_Depth>& q, size_t elem_size, QData from) {
|
||||
const QData mask = VL_MASK_Q(elem_size);
|
||||
static inline IData VL_PACK_II(int obits, int lbits, const VlQueue<SData>& q) {
|
||||
IData ret = 0;
|
||||
for (size_t i = 0; i < q.size(); ++i) ret |= static_cast<IData>(q.at(i)) << (i * lbits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline IData VL_PACK_II(int obits, int lbits, const VlQueue<IData>& q) {
|
||||
IData ret = 0;
|
||||
for (size_t i = 0; i < q.size(); ++i) ret |= q.at(i) << (i * lbits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <std::size_t T_Depth>
|
||||
static inline IData VL_PACK_II(int obits, int lbits, const VlUnpacked<CData, T_Depth>& q) {
|
||||
IData ret = 0;
|
||||
for (size_t i = 0; i < T_Depth; ++i)
|
||||
q[i] = (T)((from >> ((T_Depth - 1 - i) * elem_size)) & mask);
|
||||
ret |= static_cast<IData>(q[T_Depth - 1 - i]) << (i * lbits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline IData VL_DYN_TO_I(const VlQueue<T>& q, int elem_size) {
|
||||
template <std::size_t T_Depth>
|
||||
static inline IData VL_PACK_II(int obits, int lbits, const VlUnpacked<SData, T_Depth>& q) {
|
||||
IData ret = 0;
|
||||
for (int i = 0; i < q.size(); ++i) ret |= q.at(i) << (i * elem_size);
|
||||
for (size_t i = 0; i < T_Depth; ++i)
|
||||
ret |= static_cast<IData>(q[T_Depth - 1 - i]) << (i * lbits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline QData VL_DYN_TO_Q(const VlQueue<T>& q, int elem_size) {
|
||||
QData ret = 0;
|
||||
for (int i = 0; i < q.size(); ++i) ret |= q.at(i) << (i * elem_size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t T_Depth>
|
||||
static inline IData VL_UNPACK_TO_I(const VlUnpacked<T, T_Depth>& q, size_t elem_size) {
|
||||
template <std::size_t T_Depth>
|
||||
static inline IData VL_PACK_II(int obits, int lbits, const VlUnpacked<IData, T_Depth>& q) {
|
||||
IData ret = 0;
|
||||
for (size_t i = 0; i < T_Depth; ++i) ret |= q[T_Depth - 1 - i] << (i * elem_size);
|
||||
for (size_t i = 0; i < T_Depth; ++i) ret |= q[T_Depth - 1 - i] << (i * lbits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t T_Depth>
|
||||
static inline QData VL_UNPACK_TO_Q(const VlUnpacked<T, T_Depth>& q, size_t elem_size) {
|
||||
static inline QData VL_PACK_QI(int obits, int lbits, const VlQueue<CData>& q) {
|
||||
QData ret = 0;
|
||||
for (size_t i = 0; i < T_Depth; ++i) ret |= q[T_Depth - 1 - i] << (i * elem_size);
|
||||
for (size_t i = 0; i < q.size(); ++i) ret |= static_cast<QData>(q.at(i)) << (i * lbits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline QData VL_PACK_QI(int obits, int lbits, const VlQueue<SData>& q) {
|
||||
QData ret = 0;
|
||||
for (size_t i = 0; i < q.size(); ++i) ret |= static_cast<QData>(q.at(i)) << (i * lbits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline QData VL_PACK_QI(int obits, int lbits, const VlQueue<IData>& q) {
|
||||
QData ret = 0;
|
||||
for (size_t i = 0; i < q.size(); ++i) ret |= static_cast<QData>(q.at(i)) << (i * lbits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <std::size_t T_Depth>
|
||||
static inline QData VL_PACK_QI(int obits, int lbits, const VlUnpacked<CData, T_Depth>& q) {
|
||||
QData ret = 0;
|
||||
for (size_t i = 0; i < T_Depth; ++i)
|
||||
ret |= static_cast<QData>(q[T_Depth - 1 - i]) << (i * lbits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <std::size_t T_Depth>
|
||||
static inline QData VL_PACK_QI(int obits, int lbits, const VlUnpacked<SData, T_Depth>& q) {
|
||||
QData ret = 0;
|
||||
for (size_t i = 0; i < T_Depth; ++i)
|
||||
ret |= static_cast<QData>(q[T_Depth - 1 - i]) << (i * lbits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <std::size_t T_Depth>
|
||||
static inline QData VL_PACK_QI(int obits, int lbits, const VlUnpacked<IData, T_Depth>& q) {
|
||||
QData ret = 0;
|
||||
for (size_t i = 0; i < T_Depth; ++i)
|
||||
ret |= static_cast<QData>(q[T_Depth - 1 - i]) << (i * lbits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline QData VL_PACK_QQ(int obits, int lbits, const VlQueue<QData>& q) {
|
||||
QData ret = 0;
|
||||
for (size_t i = 0; i < q.size(); ++i) ret |= q.at(i) << (i * lbits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <std::size_t T_Depth>
|
||||
static inline QData VL_PACK_QQ(int obits, int lbits, const VlUnpacked<QData, T_Depth>& q) {
|
||||
QData ret = 0;
|
||||
for (size_t i = 0; i < T_Depth; ++i) ret |= q[T_Depth - 1 - i] << (i * lbits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline WDataOutP VL_PACK_WI(int obits, int lbits, WDataOutP owp, const VlQueue<CData>& q) {
|
||||
VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
|
||||
for (size_t i = 0; i < q.size(); ++i)
|
||||
_vl_insert_WI(owp, q.at(i), i * lbits + lbits - 1, i * lbits);
|
||||
return owp;
|
||||
}
|
||||
|
||||
static inline WDataOutP VL_PACK_WI(int obits, int lbits, WDataOutP owp, const VlQueue<SData>& q) {
|
||||
VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
|
||||
for (size_t i = 0; i < q.size(); ++i)
|
||||
_vl_insert_WI(owp, q.at(i), i * lbits + lbits - 1, i * lbits);
|
||||
return owp;
|
||||
}
|
||||
|
||||
static inline WDataOutP VL_PACK_WI(int obits, int lbits, WDataOutP owp, const VlQueue<IData>& q) {
|
||||
VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
|
||||
for (size_t i = 0; i < q.size(); ++i)
|
||||
_vl_insert_WI(owp, q.at(i), i * lbits + lbits - 1, i * lbits);
|
||||
return owp;
|
||||
}
|
||||
|
||||
template <std::size_t T_Depth>
|
||||
static inline WDataOutP VL_PACK_WI(int obits, int lbits, WDataOutP owp,
|
||||
const VlUnpacked<CData, T_Depth>& q) {
|
||||
VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
|
||||
for (size_t i = 0; i < T_Depth; ++i)
|
||||
_vl_insert_WI(owp, q[T_Depth - 1 - i], i * lbits + lbits - 1, i * lbits);
|
||||
return owp;
|
||||
}
|
||||
|
||||
template <std::size_t T_Depth>
|
||||
static inline WDataOutP VL_PACK_WI(int obits, int lbits, WDataOutP owp,
|
||||
const VlUnpacked<SData, T_Depth>& q) {
|
||||
VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
|
||||
for (size_t i = 0; i < T_Depth; ++i)
|
||||
_vl_insert_WI(owp, q[T_Depth - 1 - i], i * lbits + lbits - 1, i * lbits);
|
||||
return owp;
|
||||
}
|
||||
|
||||
template <std::size_t T_Depth>
|
||||
static inline WDataOutP VL_PACK_WI(int obits, int lbits, WDataOutP owp,
|
||||
const VlUnpacked<IData, T_Depth>& q) {
|
||||
VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
|
||||
for (size_t i = 0; i < T_Depth; ++i)
|
||||
_vl_insert_WI(owp, q[T_Depth - 1 - i], i * lbits + lbits - 1, i * lbits);
|
||||
return owp;
|
||||
}
|
||||
|
||||
static inline WDataOutP VL_PACK_WQ(int obits, int lbits, WDataOutP owp, const VlQueue<QData>& q) {
|
||||
VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
|
||||
for (size_t i = 0; i < q.size(); ++i)
|
||||
_vl_insert_WQ(owp, q.at(i), i * lbits + lbits - 1, i * lbits);
|
||||
return owp;
|
||||
}
|
||||
|
||||
template <std::size_t T_Depth>
|
||||
static inline WDataOutP VL_PACK_WQ(int obits, int lbits, WDataOutP owp,
|
||||
const VlUnpacked<QData, T_Depth>& q) {
|
||||
VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
|
||||
for (size_t i = 0; i < T_Depth; ++i)
|
||||
_vl_insert_WQ(owp, q[T_Depth - 1 - i], i * lbits + lbits - 1, i * lbits);
|
||||
return owp;
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
static inline WDataOutP VL_PACK_WW(int obits, int lbits, WDataOutP owp,
|
||||
const VlQueue<VlWide<N>>& q) {
|
||||
VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
|
||||
for (size_t i = 0; i < q.size(); ++i)
|
||||
_vl_insert_WW(owp, q.at(i), i * lbits + lbits - 1, i * lbits);
|
||||
return owp;
|
||||
}
|
||||
|
||||
template <std::size_t T_Depth, std::size_t N>
|
||||
static inline WDataOutP VL_PACK_WW(int obits, int lbits, WDataOutP owp,
|
||||
const VlUnpacked<VlWide<N>, T_Depth>& q) {
|
||||
VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
|
||||
for (size_t i = 0; i < T_Depth; ++i)
|
||||
_vl_insert_WW(owp, q[T_Depth - 1 - i], i * lbits + lbits - 1, i * lbits);
|
||||
return owp;
|
||||
}
|
||||
|
||||
// Because concats are common and wide, it's valuable to always have a clean output.
|
||||
// Thus we specify inputs must be clean, so we don't need to clean the output.
|
||||
// Note the bit shifts are always constants, so the adds in these constify out.
|
||||
@@ -2059,6 +2202,171 @@ static inline WDataOutP VL_SEL_WWII(int obits, int lbits, WDataOutP owp, WDataIn
|
||||
//======================================================================
|
||||
// Expressions needing insert/select
|
||||
|
||||
static inline void VL_UNPACK_II(int lbits, int rbits, VlQueue<CData>& q, IData from) {
|
||||
const size_t size = (rbits + lbits - 1) / lbits;
|
||||
q.renew(size);
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < size; ++i) q.at(i) = (from >> (i * lbits)) & mask;
|
||||
}
|
||||
|
||||
static inline void VL_UNPACK_II(int lbits, int rbits, VlQueue<SData>& q, IData from) {
|
||||
const size_t size = (rbits + lbits - 1) / lbits;
|
||||
q.renew(size);
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < size; ++i) q.at(i) = (from >> (i * lbits)) & mask;
|
||||
}
|
||||
|
||||
static inline void VL_UNPACK_II(int lbits, int rbits, VlQueue<IData>& q, IData from) {
|
||||
const size_t size = (rbits + lbits - 1) / lbits;
|
||||
q.renew(size);
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < size; ++i) q.at(i) = (from >> (i * lbits)) & mask;
|
||||
}
|
||||
|
||||
static inline void VL_UNPACK_IQ(int lbits, int rbits, VlQueue<CData>& q, QData from) {
|
||||
const size_t size = (rbits + lbits - 1) / lbits;
|
||||
q.renew(size);
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < size; ++i) q.at(i) = (from >> (i * lbits)) & mask;
|
||||
}
|
||||
|
||||
static inline void VL_UNPACK_IQ(int lbits, int rbits, VlQueue<SData>& q, QData from) {
|
||||
const size_t size = (rbits + lbits - 1) / lbits;
|
||||
q.renew(size);
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < size; ++i) q.at(i) = (from >> (i * lbits)) & mask;
|
||||
}
|
||||
|
||||
static inline void VL_UNPACK_IQ(int lbits, int rbits, VlQueue<IData>& q, QData from) {
|
||||
const size_t size = (rbits + lbits - 1) / lbits;
|
||||
q.renew(size);
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < size; ++i) q.at(i) = (from >> (i * lbits)) & mask;
|
||||
}
|
||||
|
||||
static inline void VL_UNPACK_QQ(int lbits, int rbits, VlQueue<QData>& q, QData from) {
|
||||
const size_t size = (rbits + lbits - 1) / lbits;
|
||||
q.renew(size);
|
||||
const QData mask = VL_MASK_Q(lbits);
|
||||
for (size_t i = 0; i < size; ++i) q.at(i) = (from >> (i * lbits)) & mask;
|
||||
}
|
||||
|
||||
static inline void VL_UNPACK_IW(int lbits, int rbits, VlQueue<CData>& q, WDataInP rwp) {
|
||||
const int size = (rbits + lbits - 1) / lbits;
|
||||
q.renew(size);
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < size; ++i) q.at(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask;
|
||||
}
|
||||
|
||||
static inline void VL_UNPACK_IW(int lbits, int rbits, VlQueue<SData>& q, WDataInP rwp) {
|
||||
const int size = (rbits + lbits - 1) / lbits;
|
||||
q.renew(size);
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < size; ++i) q.at(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask;
|
||||
}
|
||||
|
||||
static inline void VL_UNPACK_IW(int lbits, int rbits, VlQueue<IData>& q, WDataInP rwp) {
|
||||
const int size = (rbits + lbits - 1) / lbits;
|
||||
q.renew(size);
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < size; ++i) q.at(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask;
|
||||
}
|
||||
|
||||
static inline void VL_UNPACK_QW(int lbits, int rbits, VlQueue<QData>& q, WDataInP rwp) {
|
||||
const int size = (rbits + lbits - 1) / lbits;
|
||||
q.renew(size);
|
||||
const QData mask = VL_MASK_Q(lbits);
|
||||
for (size_t i = 0; i < size; ++i) q.at(i) = VL_SEL_QWII(rbits, rwp, i * lbits, lbits) & mask;
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
static inline void VL_UNPACK_WW(int lbits, int rbits, VlQueue<VlWide<N>>& q, WDataInP rwp) {
|
||||
const int size = (rbits + lbits - 1) / lbits;
|
||||
q.renew(size);
|
||||
for (size_t i = 0; i < size; ++i) VL_SEL_WWII(lbits, rbits, q.at(i), rwp, i * lbits, lbits);
|
||||
}
|
||||
|
||||
template <std::size_t T_Depth>
|
||||
static inline void VL_UNPACK_II(int lbits, int rbits, VlUnpacked<CData, T_Depth>& q, IData from) {
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < T_Depth; ++i) q[i] = (from >> ((T_Depth - 1 - i) * lbits)) & mask;
|
||||
}
|
||||
|
||||
template <std::size_t T_Depth>
|
||||
static inline void VL_UNPACK_II(int lbits, int rbits, VlUnpacked<SData, T_Depth>& q, IData from) {
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < T_Depth; ++i) q[i] = (from >> ((T_Depth - 1 - i) * lbits)) & mask;
|
||||
}
|
||||
|
||||
template <std::size_t T_Depth>
|
||||
static inline void VL_UNPACK_II(int lbits, int rbits, VlUnpacked<IData, T_Depth>& q, IData from) {
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < T_Depth; ++i) q[i] = (from >> ((T_Depth - 1 - i) * lbits)) & mask;
|
||||
}
|
||||
|
||||
template <std::size_t T_Depth>
|
||||
static inline void VL_UNPACK_IQ(int lbits, int rbits, VlUnpacked<CData, T_Depth>& q, QData from) {
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < T_Depth; ++i) q[i] = (from >> ((T_Depth - 1 - i) * lbits)) & mask;
|
||||
}
|
||||
|
||||
template <std::size_t T_Depth>
|
||||
static inline void VL_UNPACK_IQ(int lbits, int rbits, VlUnpacked<SData, T_Depth>& q, QData from) {
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < T_Depth; ++i) q[i] = (from >> ((T_Depth - 1 - i) * lbits)) & mask;
|
||||
}
|
||||
|
||||
template <std::size_t T_Depth>
|
||||
static inline void VL_UNPACK_IQ(int lbits, int rbits, VlUnpacked<IData, T_Depth>& q, QData from) {
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < T_Depth; ++i) q[i] = (from >> ((T_Depth - 1 - i) * lbits)) & mask;
|
||||
}
|
||||
|
||||
template <std::size_t T_Depth>
|
||||
static inline void VL_UNPACK_QQ(int lbits, int rbits, VlUnpacked<QData, T_Depth>& q, QData from) {
|
||||
const QData mask = VL_MASK_Q(lbits);
|
||||
for (size_t i = 0; i < T_Depth; ++i) q[i] = (from >> ((T_Depth - 1 - i) * lbits)) & mask;
|
||||
}
|
||||
|
||||
template <std::size_t T_Depth>
|
||||
static inline void VL_UNPACK_IW(int lbits, int rbits, VlUnpacked<CData, T_Depth>& q,
|
||||
WDataInP rwp) {
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < T_Depth; ++i)
|
||||
q[i] = VL_SEL_IWII(rbits, rwp, (T_Depth - 1 - i) * lbits, lbits) & mask;
|
||||
}
|
||||
|
||||
template <std::size_t T_Depth>
|
||||
static inline void VL_UNPACK_IW(int lbits, int rbits, VlUnpacked<SData, T_Depth>& q,
|
||||
WDataInP rwp) {
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < T_Depth; ++i)
|
||||
q[i] = VL_SEL_IWII(rbits, rwp, (T_Depth - 1 - i) * lbits, lbits) & mask;
|
||||
}
|
||||
|
||||
template <std::size_t T_Depth>
|
||||
static inline void VL_UNPACK_IW(int lbits, int rbits, VlUnpacked<IData, T_Depth>& q,
|
||||
WDataInP rwp) {
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < T_Depth; ++i)
|
||||
q[i] = VL_SEL_IWII(rbits, rwp, (T_Depth - 1 - i) * lbits, lbits) & mask;
|
||||
}
|
||||
|
||||
template <std::size_t T_Depth>
|
||||
static inline void VL_UNPACK_QW(int lbits, int rbits, VlUnpacked<QData, T_Depth>& q,
|
||||
WDataInP rwp) {
|
||||
const QData mask = VL_MASK_Q(lbits);
|
||||
for (size_t i = 0; i < T_Depth; ++i)
|
||||
q[i] = VL_SEL_QWII(rbits, rwp, (T_Depth - 1 - i) * lbits, lbits) & mask;
|
||||
}
|
||||
|
||||
template <std::size_t T_Depth, std::size_t N>
|
||||
static inline void VL_UNPACK_WW(int lbits, int rbits, VlUnpacked<VlWide<N>, T_Depth>& q,
|
||||
WDataInP rwp) {
|
||||
for (size_t i = 0; i < T_Depth; ++i)
|
||||
VL_SEL_WWII(lbits, rbits, q[i], rwp, (T_Depth - 1 - i) * lbits, lbits);
|
||||
}
|
||||
|
||||
// Return QData from double (numeric)
|
||||
// EMIT_RULE: VL_RTOIROUND_Q_D: oclean=dirty; lclean==clean/real
|
||||
static inline QData VL_RTOIROUND_Q_D(double lhs) VL_PURE {
|
||||
|
||||
@@ -403,8 +403,12 @@ bool VlRandomizer::parseSolution(std::iostream& f) {
|
||||
|
||||
auto it = m_vars.find(name);
|
||||
if (it == m_vars.end()) continue;
|
||||
const VlRandomVar& varr = *it->second;
|
||||
if (m_randmode && !varr.randModeIdxNone()) {
|
||||
if (!(m_randmode->at(varr.randModeIdx()))) continue;
|
||||
}
|
||||
|
||||
it->second->set(std::move(value));
|
||||
varr.set(std::move(value));
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -414,6 +418,8 @@ void VlRandomizer::hard(std::string&& constraint) {
|
||||
m_constraints.emplace_back(std::move(constraint));
|
||||
}
|
||||
|
||||
void VlRandomizer::clear() { m_constraints.clear(); }
|
||||
|
||||
#ifdef VL_DEBUG
|
||||
void VlRandomizer::dump() const {
|
||||
for (const auto& var : m_vars) {
|
||||
|
||||
@@ -38,15 +38,19 @@ class VlRandomVar final : public VlRandomExpr {
|
||||
const char* const m_name; // Variable name
|
||||
void* const m_datap; // Reference to variable data
|
||||
const int m_width; // Variable width in bits
|
||||
const std::uint32_t m_randModeIdx; // rand_mode index
|
||||
|
||||
public:
|
||||
VlRandomVar(const char* name, int width, void* datap)
|
||||
VlRandomVar(const char* name, int width, void* datap, std::uint32_t randModeIdx)
|
||||
: m_name{name}
|
||||
, m_datap{datap}
|
||||
, m_width{width} {}
|
||||
, m_width{width}
|
||||
, m_randModeIdx{randModeIdx} {}
|
||||
const char* name() const { return m_name; }
|
||||
int width() const { return m_width; }
|
||||
void* datap() const { return m_datap; }
|
||||
std::uint32_t randModeIdx() const { return m_randModeIdx; }
|
||||
bool randModeIdxNone() const { return randModeIdx() == std::numeric_limits<unsigned>::max(); }
|
||||
bool set(std::string&&) const;
|
||||
void emit(std::ostream& s) const override;
|
||||
};
|
||||
@@ -94,8 +98,9 @@ public:
|
||||
class VlRandomizer final {
|
||||
// MEMBERS
|
||||
std::vector<std::string> m_constraints; // Solver-dependent constraints
|
||||
std::map<std::string, std::shared_ptr<const VlRandomVar>>
|
||||
m_vars; // Solver-dependent variables
|
||||
std::map<std::string, std::shared_ptr<const VlRandomVar>> m_vars; // Solver-dependent
|
||||
// variables
|
||||
const VlQueue<CData>* m_randmode; // rand_mode state;
|
||||
|
||||
// PRIVATE METHODS
|
||||
std::shared_ptr<const VlRandomExpr> randomConstraint(VlRNG& rngr, int bits);
|
||||
@@ -106,12 +111,15 @@ public:
|
||||
// Finds the next solution satisfying the constraints
|
||||
bool next(VlRNG& rngr);
|
||||
template <typename T>
|
||||
void write_var(T& var, int width, const char* name) {
|
||||
void write_var(T& var, int width, const char* name,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
auto it = m_vars.find(name);
|
||||
if (it != m_vars.end()) return;
|
||||
m_vars[name] = std::make_shared<const VlRandomVar>(name, width, &var);
|
||||
m_vars[name] = std::make_shared<const VlRandomVar>(name, width, &var, randmodeIdx);
|
||||
}
|
||||
void hard(std::string&& constraint);
|
||||
void clear();
|
||||
void set_randmode(const VlQueue<CData>& randmode) { m_randmode = &randmode; }
|
||||
#ifdef VL_DEBUG
|
||||
void dump() const;
|
||||
#endif
|
||||
|
||||
@@ -95,8 +95,7 @@ void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE {
|
||||
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_HEADER_STR, std::strlen(VLTSAVE_HEADER_STR)))) {
|
||||
const std::string fn = filename();
|
||||
const std::string msg
|
||||
= std::
|
||||
string{"Can't deserialize; file has wrong header signature, or file not found: "}
|
||||
= "Can't deserialize; file has wrong header signature, or file not found: "s
|
||||
+ filename();
|
||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||
// Die before we close() as close would infinite loop
|
||||
|
||||
@@ -40,7 +40,7 @@ package std;
|
||||
|
||||
task put(T message);
|
||||
`ifdef VERILATOR_TIMING
|
||||
if (m_bound != 0)
|
||||
while (m_bound != 0 && m_queue.size() >= m_bound)
|
||||
wait (m_queue.size() < m_bound);
|
||||
m_queue.push_back(message);
|
||||
`endif
|
||||
@@ -56,7 +56,9 @@ package std;
|
||||
|
||||
task get(ref T message);
|
||||
`ifdef VERILATOR_TIMING
|
||||
wait (m_queue.size() > 0);
|
||||
while (m_queue.size() == 0) begin
|
||||
wait (m_queue.size() > 0);
|
||||
end
|
||||
message = m_queue.pop_front();
|
||||
`endif
|
||||
endtask
|
||||
@@ -71,7 +73,9 @@ package std;
|
||||
|
||||
task peek(ref T message);
|
||||
`ifdef VERILATOR_TIMING
|
||||
wait (m_queue.size() > 0);
|
||||
while (m_queue.size() == 0) begin
|
||||
wait (m_queue.size() > 0);
|
||||
end
|
||||
message = m_queue[0];
|
||||
`endif
|
||||
endtask
|
||||
@@ -98,7 +102,9 @@ package std;
|
||||
|
||||
task get(int keyCount = 1);
|
||||
`ifdef VERILATOR_TIMING
|
||||
wait (m_keyCount >= keyCount);
|
||||
while (m_keyCount < keyCount) begin
|
||||
wait (m_keyCount >= keyCount);
|
||||
end
|
||||
m_keyCount -= keyCount;
|
||||
`endif
|
||||
endtask
|
||||
@@ -189,4 +195,7 @@ package std;
|
||||
$urandom(s.atoi()); // Set the seed using a string
|
||||
endfunction
|
||||
endclass
|
||||
function int randomize();
|
||||
randomize = 0;
|
||||
endfunction
|
||||
endpackage
|
||||
|
||||
+36
-31
@@ -512,28 +512,29 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
static VlQueue cons(const T_Value& lhs) {
|
||||
// Construct new object from _V_alue and/or _C_ontainer child objects
|
||||
static VlQueue consV(const T_Value& lhs) {
|
||||
VlQueue out;
|
||||
out.push_back(lhs);
|
||||
return out;
|
||||
}
|
||||
static VlQueue cons(const T_Value& lhs, const T_Value& rhs) {
|
||||
static VlQueue consVV(const T_Value& lhs, const T_Value& rhs) {
|
||||
VlQueue out;
|
||||
out.push_back(rhs);
|
||||
out.push_back(lhs);
|
||||
return out;
|
||||
}
|
||||
static VlQueue cons(const VlQueue& lhs, const T_Value& rhs) {
|
||||
static VlQueue consCV(const VlQueue& lhs, const T_Value& rhs) {
|
||||
VlQueue out = lhs;
|
||||
out.push_front(rhs);
|
||||
return out;
|
||||
}
|
||||
static VlQueue cons(const T_Value& lhs, const VlQueue& rhs) {
|
||||
static VlQueue consVC(const T_Value& lhs, const VlQueue& rhs) {
|
||||
VlQueue out = rhs;
|
||||
out.push_back(lhs);
|
||||
return out;
|
||||
}
|
||||
static VlQueue cons(const VlQueue& lhs, const VlQueue& rhs) {
|
||||
static VlQueue consCC(const VlQueue& lhs, const VlQueue& rhs) {
|
||||
VlQueue out = rhs;
|
||||
for (const auto& i : lhs.m_deque) out.push_back(i);
|
||||
return out;
|
||||
@@ -751,7 +752,7 @@ public:
|
||||
// Can't use std::find_if as need index number
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
if (with_func(index, i)) return VlQueue::cons(i);
|
||||
if (with_func(index, i)) return VlQueue::consV(i);
|
||||
++index;
|
||||
}
|
||||
return VlQueue{};
|
||||
@@ -760,7 +761,7 @@ public:
|
||||
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);
|
||||
if (with_func(index, i)) return VlQueue<IData>::consV(index);
|
||||
++index;
|
||||
}
|
||||
return VlQueue<IData>{};
|
||||
@@ -769,7 +770,7 @@ public:
|
||||
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);
|
||||
if (with_func(index, item)) return VlQueue::consV(item);
|
||||
--index;
|
||||
}
|
||||
return VlQueue{};
|
||||
@@ -778,7 +779,7 @@ public:
|
||||
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);
|
||||
if (with_func(index, item)) return VlQueue<IData>::consV(index);
|
||||
--index;
|
||||
}
|
||||
return VlQueue<IData>{};
|
||||
@@ -788,7 +789,7 @@ public:
|
||||
VlQueue min() const {
|
||||
if (m_deque.empty()) return VlQueue{};
|
||||
const auto it = std::min_element(m_deque.cbegin(), m_deque.cend());
|
||||
return VlQueue::cons(*it);
|
||||
return VlQueue::consV(*it);
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue min(Func with_func) const {
|
||||
@@ -797,12 +798,12 @@ public:
|
||||
[&with_func](const IData& a, const IData& b) {
|
||||
return with_func(0, a) < with_func(0, b);
|
||||
});
|
||||
return VlQueue::cons(*it);
|
||||
return VlQueue::consV(*it);
|
||||
}
|
||||
VlQueue max() const {
|
||||
if (m_deque.empty()) return VlQueue{};
|
||||
const auto it = std::max_element(m_deque.cbegin(), m_deque.cend());
|
||||
return VlQueue::cons(*it);
|
||||
return VlQueue::consV(*it);
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue max(Func with_func) const {
|
||||
@@ -811,7 +812,7 @@ public:
|
||||
[&with_func](const IData& a, const IData& b) {
|
||||
return with_func(0, a) < with_func(0, b);
|
||||
});
|
||||
return VlQueue::cons(*it);
|
||||
return VlQueue::consV(*it);
|
||||
}
|
||||
|
||||
T_Value r_sum() const {
|
||||
@@ -1092,7 +1093,7 @@ public:
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
if (it == m_map.end()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
return VlQueue<T_Value>::consV(it->second);
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Key> find_first_index(Func with_func) const {
|
||||
@@ -1101,7 +1102,7 @@ public:
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
if (it == m_map.end()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Key>::cons(it->first);
|
||||
return VlQueue<T_Key>::consV(it->first);
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> find_last(Func with_func) const {
|
||||
@@ -1109,7 +1110,7 @@ public:
|
||||
m_map.crbegin(), m_map.crend(),
|
||||
[=](const std::pair<T_Key, T_Value>& i) { return with_func(i.first, i.second); });
|
||||
if (it == m_map.rend()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
return VlQueue<T_Value>::consV(it->second);
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Key> find_last_index(Func with_func) const {
|
||||
@@ -1117,7 +1118,7 @@ public:
|
||||
m_map.crbegin(), m_map.crend(),
|
||||
[=](const std::pair<T_Key, T_Value>& i) { return with_func(i.first, i.second); });
|
||||
if (it == m_map.rend()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Key>::cons(it->first);
|
||||
return VlQueue<T_Key>::consV(it->first);
|
||||
}
|
||||
|
||||
// Reduction operators
|
||||
@@ -1128,7 +1129,7 @@ public:
|
||||
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
|
||||
return a.second < b.second;
|
||||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
return VlQueue<T_Value>::consV(it->second);
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> min(Func with_func) const {
|
||||
@@ -1138,7 +1139,7 @@ public:
|
||||
[&with_func](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
|
||||
return with_func(a.first, a.second) < with_func(b.first, b.second);
|
||||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
return VlQueue<T_Value>::consV(it->second);
|
||||
}
|
||||
VlQueue<T_Value> max() const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
@@ -1147,7 +1148,7 @@ public:
|
||||
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
|
||||
return a.second < b.second;
|
||||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
return VlQueue<T_Value>::consV(it->second);
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> max(Func with_func) const {
|
||||
@@ -1157,7 +1158,7 @@ public:
|
||||
[&with_func](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
|
||||
return with_func(a.first, a.second) < with_func(b.first, b.second);
|
||||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
return VlQueue<T_Value>::consV(it->second);
|
||||
}
|
||||
|
||||
T_Value r_sum() const {
|
||||
@@ -1438,7 +1439,7 @@ public:
|
||||
// Can't use std::find_if as need index number
|
||||
IData index = 0;
|
||||
for (const auto& i : m_storage) {
|
||||
if (with_func(index, i)) return VlQueue<T_Value>::cons(i);
|
||||
if (with_func(index, i)) return VlQueue<T_Value>::consV(i);
|
||||
++index;
|
||||
}
|
||||
return VlQueue<T_Value>{};
|
||||
@@ -1447,7 +1448,7 @@ public:
|
||||
VlQueue<T_Key> find_first_index(Func with_func) const {
|
||||
IData index = 0;
|
||||
for (const auto& i : m_storage) {
|
||||
if (with_func(index, i)) return VlQueue<IData>::cons(index);
|
||||
if (with_func(index, i)) return VlQueue<IData>::consV(index);
|
||||
++index;
|
||||
}
|
||||
return VlQueue<T_Key>{};
|
||||
@@ -1455,14 +1456,14 @@ public:
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> find_last(Func with_func) const {
|
||||
for (int i = T_Depth - 1; i >= 0; i--) {
|
||||
if (with_func(i, m_storage[i])) return VlQueue<T_Value>::cons(m_storage[i]);
|
||||
if (with_func(i, m_storage[i])) return VlQueue<T_Value>::consV(m_storage[i]);
|
||||
}
|
||||
return VlQueue<T_Value>{};
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Key> find_last_index(Func with_func) const {
|
||||
for (int i = T_Depth - 1; i >= 0; i--) {
|
||||
if (with_func(i, m_storage[i])) return VlQueue<IData>::cons(i);
|
||||
if (with_func(i, m_storage[i])) return VlQueue<IData>::consV(i);
|
||||
}
|
||||
return VlQueue<T_Key>{};
|
||||
}
|
||||
@@ -1470,7 +1471,7 @@ public:
|
||||
// Reduction operators
|
||||
VlQueue<T_Value> min() const {
|
||||
const auto it = std::min_element(std::begin(m_storage), std::end(m_storage));
|
||||
return VlQueue<T_Value>::cons(*it);
|
||||
return VlQueue<T_Value>::consV(*it);
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> min(Func with_func) const {
|
||||
@@ -1478,11 +1479,11 @@ public:
|
||||
[&with_func](const IData& a, const IData& b) {
|
||||
return with_func(0, a) < with_func(0, b);
|
||||
});
|
||||
return VlQueue<T_Value>::cons(*it);
|
||||
return VlQueue<T_Value>::consV(*it);
|
||||
}
|
||||
VlQueue<T_Value> max() const {
|
||||
const auto it = std::max_element(std::begin(m_storage), std::end(m_storage));
|
||||
return VlQueue<T_Value>::cons(*it);
|
||||
return VlQueue<T_Value>::consV(*it);
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> max(Func with_func) const {
|
||||
@@ -1490,7 +1491,7 @@ public:
|
||||
[&with_func](const IData& a, const IData& b) {
|
||||
return with_func(0, a) < with_func(0, b);
|
||||
});
|
||||
return VlQueue<T_Value>::cons(*it);
|
||||
return VlQueue<T_Value>::consV(*it);
|
||||
}
|
||||
|
||||
// Dumping. Verilog: str = $sformatf("%p", assoc)
|
||||
@@ -1786,12 +1787,16 @@ public:
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// Represents the null pointer. Used for setting VlClassRef to null instead of
|
||||
// via nullptr_t, to prevent the implicit conversion of 0 to nullptr.
|
||||
// Represents the null pointer. Used for:
|
||||
// * setting VlClassRef to null instead of via nullptr_t, to prevent the implicit conversion of 0
|
||||
// to nullptr,
|
||||
// * comparing interface pointers to null.
|
||||
|
||||
struct VlNull final {
|
||||
operator bool() const { return false; }
|
||||
bool operator==(void* ptr) const { return !ptr; }
|
||||
};
|
||||
inline bool operator==(void* ptr, VlNull) { return !ptr; }
|
||||
|
||||
//===================================================================
|
||||
// Verilog class reference container
|
||||
|
||||
@@ -958,8 +958,8 @@ public:
|
||||
}
|
||||
static void dumpCbs() VL_MT_UNSAFE_ONE;
|
||||
static VerilatedVpiError* error_info() VL_MT_UNSAFE_ONE; // getter for vpi error info
|
||||
static void evalNeeded(bool evalNeeded) { s().m_evalNeeded = evalNeeded; }
|
||||
static bool evalNeeded() { return s().m_evalNeeded; }
|
||||
static void evalNeeded(bool evalNeeded) { s().m_evalNeeded = evalNeeded; }
|
||||
static void inertialDelay(const VerilatedVpioVar* vop, p_vpi_value valuep) {
|
||||
s().m_inertialPuts.emplace_back(vop, valuep);
|
||||
}
|
||||
|
||||
@@ -51,10 +51,10 @@ double DeltaCpuTime::gettime() VL_MT_SAFE {
|
||||
#else
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
|
||||
timespec ts;
|
||||
if (0 != clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts)) // MT-Safe
|
||||
if (0 == clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts)) // MT-Safe // LCOV_EXCL_BR_LINE
|
||||
return ts.tv_sec + ts.tv_nsec * 1e-9;
|
||||
#endif
|
||||
return 0.0;
|
||||
return 0.0; // LCOV_EXCL_LINE
|
||||
}
|
||||
double DeltaWallTime::gettime() VL_MT_SAFE {
|
||||
#if defined(_WIN32) || defined(__MINGW32__)
|
||||
@@ -66,9 +66,9 @@ double DeltaWallTime::gettime() VL_MT_SAFE {
|
||||
#else
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
|
||||
timespec ts;
|
||||
if (0 == clock_gettime(CLOCK_MONOTONIC, &ts)) // MT-Safe
|
||||
if (0 == clock_gettime(CLOCK_MONOTONIC, &ts)) // MT-Safe // LCOV_EXCL_BR_LINE
|
||||
return ts.tv_sec + ts.tv_nsec * 1e-9;
|
||||
return 0.0;
|
||||
return 0.0; // LCOV_EXCL_LINE
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
+24
-5
@@ -180,9 +180,18 @@ def test():
|
||||
if Args.stage_enabled[18]:
|
||||
ci_fold_start("upload")
|
||||
print("Stage 18: Upload")
|
||||
cmd = "bash <(cat ci/coverage-upload.sh) -f " + cc_dir + "/app_total.info"
|
||||
print("print: Not running: export CODECOV_TOKEN=<hidden>")
|
||||
print("print: Not running: " + cmd)
|
||||
# curl -Os https://cli.codecov.io/latest/linux/codecov ; sudo chmod +x codecov
|
||||
# --disable-search does not seem to work
|
||||
# -d with false directory does not seem to work
|
||||
# So, remove gcno files before calling codecov
|
||||
upload_dir = "nodist/obj_dir/upload"
|
||||
os.makedirs(upload_dir, exist_ok=True)
|
||||
cmd = ("ci/codecov -v upload-process -Z" + " -f " + cc_dir +
|
||||
"/app_total.info )")
|
||||
print("print: Not running:")
|
||||
print(" export CODECOV_TOKEN=<hidden>")
|
||||
print(" find . -name '*.gcno' -exec rm {} \\;")
|
||||
print(" " + cmd)
|
||||
ci_fold_end()
|
||||
|
||||
if Args.stage_enabled[19]:
|
||||
@@ -253,12 +262,21 @@ def cleanup_abs_paths_info(cc_dir, infile, outfile):
|
||||
lines = []
|
||||
with open(infile, "r", encoding="utf8") as fh:
|
||||
for line in fh:
|
||||
if re.search(r'^SF:', line):
|
||||
if re.search(r'^SF:', line) and not re.search(r'^SF:/usr/', line):
|
||||
line = re.sub(os.environ['VERILATOR_ROOT'] + '/',
|
||||
'',
|
||||
line,
|
||||
count=1)
|
||||
line = re.sub(cc_dir + '/', '', line, count=1)
|
||||
line = re.sub(r'^SF:.*?/include/',
|
||||
'SF:include/',
|
||||
line,
|
||||
count=1)
|
||||
line = re.sub(r'^SF:.*?/src/', 'SF:src/', line, count=1)
|
||||
line = re.sub(r'^SF:.*?/test_regress/',
|
||||
'SF:test_regress/',
|
||||
line,
|
||||
count=1)
|
||||
line = re.sub(r'obj_dbg/verilog.y$', 'verilog.y', line)
|
||||
# print("Remaining SF: "+line)
|
||||
lines.append(line)
|
||||
@@ -364,12 +382,13 @@ parser.add_argument('--scenarios',
|
||||
help='pass test scenarios onto driver.pl test harness')
|
||||
parser.add_argument(
|
||||
'--stages',
|
||||
'--stage',
|
||||
action='store',
|
||||
help='runs a specific stage or range of stages (see the script)')
|
||||
parser.add_argument(
|
||||
'--tests',
|
||||
'--test',
|
||||
action='append',
|
||||
dest='tests',
|
||||
default=[],
|
||||
help=
|
||||
'Instead of normal regressions, run the specified test(s), may be used multiple times'
|
||||
|
||||
+374
-49
@@ -18,20 +18,21 @@
|
||||
$ ./fastcov.py --exclude /usr/include test/ --lcov -o report.info
|
||||
$ genhtml -o code_coverage report.info
|
||||
"""
|
||||
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
import glob
|
||||
import json
|
||||
import time
|
||||
import fnmatch
|
||||
import logging
|
||||
import argparse
|
||||
import threading
|
||||
import subprocess
|
||||
import multiprocessing
|
||||
from pathlib import Path
|
||||
|
||||
FASTCOV_VERSION = (1,7)
|
||||
FASTCOV_VERSION = (1,15)
|
||||
MINIMUM_PYTHON = (3,5)
|
||||
MINIMUM_GCOV = (9,0,0)
|
||||
|
||||
@@ -40,6 +41,10 @@ START_TIME = time.monotonic()
|
||||
GCOVS_TOTAL = 0
|
||||
GCOVS_SKIPPED = 0
|
||||
|
||||
# Gcov Coverage File Extensions
|
||||
GCOV_GCNO_EXT = ".gcno" # gcno = "[gc]ov [no]te"
|
||||
GCOV_GCDA_EXT = ".gcda" # gcda = "[gc]ov [da]ta"
|
||||
|
||||
# For when things go wrong...
|
||||
# Start error codes at 3 because 1-2 are special
|
||||
# See https://stackoverflow.com/a/1535733/2516916
|
||||
@@ -49,6 +54,8 @@ EXIT_CODES = {
|
||||
"python_version": 4,
|
||||
"unsupported_coverage_format": 5,
|
||||
"excl_not_found": 6,
|
||||
"bad_chunk_file": 7,
|
||||
"missing_json_key": 8,
|
||||
}
|
||||
|
||||
# Disable all logging in case developers are using this as a module
|
||||
@@ -60,6 +67,132 @@ class FastcovFormatter(logging.Formatter):
|
||||
log_message = super(FastcovFormatter, self).format(record)
|
||||
return "[{:.3f}s] {}".format(stopwatch(), log_message)
|
||||
|
||||
class DiffParseError(Exception):
|
||||
pass
|
||||
|
||||
class DiffParser(object):
|
||||
def _refinePaths(self, diff_metadata, diff_base_dir):
|
||||
diff_metadata.pop('/dev/null', None)
|
||||
diff_metadata.pop('', None)
|
||||
for key, value in diff_metadata.copy().items():
|
||||
diff_metadata.pop(key)
|
||||
#sources without added lines will be excluded
|
||||
if value:
|
||||
newpath = os.path.join(diff_base_dir, key) if diff_base_dir else os.path.abspath(key)
|
||||
diff_metadata[newpath] = value
|
||||
|
||||
def _parseTargetFile(self, line_with_target_file):
|
||||
#f.e. '+++ b/README.md1' or '+++ b/README.md1 timestamp'
|
||||
target_source = line_with_target_file[4:].partition('\t')[0].strip()
|
||||
target_source = target_source[2:] if target_source.startswith('b/') else target_source
|
||||
return target_source
|
||||
|
||||
def _parseHunkBoundaries(self, line_with_hunk_boundaries, line_index):
|
||||
#f.e. '@@ -121,4 +122,4 @@ Time to process all gcda and parse all gcov:'
|
||||
# Here ['-121,4', '+122,4']
|
||||
lines_info = line_with_hunk_boundaries[3:].partition("@@")[0].strip().split(' ')
|
||||
if len(lines_info) != 2:
|
||||
raise DiffParseError("Found invalid hunk. Line #{}. {}".format(line_index, line_with_hunk_boundaries))
|
||||
# Here ['122','4']
|
||||
target_lines_info = lines_info[1].strip('+').partition(',')
|
||||
target_line_current = int(target_lines_info[0])
|
||||
target_lines_count = int(target_lines_info[2]) if target_lines_info[2] else 1
|
||||
|
||||
# Here ['121','4']
|
||||
source_lines_info = lines_info[0].strip('-').partition(',')
|
||||
source_line_current = int(source_lines_info[0])
|
||||
source_lines_count = int(source_lines_info[2]) if source_lines_info[2] else 1
|
||||
return target_line_current, target_lines_count, source_line_current, source_lines_count
|
||||
|
||||
def parseDiffFile(self, diff_file, diff_base_dir, fallback_encodings=[]):
|
||||
diff_metadata = {}
|
||||
target_source = None
|
||||
target_hunk = set()
|
||||
target_line_current = 0
|
||||
target_line_end = 0
|
||||
source_line_current = 0
|
||||
source_line_end = 0
|
||||
found_hunk = False
|
||||
for i, line in enumerate(getSourceLines(diff_file, fallback_encodings), 1):
|
||||
line = line.rstrip()
|
||||
if not found_hunk:
|
||||
if line.startswith('+++ '):
|
||||
# refresh file
|
||||
target_source = self._parseTargetFile(line)
|
||||
elif line.startswith('@@ '):
|
||||
# refresh hunk
|
||||
target_line_current, target_lines_count, source_line_current, source_lines_count = self._parseHunkBoundaries(line, i)
|
||||
target_line_end = target_line_current + target_lines_count
|
||||
source_line_end = source_line_current + source_lines_count
|
||||
target_hunk = set()
|
||||
found_hunk = True
|
||||
continue
|
||||
if target_line_current > target_line_end or source_line_current > source_line_end:
|
||||
raise DiffParseError("Hunk longer than expected. Line #{}. {}".format(i, line))
|
||||
|
||||
if line.startswith('+'):
|
||||
#line related to target
|
||||
target_hunk.add(target_line_current)
|
||||
target_line_current = target_line_current + 1
|
||||
elif line.startswith(' ') or line == '':
|
||||
# line related to both
|
||||
target_line_current = target_line_current + 1
|
||||
source_line_current = source_line_current + 1
|
||||
elif line.startswith('-'):
|
||||
# line related to source
|
||||
source_line_current = source_line_current + 1
|
||||
elif not line.startswith('\\'): # No newline at end of file
|
||||
# line with newline marker is not included into any boundaries
|
||||
raise DiffParseError("Found unrecognized hunk line type. Line #{}. {}".format(i, line))
|
||||
|
||||
if target_line_current == target_line_end and source_line_current == source_line_end:
|
||||
# Checked all lines, save data
|
||||
if target_source in diff_metadata:
|
||||
diff_metadata[target_source] = target_hunk.union(diff_metadata[target_source])
|
||||
else:
|
||||
diff_metadata[target_source] = target_hunk
|
||||
target_hunk = set()
|
||||
found_hunk = False
|
||||
|
||||
if target_line_current != target_line_end or source_line_current != source_line_end:
|
||||
raise DiffParseError("Unexpected end of file. Expected hunk with {} target lines, {} source lines".format(
|
||||
target_line_end - target_line_current, source_line_end - source_line_current))
|
||||
|
||||
self._refinePaths(diff_metadata, diff_base_dir)
|
||||
return diff_metadata
|
||||
|
||||
def filterByDiff(self, diff_file, dir_base_dir, fastcov_json, fallback_encodings=[]):
|
||||
diff_metadata = self.parseDiffFile(diff_file, dir_base_dir, fallback_encodings)
|
||||
logging.debug("Include only next files: {}".format(diff_metadata.keys()))
|
||||
excluded_files_count = 0
|
||||
excluded_lines_count = 0
|
||||
for source in list(fastcov_json["sources"].keys()):
|
||||
diff_lines = diff_metadata.get(source, None)
|
||||
if not diff_lines:
|
||||
excluded_files_count = excluded_files_count + 1
|
||||
logging.debug("Exclude {} according to diff file".format(source))
|
||||
fastcov_json["sources"].pop(source)
|
||||
continue
|
||||
for test_name, report_data in fastcov_json["sources"][source].copy().items():
|
||||
#No info about functions boundaries, removing all
|
||||
for function in list(report_data["functions"].keys()):
|
||||
report_data["functions"].pop(function, None)
|
||||
for line in list(report_data["lines"].keys()):
|
||||
if line not in diff_lines:
|
||||
excluded_lines_count = excluded_lines_count + 1
|
||||
report_data["lines"].pop(line)
|
||||
for branch_line in list(report_data["branches"].keys()):
|
||||
if branch_line not in diff_lines:
|
||||
report_data["branches"].pop(branch_line)
|
||||
if len(report_data["lines"]) == 0:
|
||||
fastcov_json["sources"][source].pop(test_name)
|
||||
if len(fastcov_json["sources"][source]) == 0:
|
||||
excluded_files_count = excluded_files_count + 1
|
||||
logging.debug('Exclude {} file as it has no lines due to diff filter'.format(source))
|
||||
fastcov_json["sources"].pop(source)
|
||||
logging.info("Excluded {} files and {} lines according to diff file".format(excluded_files_count, excluded_lines_count))
|
||||
return fastcov_json
|
||||
|
||||
def chunks(l, n):
|
||||
"""Yield successive n-sized chunks from l."""
|
||||
for i in range(0, len(l), n):
|
||||
@@ -69,6 +202,10 @@ def setExitCode(key):
|
||||
global EXIT_CODE
|
||||
EXIT_CODE = EXIT_CODES[key]
|
||||
|
||||
def setExitCodeRaw(code):
|
||||
global EXIT_CODE
|
||||
EXIT_CODE = code
|
||||
|
||||
def incrementCounters(total, skipped):
|
||||
global GCOVS_TOTAL
|
||||
global GCOVS_SKIPPED
|
||||
@@ -98,10 +235,48 @@ def getGcovVersion(gcov):
|
||||
p.wait()
|
||||
return parseVersionFromLine(output.split("\n")[0])
|
||||
|
||||
def tryParseNumber(s):
|
||||
try:
|
||||
return int(s)
|
||||
except ValueError:
|
||||
# Log a warning if not hyphen
|
||||
if s != "-":
|
||||
logging.warning("Unsupported numerical value '%s', using 0", s)
|
||||
|
||||
# Default to 0 if we can't parse the number (e.g. "-", "NaN", etc.)
|
||||
return 0
|
||||
|
||||
def removeFiles(files):
|
||||
for file in files:
|
||||
os.remove(file)
|
||||
|
||||
def processPrefix(path, prefix, prefix_strip):
|
||||
p = Path(path)
|
||||
if p.exists() or not p.is_absolute():
|
||||
return path
|
||||
|
||||
if prefix_strip > 0:
|
||||
segments = p.parts
|
||||
|
||||
if len(segments) < prefix_strip + 1:
|
||||
logging.warning("Couldn't strip %i path levels from %s.", prefix_strip, path)
|
||||
return path
|
||||
|
||||
segments = segments[prefix_strip+1:]
|
||||
p = Path(segments[0])
|
||||
segments = segments[1:]
|
||||
for s in segments:
|
||||
p = p.joinpath(s)
|
||||
|
||||
if len(prefix) > 0:
|
||||
if p.is_absolute():
|
||||
p = Path(prefix).joinpath(p.relative_to('/'))
|
||||
else:
|
||||
p = Path(prefix).joinpath(p)
|
||||
|
||||
return str(p)
|
||||
|
||||
|
||||
def getFilteredCoverageFiles(coverage_files, exclude):
|
||||
def excludeGcda(gcda):
|
||||
for ex in exclude:
|
||||
@@ -111,11 +286,16 @@ def getFilteredCoverageFiles(coverage_files, exclude):
|
||||
return True
|
||||
return list(filter(excludeGcda, coverage_files))
|
||||
|
||||
def globCoverageFiles(cwd, coverage_type):
|
||||
return glob.glob(os.path.join(os.path.abspath(cwd), "**/*" + coverage_type), recursive=True)
|
||||
|
||||
def findCoverageFiles(cwd, coverage_files, use_gcno):
|
||||
coverage_type = "user provided"
|
||||
if not coverage_files:
|
||||
coverage_type = "gcno" if use_gcno else "gcda"
|
||||
coverage_files = glob.glob(os.path.join(os.path.abspath(cwd), "**/*." + coverage_type), recursive=True)
|
||||
# gcov strips off extension of whatever you pass it and searches [extensionless name] + .gcno/.gcda
|
||||
# We should pass either gcno or gcda, but not both - if you pass both it will be processed twice
|
||||
coverage_type = GCOV_GCNO_EXT if use_gcno else GCOV_GCDA_EXT
|
||||
coverage_files = globCoverageFiles(cwd, coverage_type)
|
||||
|
||||
logging.info("Found {} coverage files ({})".format(len(coverage_files), coverage_type))
|
||||
logging.debug("Coverage files found:\n %s", "\n ".join(coverage_files))
|
||||
@@ -125,15 +305,32 @@ def gcovWorker(data_q, metrics_q, args, chunk, gcov_filter_options):
|
||||
base_report = {"sources": {}}
|
||||
gcovs_total = 0
|
||||
gcovs_skipped = 0
|
||||
error_exit = False
|
||||
|
||||
gcov_args = "-it"
|
||||
gcov_bin = args.gcov
|
||||
gcov_args = ["--json-format", "--stdout"]
|
||||
if args.branchcoverage or args.xbranchcoverage:
|
||||
gcov_args += "b"
|
||||
gcov_args.append("--branch-probabilities")
|
||||
|
||||
p = subprocess.Popen([args.gcov, gcov_args] + chunk, cwd=args.cdirectory, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
|
||||
for line in iter(p.stdout.readline, b''):
|
||||
intermediate_json = json.loads(line.decode(sys.stdout.encoding))
|
||||
intermediate_json_files = processGcovs(args.cdirectory, intermediate_json["files"], gcov_filter_options)
|
||||
encoding = sys.stdout.encoding if sys.stdout.encoding else 'UTF-8'
|
||||
workdir = args.cdirectory if args.cdirectory else "."
|
||||
|
||||
p = subprocess.Popen([gcov_bin] + gcov_args + chunk, cwd=workdir, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
|
||||
for i, line in enumerate(iter(p.stdout.readline, b'')):
|
||||
try:
|
||||
intermediate_json = json.loads(line.decode(encoding))
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
logging.error("Could not process chunk file '{}' ({}/{})".format(chunk[i], i+1, len(chunk)))
|
||||
logging.error(str(e))
|
||||
setExitCode("bad_chunk_file")
|
||||
continue
|
||||
|
||||
if "current_working_directory" not in intermediate_json:
|
||||
logging.error("Missing 'current_working_directory' for data file: {}".format(intermediate_json))
|
||||
setExitCode("missing_json_key")
|
||||
continue
|
||||
|
||||
intermediate_json_files = processGcovs(args.cdirectory, intermediate_json["files"], intermediate_json["current_working_directory"], gcov_filter_options)
|
||||
for f in intermediate_json_files:
|
||||
distillSource(f, base_report["sources"], args.test_name, args.xbranchcoverage)
|
||||
gcovs_total += len(intermediate_json["files"])
|
||||
@@ -143,6 +340,8 @@ def gcovWorker(data_q, metrics_q, args, chunk, gcov_filter_options):
|
||||
data_q.put(base_report)
|
||||
metrics_q.put((gcovs_total, gcovs_skipped))
|
||||
|
||||
sys.exit(EXIT_CODE)
|
||||
|
||||
def processGcdas(args, coverage_files, gcov_filter_options):
|
||||
chunk_size = max(args.minimum_chunk, int(len(coverage_files) / args.jobs) + 1)
|
||||
|
||||
@@ -163,6 +362,8 @@ def processGcdas(args, coverage_files, gcov_filter_options):
|
||||
|
||||
for p in processes:
|
||||
p.join()
|
||||
if p.exitcode != 0:
|
||||
setExitCodeRaw(p.exitcode)
|
||||
|
||||
base_fastcov = fastcov_jsons.pop()
|
||||
for fj in fastcov_jsons:
|
||||
@@ -184,6 +385,12 @@ def shouldFilterSource(source, gcov_filter_options):
|
||||
logging.debug("Filtering coverage for '%s' due to option '--exclude %s'", source, ex)
|
||||
return True
|
||||
|
||||
# Check exclude filter
|
||||
for ex_glob in gcov_filter_options["exclude_glob"]:
|
||||
if fnmatch.fnmatch(source, ex_glob):
|
||||
logging.debug("Filtering coverage for '%s' due to option '--exclude-glob %s'", source, ex_glob)
|
||||
return True
|
||||
|
||||
# Check include filter
|
||||
if gcov_filter_options["include"]:
|
||||
included = False
|
||||
@@ -205,9 +412,12 @@ def filterFastcov(fastcov_json, args):
|
||||
if shouldFilterSource(source, gcov_filter_options):
|
||||
del fastcov_json["sources"][source]
|
||||
|
||||
def processGcov(cwd, gcov, files, gcov_filter_options):
|
||||
def processGcov(cwd, gcov, source_base_dir, files, gcov_filter_options):
|
||||
# Uses cwd if set, else source_base_dir from gcov json. If both are empty, uses "."
|
||||
base_dir = cwd if cwd else source_base_dir
|
||||
base_dir = base_dir if base_dir else "."
|
||||
# Add absolute path
|
||||
gcov["file_abs"] = os.path.abspath(os.path.join(cwd, gcov["file"]))
|
||||
gcov["file_abs"] = os.path.abspath(os.path.join(base_dir, gcov["file"]))
|
||||
|
||||
if shouldFilterSource(gcov["file_abs"], gcov_filter_options):
|
||||
return
|
||||
@@ -215,10 +425,10 @@ def processGcov(cwd, gcov, files, gcov_filter_options):
|
||||
files.append(gcov)
|
||||
logging.debug("Accepted coverage for '%s'", gcov["file_abs"])
|
||||
|
||||
def processGcovs(cwd, gcov_files, gcov_filter_options):
|
||||
def processGcovs(cwd, gcov_files, source_base_dir, gcov_filter_options):
|
||||
files = []
|
||||
for gcov in gcov_files:
|
||||
processGcov(cwd, gcov, files, gcov_filter_options)
|
||||
processGcov(cwd, gcov, source_base_dir, files, gcov_filter_options)
|
||||
return files
|
||||
|
||||
def dumpBranchCoverageToLcovInfo(f, branches):
|
||||
@@ -288,15 +498,44 @@ def getSourceLines(source, fallback_encodings=[]):
|
||||
with open(source, errors="ignore") as f:
|
||||
return f.readlines()
|
||||
|
||||
def exclProcessSource(fastcov_sources, source, exclude_branches_sw, include_branches_sw, fallback_encodings):
|
||||
def containsMarker(markers, strBody):
|
||||
for marker in markers:
|
||||
if marker in strBody:
|
||||
return True
|
||||
return False
|
||||
|
||||
# Returns whether source coverage changed or not
|
||||
def exclProcessSource(fastcov_sources, source, exclude_branches_sw, include_branches_sw, exclude_line_marker, fallback_encodings, gcov_prefix, gcov_prefix_strip):
|
||||
source_to_open = processPrefix(source, gcov_prefix, gcov_prefix_strip)
|
||||
|
||||
# Before doing any work, check if this file even needs to be processed
|
||||
if not exclude_branches_sw and not include_branches_sw:
|
||||
# Ignore unencodable characters
|
||||
with open(source_to_open, errors="ignore") as f:
|
||||
if not containsMarker(exclude_line_marker + ["LCOV_EXCL"], f.read()):
|
||||
return False
|
||||
|
||||
# If we've made it this far we have to check every line
|
||||
|
||||
start_line = 0
|
||||
end_line = 0
|
||||
# Start enumeration at line 1 because the first line of the file is line 1 not 0
|
||||
for i, line in enumerate(getSourceLines(source, fallback_encodings), 1):
|
||||
for i, line in enumerate(getSourceLines(source_to_open, fallback_encodings), 1):
|
||||
# Cycle through test names (likely only 1)
|
||||
for test_name in fastcov_sources[source]:
|
||||
fastcov_data = fastcov_sources[source][test_name]
|
||||
|
||||
# Check if branch coverage should be deleted based on CLI options
|
||||
if (exclude_branches_sw or include_branches_sw) and (i in fastcov_data["branches"]):
|
||||
del_exclude_br = exclude_branches_sw and any(line.lstrip().startswith(e) for e in exclude_branches_sw)
|
||||
del_include_br = include_branches_sw and all(not line.lstrip().startswith(e) for e in include_branches_sw)
|
||||
if del_exclude_br or del_include_br:
|
||||
del fastcov_data["branches"][i]
|
||||
|
||||
# Skip to next line as soon as possible
|
||||
if not containsMarker(exclude_line_marker + ["LCOV_EXCL"], line):
|
||||
continue
|
||||
|
||||
# Build line to function dict so can quickly delete by line number
|
||||
line_to_func = {}
|
||||
for f in fastcov_data["functions"].keys():
|
||||
@@ -305,16 +544,7 @@ def exclProcessSource(fastcov_sources, source, exclude_branches_sw, include_bran
|
||||
line_to_func[l] = set()
|
||||
line_to_func[l].add(f)
|
||||
|
||||
if i in fastcov_data["branches"]:
|
||||
del_exclude_br = exclude_branches_sw and any(line.lstrip().startswith(e) for e in exclude_branches_sw)
|
||||
del_include_br = include_branches_sw and all(not line.lstrip().startswith(e) for e in include_branches_sw)
|
||||
if del_exclude_br or del_include_br:
|
||||
del fastcov_data["branches"][i]
|
||||
|
||||
if "LCOV_EXCL" not in line:
|
||||
continue
|
||||
|
||||
if "LCOV_EXCL_LINE" in line:
|
||||
if any(marker in line for marker in exclude_line_marker):
|
||||
for key in ["lines", "branches"]:
|
||||
if i in fastcov_data[key]:
|
||||
del fastcov_data[key][i]
|
||||
@@ -347,26 +577,56 @@ def exclProcessSource(fastcov_sources, source, exclude_branches_sw, include_bran
|
||||
if i in fastcov_data["branches"]:
|
||||
del fastcov_data["branches"][i]
|
||||
|
||||
def exclMarkerWorker(fastcov_sources, chunk, exclude_branches_sw, include_branches_sw, fallback_encodings):
|
||||
# Source coverage changed
|
||||
return True
|
||||
|
||||
def exclMarkerWorker(data_q, fastcov_sources, chunk, exclude_branches_sw, include_branches_sw, exclude_line_marker, fallback_encodings, gcov_prefix, gcov_prefix_strip):
|
||||
changed_sources = []
|
||||
|
||||
for source in chunk:
|
||||
try:
|
||||
exclProcessSource(fastcov_sources, source, exclude_branches_sw, include_branches_sw, fallback_encodings)
|
||||
if exclProcessSource(fastcov_sources, source, exclude_branches_sw, include_branches_sw, exclude_line_marker, fallback_encodings, gcov_prefix, gcov_prefix_strip):
|
||||
changed_sources.append((source, fastcov_sources[source]))
|
||||
except FileNotFoundError:
|
||||
logging.error("Could not find '%s' to scan for exclusion markers...", source)
|
||||
setExitCode("excl_not_found") # Set exit code because of error
|
||||
|
||||
def scanExclusionMarkers(fastcov_json, jobs, exclude_branches_sw, include_branches_sw, min_chunk_size, fallback_encodings):
|
||||
# Write out changed sources back to main fastcov file
|
||||
data_q.put(changed_sources)
|
||||
|
||||
# Exit current process with appropriate code
|
||||
sys.exit(EXIT_CODE)
|
||||
|
||||
def processExclusionMarkers(fastcov_json, jobs, exclude_branches_sw, include_branches_sw, exclude_line_marker, min_chunk_size, fallback_encodings, gcov_prefix, gcov_prefix_strip):
|
||||
chunk_size = max(min_chunk_size, int(len(fastcov_json["sources"]) / jobs) + 1)
|
||||
|
||||
threads = []
|
||||
processes = []
|
||||
data_q = multiprocessing.Queue()
|
||||
for chunk in chunks(list(fastcov_json["sources"].keys()), chunk_size):
|
||||
t = threading.Thread(target=exclMarkerWorker, args=(fastcov_json["sources"], chunk, exclude_branches_sw, include_branches_sw, fallback_encodings))
|
||||
threads.append(t)
|
||||
t.start()
|
||||
p = multiprocessing.Process(target=exclMarkerWorker, args=(data_q, fastcov_json["sources"], chunk, exclude_branches_sw, include_branches_sw, exclude_line_marker, fallback_encodings, gcov_prefix, gcov_prefix_strip))
|
||||
processes.append(p)
|
||||
p.start()
|
||||
|
||||
logging.info("Spawned {} threads each scanning at most {} source files".format(len(threads), chunk_size))
|
||||
for t in threads:
|
||||
t.join()
|
||||
logging.info("Spawned {} exclusion marker scanning processes, each processing at most {} source files".format(len(processes), chunk_size))
|
||||
|
||||
changed_sources = []
|
||||
for p in processes:
|
||||
changed_sources += data_q.get()
|
||||
|
||||
for p in processes:
|
||||
p.join()
|
||||
if p.exitcode != 0:
|
||||
setExitCodeRaw(p.exitcode)
|
||||
|
||||
for changed_source in changed_sources:
|
||||
fastcov_json["sources"][changed_source[0]] = changed_source[1]
|
||||
|
||||
def validateSources(fastcov_json, gcov_prefix, gcov_prefix_strip):
|
||||
logging.info("Checking if all sources exist")
|
||||
for source in fastcov_json["sources"].keys():
|
||||
source = processPrefix(source, gcov_prefix, gcov_prefix_strip)
|
||||
if not os.path.exists(source):
|
||||
logging.error("Cannot find '{}'".format(source))
|
||||
|
||||
def distillFunction(function_raw, functions):
|
||||
function_name = function_raw["name"]
|
||||
@@ -413,6 +673,13 @@ def filterExceptionalBranches(branches):
|
||||
def distillLine(line_raw, lines, branches, include_exceptional_branches):
|
||||
line_number = int(line_raw["line_number"])
|
||||
count = int(line_raw["count"])
|
||||
if count < 0:
|
||||
if "function_name" in line_raw:
|
||||
logging.warning("Ignoring negative count found in '%s'.", line_raw["function_name"])
|
||||
else:
|
||||
logging.warning("Ignoring negative count.")
|
||||
count = 0
|
||||
|
||||
if line_number not in lines:
|
||||
lines[line_number] = count
|
||||
else:
|
||||
@@ -458,6 +725,7 @@ def getGcovFilterOptions(args):
|
||||
"sources": set([os.path.abspath(s) for s in args.sources]), #Make paths absolute, use set for fast lookups
|
||||
"include": args.includepost,
|
||||
"exclude": args.excludepost,
|
||||
"exclude_glob":args.excludepost_glob
|
||||
}
|
||||
|
||||
def addDicts(dict1, dict2):
|
||||
@@ -529,30 +797,30 @@ def parseInfo(path):
|
||||
current_test_name = line[3:].strip()
|
||||
elif line.startswith("SF:"):
|
||||
current_sf = line[3:].strip()
|
||||
fastcov_json["sources"][current_sf] = {
|
||||
fastcov_json["sources"].setdefault(current_sf, {
|
||||
current_test_name: {
|
||||
"functions": {},
|
||||
"branches": {},
|
||||
"lines": {},
|
||||
}
|
||||
}
|
||||
})
|
||||
current_data = fastcov_json["sources"][current_sf][current_test_name]
|
||||
elif line.startswith("FN:"):
|
||||
line_num, function_name = line[3:].strip().split(",")
|
||||
current_data["functions"][function_name] = {}
|
||||
current_data["functions"][function_name]["start_line"] = int(line_num)
|
||||
current_data["functions"][function_name]["start_line"] = tryParseNumber(line_num)
|
||||
elif line.startswith("FNDA:"):
|
||||
count, function_name = line[5:].strip().split(",")
|
||||
current_data["functions"][function_name]["execution_count"] = int(count)
|
||||
current_data["functions"][function_name]["execution_count"] = tryParseNumber(count)
|
||||
elif line.startswith("DA:"):
|
||||
line_num, count = line[3:].strip().split(",")
|
||||
current_data["lines"][line_num] = int(count)
|
||||
current_data["lines"][line_num] = tryParseNumber(count)
|
||||
elif line.startswith("BRDA:"):
|
||||
branch_tokens = line[5:].strip().split(",")
|
||||
line_num, count = branch_tokens[0], branch_tokens[-1]
|
||||
if line_num not in current_data["branches"]:
|
||||
current_data["branches"][line_num] = []
|
||||
current_data["branches"][line_num].append(int(count))
|
||||
current_data["branches"][line_num].append(tryParseNumber(count))
|
||||
|
||||
return fastcov_json
|
||||
|
||||
@@ -610,9 +878,9 @@ def getGcovCoverage(args):
|
||||
coverage_files = getFilteredCoverageFiles(coverage_files, args.excludepre)
|
||||
logging.info("Found {} coverage files after filtering".format(len(coverage_files)))
|
||||
|
||||
# We "zero" the "counters" by deleting all gcda files
|
||||
# We "zero" the "counters" by simply deleting all gcda files
|
||||
if args.zerocounters:
|
||||
removeFiles(coverage_files)
|
||||
removeFiles(globCoverageFiles(args.directory, GCOV_GCDA_EXT))
|
||||
logging.info("Removed {} .gcda files".format(len(coverage_files)))
|
||||
sys.exit()
|
||||
|
||||
@@ -626,6 +894,37 @@ def getGcovCoverage(args):
|
||||
|
||||
return fastcov_json
|
||||
|
||||
def formatCoveredItems(covered, total):
|
||||
coverage = (covered * 100.0) / total if total > 0 else 100.0
|
||||
coverage = round(coverage, 2)
|
||||
return "{:.2f}%, {}/{}".format(coverage, covered, total)
|
||||
|
||||
def dumpStatistic(fastcov_json):
|
||||
total_lines = 0
|
||||
covered_lines = 0
|
||||
total_functions = 0
|
||||
covered_functions = 0
|
||||
total_files = len(fastcov_json["sources"])
|
||||
covered_files = 0
|
||||
|
||||
for source_name, source in fastcov_json["sources"].items():
|
||||
is_file_covered = False
|
||||
for test_name, test in source.items():
|
||||
total_lines += len(test["lines"])
|
||||
for execution_count in test["lines"].values():
|
||||
covered_lines += 1 if execution_count > 0 else 0
|
||||
is_file_covered = is_file_covered or execution_count > 0
|
||||
total_functions += len(test["functions"])
|
||||
for function in test["functions"].values():
|
||||
covered_functions += 1 if function['execution_count'] > 0 else 0
|
||||
is_file_covered = is_file_covered or function['execution_count'] > 0
|
||||
if is_file_covered:
|
||||
covered_files = covered_files + 1
|
||||
|
||||
logging.info("Files Coverage: {}".format(formatCoveredItems(covered_files, total_files)))
|
||||
logging.info("Functions Coverage: {}".format(formatCoveredItems(covered_functions, total_functions)))
|
||||
logging.info("Lines Coverage: {}".format(formatCoveredItems(covered_lines, total_lines)))
|
||||
|
||||
def dumpFile(fastcov_json, args):
|
||||
if args.lcov:
|
||||
dumpToLcovInfo(fastcov_json, args.output)
|
||||
@@ -634,6 +933,9 @@ def dumpFile(fastcov_json, args):
|
||||
dumpToJson(fastcov_json, args.output)
|
||||
logging.info("Created fastcov json file '{}'".format(args.output))
|
||||
|
||||
if args.dump_statistic:
|
||||
dumpStatistic(fastcov_json)
|
||||
|
||||
def tupleToDotted(tup):
|
||||
return ".".join(map(str, tup))
|
||||
|
||||
@@ -655,15 +957,19 @@ def parseArgs():
|
||||
# Filtering Options
|
||||
parser.add_argument('-s', '--source-files', dest='sources', nargs="+", metavar='', default=[], help='Filter: Specify exactly which source files should be included in the final report. Paths must be either absolute or relative to current directory.')
|
||||
parser.add_argument('-e', '--exclude', dest='excludepost', nargs="+", metavar='', default=[], help='Filter: Exclude source files from final report if they contain one of the provided substrings (i.e. /usr/include test/, etc.)')
|
||||
parser.add_argument('-eg', '--exclude-glob', dest='excludepost_glob', nargs="+", metavar='', default=[], help='Filter: Exclude source files by glob pattern from final report if they contain one of the provided substrings (i.e. /usr/include test/, etc.)')
|
||||
parser.add_argument('-i', '--include', dest='includepost', nargs="+", metavar='', default=[], help='Filter: Only include source files in final report that contain one of the provided substrings (i.e. src/ etc.)')
|
||||
parser.add_argument('-f', '--gcda-files', dest='coverage_files', nargs="+", metavar='', default=[], help='Filter: Specify exactly which gcda or gcno files should be processed. Note that specifying gcno causes both gcno and gcda to be processed.')
|
||||
parser.add_argument('-E', '--exclude-gcda', dest='excludepre', nargs="+", metavar='', default=[], help='Filter: Exclude gcda or gcno files from being processed via simple find matching (not regex)')
|
||||
parser.add_argument('-u', '--diff-filter', dest='diff_file', default='', help='Unified diff file with changes which will be included into final report')
|
||||
parser.add_argument('-ub', '--diff-base-dir', dest='diff_base_dir', default='', help='Base directory for sources in unified diff file, usually repository dir')
|
||||
parser.add_argument('-ce', '--custom-exclusion-marker', dest='exclude_line_marker', nargs="+", metavar='', default=["LCOV_EXCL_LINE"], help='Filter: Add filter for lines that will be excluded from coverage (same behavior as "LCOV_EXCL_LINE")')
|
||||
|
||||
parser.add_argument('-g', '--gcov', dest='gcov', default='gcov', help='Which gcov binary to use')
|
||||
|
||||
parser.add_argument('-d', '--search-directory', dest='directory', default=".", help='Base directory to recursively search for gcda files (default: .)')
|
||||
parser.add_argument('-c', '--compiler-directory', dest='cdirectory', default=".", help='Base directory compiler was invoked from (default: .) \
|
||||
This needs to be set if invoking fastcov from somewhere other than the base compiler directory.')
|
||||
parser.add_argument('-c', '--compiler-directory', dest='cdirectory', default="", help='Base directory compiler was invoked from (default: . or read from gcov) \
|
||||
This needs to be set if invoking fastcov from somewhere other than the base compiler directory. No need to set it if gcc version > 9.1')
|
||||
|
||||
parser.add_argument('-j', '--jobs', dest='jobs', type=int, default=multiprocessing.cpu_count(), help='Number of parallel gcov to spawn (default: {}).'.format(multiprocessing.cpu_count()))
|
||||
parser.add_argument('-m', '--minimum-chunk-size', dest='minimum_chunk', type=int, default=5, help='Minimum number of files a thread should process (default: 5). \
|
||||
@@ -672,17 +978,23 @@ def parseArgs():
|
||||
parser.add_argument('-F', '--fallback-encodings', dest='fallback_encodings', nargs="+", metavar='', default=[], help='List of encodings to try if opening a source file with the default fails (i.e. latin1, etc.). This option is not usually needed.')
|
||||
|
||||
parser.add_argument('-l', '--lcov', dest='lcov', action="store_true", help='Output in lcov info format instead of fastcov json')
|
||||
parser.add_argument('-o', '--output', dest='output', default="coverage.json", help='Name of output file (default: coverage.json)')
|
||||
parser.add_argument('-o', '--output', dest='output', default="", help='Name of output file (default: coverage.json or coverage.info, depends on --lcov option)')
|
||||
parser.add_argument('-q', '--quiet', dest='quiet', action="store_true", help='Suppress output to stdout')
|
||||
|
||||
parser.add_argument('-t', '--test-name', dest='test_name', default="", help='Specify a test name for the coverage. Equivalent to lcov\'s `-t`.')
|
||||
parser.add_argument('-C', '--add-tracefile', dest='combine', nargs="+", help='Combine multiple coverage files into one. If this flag is specified, fastcov will do a combine operation instead invoking gcov. Equivalent to lcov\'s `-a`.')
|
||||
|
||||
parser.add_argument('-V', '--verbose', dest="verbose", action="store_true", help="Print more detailed information about what fastcov is doing")
|
||||
parser.add_argument('-w', '--validate-sources', dest="validate_sources", action="store_true", help="Check if every source file exists")
|
||||
parser.add_argument('-p', '--dump-statistic', dest="dump_statistic", action="store_true", help="Dump total statistic at the end")
|
||||
parser.add_argument('-v', '--version', action="version", version='%(prog)s {version}'.format(version=__version__), help="Show program's version number and exit")
|
||||
|
||||
args = parser.parse_args()
|
||||
parser.add_argument('-gps', '--gcov_prefix_strip', dest="gcov_prefix_strip", action="store", default=0, type=int, help="The number of initial directory names to strip off the absolute paths in the object file.")
|
||||
parser.add_argument('-gp', '--gcov_prefix', dest="gcov_prefix", action="store", default="", help="The prefix to add to the paths in the object file.")
|
||||
|
||||
args = parser.parse_args()
|
||||
if not args.output:
|
||||
args.output = 'coverage.info' if args.lcov else 'coverage.json'
|
||||
return args
|
||||
|
||||
def checkPythonVersion(version):
|
||||
@@ -717,6 +1029,12 @@ def main():
|
||||
# Setup logging
|
||||
setupLogging(args.quiet, args.verbose)
|
||||
|
||||
if args.gcov_prefix_strip > 0:
|
||||
os.environ["GCOV_PREFIX_STRIP"] = str(args.gcov_prefix_strip)
|
||||
|
||||
if len(args.gcov_prefix) > 0:
|
||||
os.environ["GCOV_PREFIX"] = args.gcov_prefix
|
||||
|
||||
# Get report from appropriate source
|
||||
if args.combine:
|
||||
fastcov_json = getCombineCoverage(args)
|
||||
@@ -727,9 +1045,16 @@ def main():
|
||||
|
||||
# Scan for exclusion markers
|
||||
if not skip_exclusion_markers:
|
||||
scanExclusionMarkers(fastcov_json, args.jobs, args.exclude_branches_sw, args.include_branches_sw, args.minimum_chunk, args.fallback_encodings)
|
||||
processExclusionMarkers(fastcov_json, args.jobs, args.exclude_branches_sw, args.include_branches_sw, args.exclude_line_marker, args.minimum_chunk, args.fallback_encodings, args.gcov_prefix, args.gcov_prefix_strip)
|
||||
logging.info("Scanned {} source files for exclusion markers".format(len(fastcov_json["sources"])))
|
||||
|
||||
if args.diff_file:
|
||||
logging.info("Filtering according to {} file".format(args.diff_file))
|
||||
DiffParser().filterByDiff(args.diff_file, args.diff_base_dir, fastcov_json, args.fallback_encodings)
|
||||
|
||||
if args.validate_sources:
|
||||
validateSources(fastcov_json, args.gcov_prefix, args.gcov_prefix_strip)
|
||||
|
||||
# Dump to desired file format
|
||||
dumpFile(fastcov_json, args)
|
||||
|
||||
|
||||
Executable
+120
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env python3
|
||||
# pylint: disable=C0114,C0116,C0209,R0912,R0915
|
||||
######################################################################
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
#from pprint import pprint,pformat
|
||||
|
||||
#######################################################################
|
||||
|
||||
|
||||
def message_section(msg):
|
||||
"""Return sorting-section number for given commit message"""
|
||||
if re.match(r'^Support', msg):
|
||||
return 10
|
||||
if re.match(r'^Add', msg):
|
||||
return 20
|
||||
if re.match(r'^Improve', msg):
|
||||
return 30
|
||||
if re.match(r'^Fix', msg):
|
||||
return 40
|
||||
if re.match(r'^(Internals|CI|Tests)', msg):
|
||||
return -1
|
||||
return 0
|
||||
|
||||
|
||||
def process():
|
||||
cmd = "git log"
|
||||
|
||||
msgs = {}
|
||||
with os.popen(cmd) as fh:
|
||||
author = ""
|
||||
lineno = 0
|
||||
for line in fh:
|
||||
lineno += 1
|
||||
line = line.rstrip()
|
||||
if re.match(r'^Date', line):
|
||||
continue
|
||||
if re.match(r'^commit', line):
|
||||
continue
|
||||
if re.search(r'Commentary: Changes update', line):
|
||||
break
|
||||
|
||||
am = re.match(r'^Author: (.*) <(.*)>', line)
|
||||
dm = re.match(r'^ +(.*)', line)
|
||||
|
||||
if am:
|
||||
email = am.group(2)
|
||||
author = am.group(1)
|
||||
if re.search(r'antmicro', email):
|
||||
author += ", Antmicro Ltd."
|
||||
if re.search(r'github action', author):
|
||||
author = ""
|
||||
continue
|
||||
|
||||
elif author != "" and dm:
|
||||
msg = dm.group(1)
|
||||
if not re.search(r'\.$', msg):
|
||||
msg += '.'
|
||||
msg += ' [' + author + ']'
|
||||
|
||||
mid = re.search(r'\(#([0-9][0-9][0-9][0-9]+)', line)
|
||||
if mid:
|
||||
bug_id = mid.group(1)
|
||||
else:
|
||||
bug_id = " %d" % lineno
|
||||
|
||||
section = message_section(msg)
|
||||
if section >= 0:
|
||||
key = "%06s_%06s_%06d" % (section, bug_id, lineno)
|
||||
msgs[key] = '* ' + msg
|
||||
# print("i [%s] %s" % (key, msg))
|
||||
|
||||
author = ""
|
||||
|
||||
if not msgs:
|
||||
print("No Changes need to be inserted.")
|
||||
return
|
||||
|
||||
print()
|
||||
print("Insertion-sort the following lines into 'Changes' file:")
|
||||
print()
|
||||
|
||||
dedup = {}
|
||||
for key in sorted(msgs.keys()):
|
||||
if msgs[key] not in dedup:
|
||||
dedup[msgs[key]] = True
|
||||
print(msgs[key])
|
||||
|
||||
print()
|
||||
print("You may now want to clean up spelling, and commit:")
|
||||
print(" (cd docs ; make spelling)")
|
||||
print(" git ci -am 'Commentary: Changes update'")
|
||||
print()
|
||||
|
||||
|
||||
#######################################################################
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
allow_abbrev=False,
|
||||
prog="log_changes",
|
||||
description="Create example entries for 'Changes' from parsing 'git log'",
|
||||
epilog=
|
||||
"""Copyright 2019-2024 by Wilson Snyder. This program is free software; you
|
||||
can redistribute it and/or modify it under the terms of either the GNU
|
||||
Lesser General Public License Version 3 or the Perl Artistic License
|
||||
Version 2.0.
|
||||
|
||||
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""")
|
||||
|
||||
parser.add_argument('--debug', action='store_true', help='enable debug')
|
||||
Args = parser.parse_args()
|
||||
|
||||
process()
|
||||
|
||||
######################################################################
|
||||
# Local Variables:
|
||||
# compile-command: "cd .. ; nodist/log_changes"
|
||||
# End:
|
||||
@@ -142,6 +142,7 @@ set(HEADERS
|
||||
V3Randomize.h
|
||||
V3Reloop.h
|
||||
V3Rtti.h
|
||||
V3Sampled.h
|
||||
V3Sched.h
|
||||
V3Scope.h
|
||||
V3Scoreboard.h
|
||||
@@ -288,6 +289,7 @@ set(COMMON_SOURCES
|
||||
V3ProtectLib.cpp
|
||||
V3Randomize.cpp
|
||||
V3Reloop.cpp
|
||||
V3Sampled.cpp
|
||||
V3Sched.cpp
|
||||
V3SchedAcyclic.cpp
|
||||
V3SchedPartition.cpp
|
||||
@@ -488,6 +490,8 @@ add_dependencies(${verilator}
|
||||
V3PreLex_yy_cpp${CMAKE_BUILD_TYPE}
|
||||
)
|
||||
|
||||
target_link_libraries(${verilator} PRIVATE Threads::Threads)
|
||||
|
||||
# verilated_cov_key.h is only regenerated in a single-configuration environment.
|
||||
# This limitation can be lifted when `add_dependencies` will support generator
|
||||
# expressions. See https://gitlab.kitware.com/cmake/cmake/issues/19467
|
||||
|
||||
@@ -284,6 +284,7 @@ RAW_OBJS_PCH_ASTNOMT = \
|
||||
V3ProtectLib.o \
|
||||
V3Randomize.o \
|
||||
V3Reloop.o \
|
||||
V3Sampled.o \
|
||||
V3Sched.o \
|
||||
V3SchedAcyclic.o \
|
||||
V3SchedPartition.o \
|
||||
|
||||
+165
-58
@@ -26,13 +26,10 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
// Assert class functions
|
||||
|
||||
class AssertVisitor final : public VNVisitor {
|
||||
// TYPES
|
||||
enum assertType_e : uint8_t {
|
||||
ASSERT_TYPE_INTRINSIC, // AstNodeAssertIntrinsinc
|
||||
ASSERT_TYPE_SVA, // SVA, PSL
|
||||
ASSERT_TYPE_CASE, // unique/unique0/priority case related checks
|
||||
ASSERT_TYPE_IF // unique/unique0/priority if related checks
|
||||
};
|
||||
// CONSTANTS
|
||||
static constexpr uint8_t ALL_ASSERT_TYPES
|
||||
= std::numeric_limits<std::underlying_type<VAssertType::en>::type>::max();
|
||||
|
||||
// NODE STATE/TYPES
|
||||
// Cleared on netlist
|
||||
// AstNode::user() -> bool. True if processed
|
||||
@@ -54,24 +51,82 @@ class AssertVisitor final : public VNVisitor {
|
||||
bool m_inSampled = false; // True inside a sampled expression
|
||||
|
||||
// METHODS
|
||||
static bool assertTypeOn(assertType_e assertType) {
|
||||
if (assertType == ASSERT_TYPE_INTRINSIC) return true;
|
||||
if (v3Global.opt.assertOn()) return true;
|
||||
if (assertType == ASSERT_TYPE_CASE && v3Global.opt.assertCaseOn()) return true;
|
||||
return false;
|
||||
static AstNodeExpr* assertOnCond(FileLine* fl, VAssertType type,
|
||||
VAssertDirectiveType directiveType) {
|
||||
switch (directiveType) {
|
||||
case VAssertDirectiveType::INTRINSIC: return new AstConst{fl, AstConst::BitTrue{}};
|
||||
case VAssertDirectiveType::VIOLATION_CASE: {
|
||||
if (v3Global.opt.assertCaseOn()) {
|
||||
return new AstCExpr{fl, "vlSymsp->_vm_contextp__->assertOn()", 1};
|
||||
}
|
||||
// If assertions are off, have constant propagation rip them out later
|
||||
// This allows syntax errors and such to be detected normally.
|
||||
return new AstConst{fl, AstConst::BitFalse{}};
|
||||
}
|
||||
case VAssertDirectiveType::ASSERT:
|
||||
case VAssertDirectiveType::COVER:
|
||||
case VAssertDirectiveType::ASSUME: {
|
||||
if (v3Global.opt.assertOn()) {
|
||||
return new AstCExpr{fl,
|
||||
"vlSymsp->_vm_contextp__->assertOnGet("s + std::to_string(type)
|
||||
+ ", "s + std::to_string(directiveType) + ")"s,
|
||||
1};
|
||||
}
|
||||
return new AstConst{fl, AstConst::BitFalse{}};
|
||||
}
|
||||
case VAssertDirectiveType::INTERNAL:
|
||||
case VAssertDirectiveType::VIOLATION_IF:
|
||||
case VAssertDirectiveType::RESTRICT: {
|
||||
if (v3Global.opt.assertOn()) {
|
||||
return new AstCExpr{fl, "vlSymsp->_vm_contextp__->assertOn()", 1};
|
||||
}
|
||||
return new AstConst{fl, AstConst::BitFalse{}};
|
||||
}
|
||||
}
|
||||
VL_UNREACHABLE;
|
||||
}
|
||||
string assertDisplayMessage(AstNode* nodep, const string& prefix, const string& message,
|
||||
VDisplayType severity) {
|
||||
if (severity == VDisplayType::DT_ERROR || severity == VDisplayType::DT_FATAL) {
|
||||
return (string{"[%0t] " + prefix + ": "} + nodep->fileline()->filebasename() + ":"
|
||||
return ("[%0t] "s + prefix + ": " + nodep->fileline()->filebasename() + ":"
|
||||
+ cvtToStr(nodep->fileline()->lineno()) + ": Assertion failed in %m"
|
||||
+ ((message != "") ? ": " : "") + message + "\n");
|
||||
} else {
|
||||
return (string{"[%0t] " + prefix + ": "} + nodep->fileline()->filebasename() + ":"
|
||||
return ("[%0t] "s + prefix + ": " + nodep->fileline()->filebasename() + ":"
|
||||
+ cvtToStr(nodep->fileline()->lineno()) + ": %m"
|
||||
+ ((message != "") ? ": " : "") + message + "\n");
|
||||
}
|
||||
}
|
||||
static bool resolveAssertType(AstAssertCtl* nodep) {
|
||||
if (!nodep->assertTypesp()) {
|
||||
nodep->ctlAssertTypes(VAssertType{ALL_ASSERT_TYPES});
|
||||
return true;
|
||||
}
|
||||
if (const AstConst* const assertTypesp = VN_CAST(nodep->assertTypesp(), Const)) {
|
||||
nodep->ctlAssertTypes(VAssertType{assertTypesp->toSInt()});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static bool resolveControlType(AstAssertCtl* nodep) {
|
||||
if (const AstConst* const constp = VN_CAST(nodep->controlTypep(), Const)) {
|
||||
nodep->ctlType(constp->toSInt());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static bool resolveDirectiveType(AstAssertCtl* nodep) {
|
||||
if (!nodep->directiveTypesp()) {
|
||||
nodep->ctlDirectiveTypes(VAssertDirectiveType::ASSERT | VAssertDirectiveType::ASSUME
|
||||
| VAssertDirectiveType::COVER);
|
||||
return true;
|
||||
}
|
||||
if (const AstConst* const directiveTypesp = VN_CAST(nodep->directiveTypesp(), Const)) {
|
||||
nodep->ctlDirectiveTypes(VAssertDirectiveType{directiveTypesp->toSInt()});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void replaceDisplay(AstDisplay* nodep, const string& prefix) {
|
||||
nodep->fmtp()->text(
|
||||
assertDisplayMessage(nodep, prefix, nodep->fmtp()->text(), nodep->displayType()));
|
||||
@@ -112,43 +167,37 @@ class AssertVisitor final : public VNVisitor {
|
||||
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
|
||||
return varrefp;
|
||||
}
|
||||
AstNode* newIfAssertOn(AstNode* nodep, assertType_e assertType) {
|
||||
static AstNodeStmt* newIfAssertOn(AstNode* bodyp, VAssertDirectiveType directiveType,
|
||||
VAssertType type = VAssertType::INTERNAL) {
|
||||
// Add a internal if to check assertions are on.
|
||||
// Don't make this a AND term, as it's unlikely to need to test this.
|
||||
FileLine* const fl = nodep->fileline();
|
||||
FileLine* const fl = bodyp->fileline();
|
||||
|
||||
// If assertions are off, have constant propagation rip them out later
|
||||
// This allows syntax errors and such to be detected normally.
|
||||
AstNodeExpr* const condp
|
||||
= assertType == ASSERT_TYPE_INTRINSIC
|
||||
? static_cast<AstNodeExpr*>(new AstConst{fl, AstConst::BitTrue{}})
|
||||
: assertTypeOn(assertType)
|
||||
? static_cast<AstNodeExpr*>(
|
||||
new AstCExpr{fl, "vlSymsp->_vm_contextp__->assertOn()", 1})
|
||||
: static_cast<AstNodeExpr*>(new AstConst{fl, AstConst::BitFalse{}});
|
||||
AstNodeIf* const newp = new AstIf{fl, condp, nodep};
|
||||
AstNodeExpr* const condp = assertOnCond(fl, type, directiveType);
|
||||
AstNodeIf* const newp = new AstIf{fl, condp, bodyp};
|
||||
newp->isBoundsCheck(true); // To avoid LATCH warning
|
||||
newp->user1(true); // Don't assert/cover this if
|
||||
return newp;
|
||||
}
|
||||
|
||||
AstNode* newFireAssertUnchecked(AstNode* nodep, const string& message,
|
||||
AstNodeExpr* exprsp = nullptr) {
|
||||
AstNodeStmt* newFireAssertUnchecked(AstNodeStmt* nodep, const string& message,
|
||||
AstNodeExpr* exprsp = nullptr) {
|
||||
// Like newFireAssert() but omits the asserts-on check
|
||||
AstDisplay* const dispp
|
||||
= new AstDisplay{nodep->fileline(), VDisplayType::DT_ERROR, message, nullptr, nullptr};
|
||||
dispp->fmtp()->timeunit(m_modp->timeunit());
|
||||
AstNode* const bodysp = dispp;
|
||||
AstNodeStmt* const bodysp = dispp;
|
||||
replaceDisplay(dispp, "%%Error"); // Convert to standard DISPLAY format
|
||||
if (exprsp) dispp->fmtp()->exprsp()->addNext(exprsp);
|
||||
if (v3Global.opt.stopFail()) bodysp->addNext(new AstStop{nodep->fileline(), true});
|
||||
return bodysp;
|
||||
}
|
||||
|
||||
AstNode* newFireAssert(AstNode* nodep, assertType_e assertType, const string& message,
|
||||
AstNodeExpr* exprsp = nullptr) {
|
||||
AstNode* bodysp = newFireAssertUnchecked(nodep, message, exprsp);
|
||||
bodysp = newIfAssertOn(bodysp, assertType);
|
||||
AstNodeStmt* newFireAssert(AstNodeStmt* nodep, VAssertDirectiveType directiveType,
|
||||
VAssertType assertType, const string& message,
|
||||
AstNodeExpr* exprsp = nullptr) {
|
||||
AstNodeStmt* bodysp = newFireAssertUnchecked(nodep, message, exprsp);
|
||||
bodysp = newIfAssertOn(bodysp, directiveType, assertType);
|
||||
return bodysp;
|
||||
}
|
||||
|
||||
@@ -191,6 +240,7 @@ class AssertVisitor final : public VNVisitor {
|
||||
}
|
||||
|
||||
if (bodysp && passsp) bodysp = bodysp->addNext(passsp);
|
||||
if (bodysp) bodysp = newIfAssertOn(bodysp, nodep->directive(), nodep->type());
|
||||
ifp = new AstIf{nodep->fileline(), propp, bodysp};
|
||||
ifp->isBoundsCheck(true); // To avoid LATCH warning
|
||||
bodysp = ifp;
|
||||
@@ -200,17 +250,13 @@ class AssertVisitor final : public VNVisitor {
|
||||
} else {
|
||||
++m_statAsNotImm;
|
||||
}
|
||||
const assertType_e assertType
|
||||
= VN_IS(nodep, AssertIntrinsic) ? ASSERT_TYPE_INTRINSIC : ASSERT_TYPE_SVA;
|
||||
if (passsp) passsp = newIfAssertOn(passsp, assertType);
|
||||
if (failsp) failsp = newIfAssertOn(failsp, assertType);
|
||||
if (!passsp && !failsp) failsp = newFireAssertUnchecked(nodep, "'assert' failed.");
|
||||
ifp = new AstIf{nodep->fileline(), propp, passsp, failsp};
|
||||
ifp->isBoundsCheck(true); // To avoid LATCH warning
|
||||
// It's more LIKELY that we'll take the nullptr if clause
|
||||
// than the sim-killing else clause:
|
||||
ifp->branchPred(VBranchPred::BP_LIKELY);
|
||||
bodysp = newIfAssertOn(ifp, assertType);
|
||||
bodysp = newIfAssertOn(ifp, nodep->directive(), nodep->type());
|
||||
} else {
|
||||
nodep->v3fatalSrc("Unknown node type");
|
||||
}
|
||||
@@ -281,9 +327,13 @@ class AssertVisitor final : public VNVisitor {
|
||||
= ((allow_none || hasDefaultElse)
|
||||
? static_cast<AstNodeExpr*>(new AstOneHot0{nodep->fileline(), propp})
|
||||
: static_cast<AstNodeExpr*>(new AstOneHot{nodep->fileline(), propp}));
|
||||
AstIf* const checkifp = new AstIf{
|
||||
nodep->fileline(), new AstLogNot{nodep->fileline(), ohot},
|
||||
newFireAssert(nodep, ASSERT_TYPE_IF, "'unique if' statement violated"), newifp};
|
||||
const VAssertType assertType
|
||||
= nodep->uniquePragma() ? VAssertType::UNIQUE : VAssertType::UNIQUE0;
|
||||
AstIf* const checkifp
|
||||
= new AstIf{nodep->fileline(), new AstLogNot{nodep->fileline(), ohot},
|
||||
newFireAssert(nodep, VAssertDirectiveType::VIOLATION_IF, assertType,
|
||||
"'unique if' statement violated"),
|
||||
newifp};
|
||||
checkifp->isBoundsCheck(true); // To avoid LATCH warning
|
||||
checkifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||
nodep->replaceWith(checkifp);
|
||||
@@ -303,6 +353,16 @@ class AssertVisitor final : public VNVisitor {
|
||||
if (itemp->isDefault()) has_default = true;
|
||||
}
|
||||
const AstNodeDType* exprDtypep = nodep->exprp()->dtypep()->skipRefp();
|
||||
|
||||
VAssertType assertType = VAssertType::INTERNAL;
|
||||
if (nodep->priorityPragma()) {
|
||||
assertType = VAssertType::PRIORITY;
|
||||
} else if (nodep->uniquePragma()) {
|
||||
assertType = VAssertType::UNIQUE;
|
||||
} else if (nodep->unique0Pragma()) {
|
||||
assertType = VAssertType::UNIQUE0;
|
||||
}
|
||||
|
||||
string valFmt;
|
||||
if (exprDtypep->isIntegralOrPacked())
|
||||
valFmt = " for '" + cvtToStr(exprDtypep->widthMin()) + "'h%X'";
|
||||
@@ -312,7 +372,7 @@ class AssertVisitor final : public VNVisitor {
|
||||
if (!has_default) {
|
||||
nodep->addItemsp(new AstCaseItem{
|
||||
nodep->fileline(), nullptr /*DEFAULT*/,
|
||||
newFireAssert(nodep, ASSERT_TYPE_CASE,
|
||||
newFireAssert(nodep, VAssertDirectiveType::VIOLATION_CASE, assertType,
|
||||
nodep->pragmaString() + ", but non-match found" + valFmt,
|
||||
valFmt.empty() ? nullptr
|
||||
: nodep->exprp()->cloneTreePure(false))});
|
||||
@@ -360,19 +420,22 @@ class AssertVisitor final : public VNVisitor {
|
||||
// else $error("multiple match");
|
||||
// end
|
||||
AstNodeExpr* const ohot = new AstOneHot{nodep->fileline(), propp};
|
||||
AstConst* const zero = new AstConst{
|
||||
nodep->fileline(), AstConst::WidthedValue{}, propp->width(), 0};
|
||||
AstIf* const ohotIfp
|
||||
= new AstIf{nodep->fileline(), new AstLogNot{nodep->fileline(), ohot}};
|
||||
AstIf* const zeroIfp
|
||||
= new AstIf{nodep->fileline(),
|
||||
new AstLogNot{nodep->fileline(), propp->cloneTreePure(false)}};
|
||||
AstIf* const zeroIfp = new AstIf{
|
||||
nodep->fileline(),
|
||||
new AstEq{nodep->fileline(), propp->cloneTreePure(false), zero}};
|
||||
AstNodeExpr* const exprp = nodep->exprp();
|
||||
const string pragmaStr = nodep->pragmaString();
|
||||
if (!allow_none)
|
||||
zeroIfp->addThensp(newFireAssert(
|
||||
nodep, ASSERT_TYPE_CASE, pragmaStr + ", but none matched" + valFmt,
|
||||
valFmt.empty() ? nullptr : exprp->cloneTreePure(false)));
|
||||
zeroIfp->addThensp(
|
||||
newFireAssert(nodep, VAssertDirectiveType::VIOLATION_CASE, assertType,
|
||||
pragmaStr + ", but none matched" + valFmt,
|
||||
valFmt.empty() ? nullptr : exprp->cloneTreePure(false)));
|
||||
zeroIfp->addElsesp(
|
||||
newFireAssert(nodep, ASSERT_TYPE_CASE,
|
||||
newFireAssert(nodep, VAssertDirectiveType::VIOLATION_CASE, assertType,
|
||||
pragmaStr + ", but multiple matches found" + valFmt,
|
||||
valFmt.empty() ? nullptr : exprp->cloneTreePure(false)));
|
||||
ohotIfp->addThensp(zeroIfp);
|
||||
@@ -440,6 +503,7 @@ class AssertVisitor final : public VNVisitor {
|
||||
AstSampled* const newp = newSampledExpr(nodep);
|
||||
relinkHandle.relink(newp);
|
||||
newp->user1(1);
|
||||
v3Global.setHasSampled();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -467,6 +531,22 @@ class AssertVisitor final : public VNVisitor {
|
||||
} else if (nodep->displayType() == VDisplayType::DT_MONITOR) {
|
||||
nodep->displayType(VDisplayType::DT_DISPLAY);
|
||||
const auto fl = nodep->fileline();
|
||||
AstNode* monExprsp = nodep->fmtp()->exprsp();
|
||||
AstSenItem* monSenItemsp = nullptr;
|
||||
while (monExprsp) {
|
||||
if (AstNodeVarRef* varrefp = VN_CAST(monExprsp, NodeVarRef)) {
|
||||
AstSenItem* const senItemp
|
||||
= new AstSenItem(fl, VEdgeType::ET_CHANGED,
|
||||
new AstVarRef{fl, varrefp->varp(), VAccess::READ});
|
||||
if (!monSenItemsp) {
|
||||
monSenItemsp = senItemp;
|
||||
} else {
|
||||
monSenItemsp->addNext(senItemp);
|
||||
}
|
||||
}
|
||||
monExprsp = monExprsp->nextp();
|
||||
}
|
||||
AstSenTree* const monSenTree = new AstSenTree{fl, monSenItemsp};
|
||||
const auto monNum = ++m_monitorNum;
|
||||
// Where $monitor was we do "__VmonitorNum = N;"
|
||||
const auto newsetp = new AstAssign{fl, newMonitorNumVarRefp(nodep, VAccess::WRITE),
|
||||
@@ -482,7 +562,7 @@ class AssertVisitor final : public VNVisitor {
|
||||
stmtsp};
|
||||
ifp->isBoundsCheck(true); // To avoid LATCH warning
|
||||
ifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||
AstNode* const newp = new AstAlways{fl, VAlwaysKwd::ALWAYS, nullptr, ifp};
|
||||
AstNode* const newp = new AstAlways{fl, VAlwaysKwd::ALWAYS, monSenTree, ifp};
|
||||
m_modp->addStmtsp(newp);
|
||||
} else if (nodep->displayType() == VDisplayType::DT_STROBE) {
|
||||
nodep->displayType(VDisplayType::DT_DISPLAY);
|
||||
@@ -527,24 +607,51 @@ class AssertVisitor final : public VNVisitor {
|
||||
|
||||
iterateChildren(nodep);
|
||||
|
||||
if (const AstConst* const constp = VN_CAST(nodep->controlTypep(), Const)) {
|
||||
nodep->ctlType(constp->toSInt());
|
||||
} else if (nodep->ctlType() == VAssertCtlType::_TO_BE_EVALUATED) {
|
||||
if (!resolveAssertType(nodep)) {
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: non-constant assert assertion-type expression");
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
}
|
||||
if (nodep->ctlAssertTypes() != ALL_ASSERT_TYPES
|
||||
&& nodep->ctlAssertTypes().containsAny(VAssertType::EXPECT | VAssertType::UNIQUE
|
||||
| VAssertType::UNIQUE0
|
||||
| VAssertType::PRIORITY)) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: assert control assertion_type");
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
}
|
||||
if (!resolveControlType(nodep)) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: non-const assert control type expression");
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
}
|
||||
if (!resolveDirectiveType(nodep)) {
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: non-const assert directive type expression");
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
}
|
||||
|
||||
FileLine* const fl = nodep->fileline();
|
||||
switch (nodep->ctlType()) {
|
||||
case VAssertCtlType::ON:
|
||||
UINFO(9, "Generating assertctl for a module: " << m_modp << endl);
|
||||
nodep->replaceWith(new AstCExpr{
|
||||
fl,
|
||||
"vlSymsp->_vm_contextp__->assertOnSet("s + std::to_string(nodep->ctlAssertTypes())
|
||||
+ ", "s + std::to_string(nodep->ctlDirectiveTypes()) + ");\n"s,
|
||||
1});
|
||||
break;
|
||||
case VAssertCtlType::OFF:
|
||||
case VAssertCtlType::KILL: {
|
||||
UINFO(9, "Generating assertctl for a module: " << m_modp << endl);
|
||||
FileLine* const fl = nodep->fileline();
|
||||
const string assertOnStmt
|
||||
= string{"vlSymsp->_vm_contextp__->assertOn("}
|
||||
+ (nodep->ctlType() == VAssertCtlType::ON ? "true" : "false") + ");\n";
|
||||
nodep->replaceWith(new AstCExpr{fl, assertOnStmt, 1});
|
||||
nodep->replaceWith(new AstCExpr{fl,
|
||||
"vlSymsp->_vm_contextp__->assertOnClear("s
|
||||
+ std::to_string(nodep->ctlAssertTypes()) + " ,"s
|
||||
+ std::to_string(nodep->ctlDirectiveTypes())
|
||||
+ ");\n"s,
|
||||
1});
|
||||
break;
|
||||
}
|
||||
case VAssertCtlType::LOCK:
|
||||
|
||||
+87
-14
@@ -57,6 +57,7 @@ private:
|
||||
bool m_inAssign = false; // True if in an AssignNode
|
||||
bool m_inAssignDlyLhs = false; // True if in AssignDly's LHS
|
||||
bool m_inSynchDrive = false; // True if in synchronous drive
|
||||
std::vector<AstVarXRef*> m_xrefsp; // list of xrefs that need name fixup
|
||||
|
||||
// METHODS
|
||||
|
||||
@@ -156,6 +157,8 @@ private:
|
||||
m_clockingp = nodep;
|
||||
UINFO(8, " CLOCKING" << nodep << endl);
|
||||
iterateChildren(nodep);
|
||||
if (nodep->eventp()) nodep->addNextHere(nodep->eventp()->unlinkFrBack());
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
void visit(AstClockingItem* const nodep) override {
|
||||
// Get a ref to the sampled/driven variable
|
||||
@@ -175,21 +178,51 @@ private:
|
||||
AstConst* const skewp = VN_AS(nodep->skewp(), Const);
|
||||
if (skewp->num().isNegative()) skewp->v3error("Skew cannot be negative");
|
||||
AstNodeExpr* const exprp = nodep->exprp();
|
||||
m_clockingp->addVarsp(varp->unlinkFrBack());
|
||||
varp->name(m_clockingp->name() + "__DOT__" + varp->name());
|
||||
m_clockingp->addNextHere(varp->unlinkFrBack());
|
||||
varp->user1p(nodep);
|
||||
if (nodep->direction() == VDirection::OUTPUT) {
|
||||
AstVarRef* const skewedRefp = new AstVarRef{flp, varp, VAccess::READ};
|
||||
skewedRefp->user1(true);
|
||||
AstAssign* const assignp = new AstAssign{flp, exprp->cloneTreePure(false), skewedRefp};
|
||||
exprp->foreach([](AstNodeVarRef* varrefp) {
|
||||
// Prevent confusing BLKANDNBLK warnings on clockvars due to generated assignments
|
||||
varrefp->fileline()->warnOff(V3ErrorCode::BLKANDNBLK, true);
|
||||
});
|
||||
AstVarRef* const skewedReadRefp = new AstVarRef{flp, varp, VAccess::READ};
|
||||
skewedReadRefp->user1(true);
|
||||
// Initialize the clockvar
|
||||
AstVarRef* const skewedWriteRefp = new AstVarRef{flp, varp, VAccess::WRITE};
|
||||
skewedWriteRefp->user1(true);
|
||||
AstInitialStatic* const initClockvarp = new AstInitialStatic{
|
||||
flp, new AstAssign{flp, skewedWriteRefp, exprp->cloneTreePure(false)}};
|
||||
m_modp->addStmtsp(initClockvarp);
|
||||
// A var to keep the previous value of the clockvar
|
||||
AstVar* const prevVarp = new AstVar{
|
||||
flp, VVarType::MODULETEMP, "__Vclocking_prev__" + varp->name(), exprp->dtypep()};
|
||||
prevVarp->lifetime(VLifetime::STATIC);
|
||||
AstInitialStatic* const initPrevClockvarp = new AstInitialStatic{
|
||||
flp, new AstAssign{flp, new AstVarRef{flp, prevVarp, VAccess::WRITE},
|
||||
skewedReadRefp->cloneTreePure(false)}};
|
||||
m_modp->addStmtsp(prevVarp);
|
||||
m_modp->addStmtsp(initPrevClockvarp);
|
||||
// Assign the clockvar to the actual var; only do it if the clockvar's value has
|
||||
// changed
|
||||
AstAssign* const assignp
|
||||
= new AstAssign{flp, exprp->cloneTreePure(false), skewedReadRefp};
|
||||
AstIf* const ifp
|
||||
= new AstIf{flp,
|
||||
new AstNeq{flp, new AstVarRef{flp, prevVarp, VAccess::READ},
|
||||
skewedReadRefp->cloneTreePure(false)},
|
||||
assignp};
|
||||
ifp->addThensp(new AstAssign{flp, new AstVarRef{flp, prevVarp, VAccess::WRITE},
|
||||
skewedReadRefp->cloneTree(false)});
|
||||
if (skewp->isZero()) {
|
||||
// Drive the var in Re-NBA (IEEE 1800-2023 14.16)
|
||||
m_clockingp->addNextHere(new AstAlwaysReactive{
|
||||
flp, new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)}, assignp});
|
||||
flp, new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)}, ifp});
|
||||
} else if (skewp->fileline()->timingOn()) {
|
||||
// Create a fork so that this AlwaysObserved can be retriggered before the
|
||||
// assignment happens. Also then it can be combo, avoiding the need for creating
|
||||
// new triggers.
|
||||
AstFork* const forkp = new AstFork{flp, "", assignp};
|
||||
AstFork* const forkp = new AstFork{flp, "", ifp};
|
||||
forkp->joinType(VJoinType::JOIN_NONE);
|
||||
// Use Observed for this to make sure we do not miss the event
|
||||
m_clockingp->addNextHere(new AstAlwaysObserved{
|
||||
@@ -226,9 +259,8 @@ private:
|
||||
AstSampleQueueDType* const queueDtp
|
||||
= new AstSampleQueueDType{flp, exprp->dtypep()};
|
||||
m_netlistp->typeTablep()->addTypesp(queueDtp);
|
||||
AstVar* const queueVarp = new AstVar{
|
||||
flp, VVarType::MODULETEMP,
|
||||
"__Vqueue__" + m_clockingp->name() + "__DOT__" + varp->name(), queueDtp};
|
||||
AstVar* const queueVarp
|
||||
= new AstVar{flp, VVarType::MODULETEMP, "__Vqueue__" + varp->name(), queueDtp};
|
||||
queueVarp->lifetime(VLifetime::STATIC);
|
||||
m_clockingp->addNextHere(queueVarp);
|
||||
// Create a process like this:
|
||||
@@ -241,7 +273,7 @@ private:
|
||||
m_clockingp->addNextHere(
|
||||
new AstAlways{flp, VAlwaysKwd::ALWAYS, nullptr, pushp->makeStmt()});
|
||||
// Create a process like this:
|
||||
// always @<clocking event> queue.pop(<skew>, /*out*/<skewed var>});
|
||||
// always @<clocking event> queue.pop(<skew>, /*out*/<skewed var>);
|
||||
AstCMethodHard* const popp = new AstCMethodHard{
|
||||
flp, new AstVarRef{flp, queueVarp, VAccess::READWRITE}, "pop",
|
||||
new AstTime{nodep->fileline(), m_modp->timeunit()}};
|
||||
@@ -265,6 +297,7 @@ private:
|
||||
nodep->v3error("Only cycle delays can be used in synchronous drives"
|
||||
" (IEEE 1800-2023 14.16)");
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
return;
|
||||
}
|
||||
if (m_inAssign && !m_inSynchDrive) {
|
||||
@@ -317,9 +350,26 @@ private:
|
||||
}
|
||||
}
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
if (AstClockingItem* const itemp = VN_CAST(nodep->varp()->user1p(), ClockingItem)) {
|
||||
if (nodep->access().isReadOrRW() && !nodep->user1()
|
||||
&& itemp->direction() == VDirection::OUTPUT) {
|
||||
UINFO(8, " -varref: " << nodep << endl);
|
||||
UINFO(8, " -varref-var-back: " << nodep->varp()->backp() << endl);
|
||||
UINFO(8, " -varref-var-user1: " << nodep->varp()->user1p() << endl);
|
||||
if (AstClockingItem* const itemp = VN_CAST(
|
||||
nodep->varp()->user1p() ? nodep->varp()->user1p() : nodep->varp()->firstAbovep(),
|
||||
ClockingItem)) {
|
||||
if (nodep->user1()) return;
|
||||
|
||||
// ensure linking still works, this has to be done only once
|
||||
if (AstVarXRef* xrefp = VN_CAST(nodep, VarXRef)) {
|
||||
UINFO(8, " -clockvarxref-in: " << xrefp << endl);
|
||||
string dotted = xrefp->dotted();
|
||||
const size_t dotPos = dotted.rfind('.');
|
||||
dotted.erase(dotPos, string::npos);
|
||||
xrefp->dotted(dotted);
|
||||
UINFO(8, " -clockvarxref-out: " << xrefp << endl);
|
||||
m_xrefsp.emplace_back(xrefp);
|
||||
}
|
||||
|
||||
if (nodep->access().isReadOrRW() && itemp->direction() == VDirection::OUTPUT) {
|
||||
nodep->v3error("Cannot read from output clockvar (IEEE 1800-2023 14.3)");
|
||||
}
|
||||
if (nodep->access().isWriteOrRW()) {
|
||||
@@ -329,7 +379,28 @@ private:
|
||||
"to clockvars (IEEE 1800-2023 14.16)");
|
||||
}
|
||||
if (m_inAssign) m_inSynchDrive = true;
|
||||
} else if (!nodep->user1() && itemp->direction() == VDirection::INPUT) {
|
||||
} else if (itemp->direction() == VDirection::INPUT) {
|
||||
nodep->v3error("Cannot write to input clockvar (IEEE 1800-2023 14.3)");
|
||||
}
|
||||
}
|
||||
nodep->user1(true);
|
||||
}
|
||||
}
|
||||
void visit(AstMemberSel* nodep) override {
|
||||
if (AstClockingItem* const itemp = VN_CAST(
|
||||
nodep->varp()->user1p() ? nodep->varp()->user1p() : nodep->varp()->firstAbovep(),
|
||||
ClockingItem)) {
|
||||
if (nodep->access().isReadOrRW() && itemp->direction() == VDirection::OUTPUT) {
|
||||
nodep->v3error("Cannot read from output clockvar (IEEE 1800-2023 14.3)");
|
||||
}
|
||||
if (nodep->access().isWriteOrRW()) {
|
||||
if (itemp->direction() == VDirection::OUTPUT) {
|
||||
if (!m_inAssignDlyLhs) {
|
||||
nodep->v3error("Only non-blocking assignments can write "
|
||||
"to clockvars (IEEE 1800-2023 14.16)");
|
||||
}
|
||||
if (m_inAssign) m_inSynchDrive = true;
|
||||
} else if (itemp->direction() == VDirection::INPUT) {
|
||||
nodep->v3error("Cannot write to input clockvar (IEEE 1800-2023 14.3)");
|
||||
}
|
||||
}
|
||||
@@ -496,6 +567,8 @@ public:
|
||||
clearAssertInfo();
|
||||
// Process
|
||||
iterate(nodep);
|
||||
// Fix up varref names
|
||||
for (AstVarXRef* xrefp : m_xrefsp) xrefp->name(xrefp->varp()->name());
|
||||
}
|
||||
~AssertPreVisitor() override = default;
|
||||
};
|
||||
|
||||
+1
-2
@@ -801,7 +801,6 @@ AstNode* AstNode::cloneTreeIter(bool needPure) {
|
||||
<< this->warnMore()
|
||||
<< "... Suggest use a temporary variable in place of this expression");
|
||||
// this->v3fatalSrc("cloneTreePure debug backtrace"); // Comment in to debug where caused
|
||||
// it
|
||||
}
|
||||
AstNode* const newp = this->clone();
|
||||
if (this->m_op1p) newp->op1p(this->m_op1p->cloneTreeIterList(needPure));
|
||||
@@ -1586,7 +1585,7 @@ VCastable AstNode::computeCastable(const AstNodeDType* toDtp, const AstNodeDType
|
||||
const auto castable = computeCastableImp(toDtp, fromDtp, fromConstp);
|
||||
UINFO(9, " castable=" << castable << " for " << toDtp << endl);
|
||||
UINFO(9, " =?= " << fromDtp << endl);
|
||||
UINFO(9, " const= " << fromConstp << endl);
|
||||
if (fromConstp) UINFO(9, " const= " << fromConstp << endl);
|
||||
return castable;
|
||||
}
|
||||
|
||||
|
||||
+138
-18
@@ -330,6 +330,7 @@ public:
|
||||
ILLEGAL,
|
||||
COVERAGE_BLOCK_OFF,
|
||||
HIER_BLOCK,
|
||||
HIER_PARAMS,
|
||||
INLINE_MODULE,
|
||||
NO_INLINE_MODULE,
|
||||
NO_INLINE_TASK,
|
||||
@@ -787,6 +788,7 @@ public:
|
||||
bool isAny() const { return m_e != NONE; }
|
||||
// Looks like inout - "ish" because not identical to being an INOUT
|
||||
bool isInoutish() const { return m_e == INOUT; }
|
||||
bool isInput() const { return m_e == INPUT; }
|
||||
bool isNonOutput() const {
|
||||
return m_e == INPUT || m_e == INOUT || m_e == REF || m_e == CONSTREF;
|
||||
}
|
||||
@@ -1154,6 +1156,101 @@ constexpr bool operator==(VAssertCtlType::en lhs, const VAssertCtlType& rhs) {
|
||||
|
||||
// ######################################################################
|
||||
|
||||
class VAssertDirectiveType final {
|
||||
public:
|
||||
// IEEE 1800-2023 Table 20-7
|
||||
enum en : uint8_t {
|
||||
INTERNAL = 0, // Non IEEE type, for directives to be evaluated from expression.
|
||||
ASSERT = (1 << 0),
|
||||
COVER = (1 << 1),
|
||||
ASSUME = (1 << 2),
|
||||
VIOLATION_CASE = (1 << 3), // Non IEEE type, for case constructs
|
||||
// with unique, unique0 or priority pragmas.
|
||||
VIOLATION_IF = (1 << 4), // Non IEEE type, for if constructs
|
||||
// with unique, unique0 or priority pragmas.
|
||||
INTRINSIC = (1 << 5), // Non IEEE type, for intrinsic assertions.
|
||||
RESTRICT = (1 << 6), // Non IEEE type, for ignored restrict assertions.
|
||||
};
|
||||
enum en m_e;
|
||||
VAssertDirectiveType()
|
||||
: m_e{ASSERT} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
constexpr VAssertDirectiveType(en _e)
|
||||
: m_e{_e} {}
|
||||
explicit VAssertDirectiveType(int _e)
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
const char* ascii() const {
|
||||
static const char* const names[]
|
||||
= {"INTERNAL", "ASSERT", "COVER", "ASSUME",
|
||||
"VIOLATION_CASE", "VIOLATION_IF", "INTRINSIC", "RESTRICT"};
|
||||
return names[m_e];
|
||||
}
|
||||
constexpr operator en() const { return m_e; }
|
||||
};
|
||||
constexpr bool operator==(const VAssertDirectiveType& lhs, const VAssertDirectiveType& rhs) {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
}
|
||||
constexpr bool operator==(const VAssertDirectiveType& lhs, VAssertDirectiveType::en rhs) {
|
||||
return lhs.m_e == rhs;
|
||||
}
|
||||
constexpr bool operator==(VAssertDirectiveType::en lhs, const VAssertDirectiveType& rhs) {
|
||||
return lhs == rhs.m_e;
|
||||
}
|
||||
constexpr VAssertDirectiveType::en operator|(VAssertDirectiveType::en lhs,
|
||||
VAssertDirectiveType::en rhs) {
|
||||
return VAssertDirectiveType::en(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
|
||||
}
|
||||
|
||||
// ######################################################################
|
||||
|
||||
class VAssertType final {
|
||||
public:
|
||||
// IEEE 1800-2023 Table 20-6
|
||||
enum en : uint8_t {
|
||||
INTERNAL = 0, // Non IEEE type, for assertions that should not be controlled.
|
||||
CONCURRENT = (1 << 0),
|
||||
SIMPLE_IMMEDIATE = (1 << 1),
|
||||
OBSERVED_DEFERRED_IMMEDIATE = (1 << 2),
|
||||
FINAL_DEFERRED_IMMEDIATE = (1 << 3),
|
||||
EXPECT = (1 << 4),
|
||||
UNIQUE = (1 << 5),
|
||||
UNIQUE0 = (1 << 6),
|
||||
PRIORITY = (1 << 7),
|
||||
};
|
||||
enum en m_e;
|
||||
VAssertType()
|
||||
: m_e{INTERNAL} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
constexpr VAssertType(en _e)
|
||||
: m_e{_e} {}
|
||||
explicit VAssertType(int _e)
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
bool containsAny(VAssertType other) const { return m_e & other.m_e; }
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"INTERNAL",
|
||||
"CONCURRENT",
|
||||
"SIMPLE_IMMEDIATE",
|
||||
"OBSERVED_DEFERRED_IMMEDIATE",
|
||||
"FINAL_DEFERRED_IMMEDIATE",
|
||||
"EXPECT",
|
||||
"UNIQUE",
|
||||
"UNIQUE0",
|
||||
"PRIORITY"};
|
||||
return names[m_e];
|
||||
}
|
||||
constexpr operator en() const { return m_e; }
|
||||
};
|
||||
constexpr bool operator==(const VAssertType& lhs, const VAssertType& rhs) {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
}
|
||||
constexpr bool operator==(const VAssertType& lhs, VAssertType::en rhs) { return lhs.m_e == rhs; }
|
||||
constexpr bool operator==(VAssertType::en lhs, const VAssertType& rhs) { return lhs == rhs.m_e; }
|
||||
constexpr VAssertType::en operator|(VAssertType::en lhs, VAssertType::en rhs) {
|
||||
return VAssertType::en(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
|
||||
}
|
||||
|
||||
// ######################################################################
|
||||
|
||||
class VCaseType final {
|
||||
public:
|
||||
enum en : uint8_t { CT_CASE, CT_CASEX, CT_CASEZ, CT_CASEINSIDE };
|
||||
@@ -1493,6 +1590,7 @@ public:
|
||||
"ENUM_IMPLICIT", "DYNAMIC_CLASS", "INCOMPATIBLE"};
|
||||
return names[m_e];
|
||||
}
|
||||
bool isAssignable() const { return m_e != UNSUPPORTED && m_e != INCOMPATIBLE; }
|
||||
VCastable()
|
||||
: m_e{UNSUPPORTED} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
@@ -1584,6 +1682,36 @@ public:
|
||||
bool operator==(const VSelfPointerText& other) const { return *m_strp == *other.m_strp; }
|
||||
};
|
||||
|
||||
// ######################################################################
|
||||
// Defines what kind of randomization is done on a variable
|
||||
|
||||
class VRandAttr final {
|
||||
public:
|
||||
enum en : uint8_t {
|
||||
NONE, // Not randomizable
|
||||
RAND, // Has a rand modifier
|
||||
RAND_CYCLIC, // Has a randc modifier
|
||||
RAND_INLINE // Not rand/randc, but used in inline random variable control
|
||||
};
|
||||
enum en m_e;
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"NONE", "RAND", "RANDC", "RAND-INLINE"};
|
||||
return names[m_e];
|
||||
}
|
||||
VRandAttr()
|
||||
: m_e{NONE} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
constexpr VRandAttr(en _e)
|
||||
: m_e{_e} {}
|
||||
constexpr operator en() const { return m_e; }
|
||||
bool isRandomizable() const { return m_e != NONE; }
|
||||
bool isRand() const { return m_e == RAND || m_e == RAND_CYCLIC; }
|
||||
bool isRandC() const { return m_e == RAND_CYCLIC; }
|
||||
};
|
||||
inline std::ostream& operator<<(std::ostream& os, const VRandAttr& rhs) {
|
||||
return os << rhs.ascii();
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// AstNUser - Generic base class for AST User nodes.
|
||||
// - Also used to allow parameter passing up/down iterate calls
|
||||
@@ -2076,8 +2204,8 @@ public:
|
||||
void fileline(FileLine* fl) { m_fileline = fl; }
|
||||
inline bool width1() const;
|
||||
inline int widthInstrs() const;
|
||||
void didWidth(bool flag) { m_flags.didWidth = flag; }
|
||||
bool didWidth() const { return m_flags.didWidth; }
|
||||
void didWidth(bool flag) { m_flags.didWidth = flag; }
|
||||
bool didWidthAndSet() {
|
||||
if (didWidth()) return true;
|
||||
didWidth(true);
|
||||
@@ -2407,7 +2535,7 @@ protected:
|
||||
static void dumpJsonStr(std::ostream& os, const std::string& name, const std::string& val);
|
||||
static void dumpJsonPtr(std::ostream& os, const std::string& name, const AstNode* const valp);
|
||||
|
||||
private:
|
||||
protected:
|
||||
void iterateListBackwardsConst(VNVisitorConst& v);
|
||||
|
||||
// For internal use only.
|
||||
@@ -2525,14 +2653,6 @@ private:
|
||||
template <typename T_Arg, bool Default, typename Callable>
|
||||
inline static bool predicateImpl(ConstCorrectAstNode<T_Arg>* nodep, const Callable& p);
|
||||
|
||||
template <typename T_Callable>
|
||||
struct Arg0NoPointerNoCV final {
|
||||
using Traits = FunctionTraits<T_Callable>;
|
||||
using T_Arg0 = typename Traits::template arg<0>::type;
|
||||
using T_Arg0NoPtr = typename std::remove_pointer<T_Arg0>::type;
|
||||
using type = typename std::remove_cv<T_Arg0NoPtr>::type;
|
||||
};
|
||||
|
||||
public:
|
||||
// Given a callable 'f' that takes a single argument of some AstNode subtype 'T_Node', traverse
|
||||
// the tree rooted at this node, and call 'f' in pre-order on each node that is of type
|
||||
@@ -2543,7 +2663,7 @@ public:
|
||||
// caches in modern CPUs, while it is basically unpredictable for VNVisitor.
|
||||
template <typename Callable>
|
||||
void foreach(Callable&& f) {
|
||||
using T_Node = typename Arg0NoPointerNoCV<Callable>::type;
|
||||
using T_Node = typename FunctionArgNoPointerNoCV<Callable, 0>::type;
|
||||
static_assert(vlstd::is_invocable<Callable, T_Node*>::value
|
||||
&& std::is_base_of<AstNode, T_Node>::value,
|
||||
"Callable 'f' must have a signature compatible with 'void(T_Node*)', "
|
||||
@@ -2554,7 +2674,7 @@ public:
|
||||
// Same as above, but for 'const' nodes
|
||||
template <typename Callable>
|
||||
void foreach(Callable&& f) const {
|
||||
using T_Node = typename Arg0NoPointerNoCV<Callable>::type;
|
||||
using T_Node = typename FunctionArgNoPointerNoCV<Callable, 0>::type;
|
||||
static_assert(vlstd::is_invocable<Callable, const T_Node*>::value
|
||||
&& std::is_base_of<AstNode, T_Node>::value,
|
||||
"Callable 'f' must have a signature compatible with 'void(const T_Node*)', "
|
||||
@@ -2565,7 +2685,7 @@ public:
|
||||
// Same as 'foreach' but also traverses 'this->nextp()' transitively
|
||||
template <typename Callable>
|
||||
void foreachAndNext(Callable&& f) {
|
||||
using T_Node = typename Arg0NoPointerNoCV<Callable>::type;
|
||||
using T_Node = typename FunctionArgNoPointerNoCV<Callable, 0>::type;
|
||||
static_assert(vlstd::is_invocable<Callable, T_Node*>::value
|
||||
&& std::is_base_of<AstNode, T_Node>::value,
|
||||
"Callable 'f' must have a signature compatible with 'void(T_Node*)', "
|
||||
@@ -2576,7 +2696,7 @@ public:
|
||||
// Same as above, but for 'const' nodes
|
||||
template <typename Callable>
|
||||
void foreachAndNext(Callable&& f) const {
|
||||
using T_Node = typename Arg0NoPointerNoCV<Callable>::type;
|
||||
using T_Node = typename FunctionArgNoPointerNoCV<Callable, 0>::type;
|
||||
static_assert(vlstd::is_invocable<Callable, const T_Node*>::value
|
||||
&& std::is_base_of<AstNode, T_Node>::value,
|
||||
"Callable 'f' must have a signature compatible with 'void(const T_Node*)', "
|
||||
@@ -2591,7 +2711,7 @@ public:
|
||||
// be determined.
|
||||
template <typename Callable>
|
||||
bool exists(Callable&& p) {
|
||||
using T_Node = typename Arg0NoPointerNoCV<Callable>::type;
|
||||
using T_Node = typename FunctionArgNoPointerNoCV<Callable, 0>::type;
|
||||
static_assert(vlstd::is_invocable_r<bool, Callable, T_Node*>::value
|
||||
&& std::is_base_of<AstNode, T_Node>::value,
|
||||
"Predicate 'p' must have a signature compatible with 'bool(T_Node*)', "
|
||||
@@ -2602,7 +2722,7 @@ public:
|
||||
// Same as above, but for 'const' nodes
|
||||
template <typename Callable>
|
||||
bool exists(Callable&& p) const {
|
||||
using T_Node = typename Arg0NoPointerNoCV<Callable>::type;
|
||||
using T_Node = typename FunctionArgNoPointerNoCV<Callable, 0>::type;
|
||||
static_assert(vlstd::is_invocable_r<bool, Callable, const T_Node*>::value
|
||||
&& std::is_base_of<AstNode, T_Node>::value,
|
||||
"Predicate 'p' must have a signature compatible with 'bool(const T_Node*)', "
|
||||
@@ -2616,7 +2736,7 @@ public:
|
||||
// in some arbitrary order and is terminated as soon as the result can be determined.
|
||||
template <typename Callable>
|
||||
bool forall(Callable&& p) {
|
||||
using T_Node = typename Arg0NoPointerNoCV<Callable>::type;
|
||||
using T_Node = typename FunctionArgNoPointerNoCV<Callable, 0>::type;
|
||||
static_assert(vlstd::is_invocable_r<bool, Callable, T_Node*>::value
|
||||
&& std::is_base_of<AstNode, T_Node>::value,
|
||||
"Predicate 'p' must have a signature compatible with 'bool(T_Node*)', "
|
||||
@@ -2627,7 +2747,7 @@ public:
|
||||
// Same as above, but for 'const' nodes
|
||||
template <typename Callable>
|
||||
bool forall(Callable&& p) const {
|
||||
using T_Node = typename Arg0NoPointerNoCV<Callable>::type;
|
||||
using T_Node = typename FunctionArgNoPointerNoCV<Callable, 0>::type;
|
||||
static_assert(vlstd::is_invocable_r<bool, Callable, const T_Node*>::value
|
||||
&& std::is_base_of<AstNode, T_Node>::value,
|
||||
"Predicate 'p' must have a signature compatible with 'bool(const T_Node*)', "
|
||||
|
||||
@@ -160,6 +160,10 @@ AstCExpr::AstCExpr(FileLine* fl, const string& textStmt, int setwidth, bool clea
|
||||
|
||||
AstVarRef::AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarRef(fl, varp, access) {}
|
||||
AstVarRef::AstVarRef(FileLine* fl, AstNodeModule* pkgp, AstVar* varp, const VAccess& access)
|
||||
: AstVarRef{fl, varp, access} {
|
||||
classOrPackagep(pkgp);
|
||||
}
|
||||
// This form only allowed post-link (see above)
|
||||
AstVarRef::AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarRef(fl, varscp->varp(), access) {
|
||||
|
||||
@@ -219,6 +219,7 @@ public:
|
||||
bool isCompound() const override { return !packed(); }
|
||||
// For basicp() we reuse the size to indicate a "fake" basic type of same size
|
||||
AstBasicDType* basicp() const override {
|
||||
if (!m_packed) return nullptr;
|
||||
return (isFourstate()
|
||||
? VN_AS(findLogicRangeDType(VNumRange{width() - 1, 0}, width(), numeric()),
|
||||
BasicDType)
|
||||
@@ -875,13 +876,13 @@ public:
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
|
||||
int widthAlignBytes() const override { return 1; }
|
||||
int widthTotalBytes() const override { return 1; }
|
||||
int widthAlignBytes() const override { return 0; }
|
||||
int widthTotalBytes() const override { return 0; }
|
||||
bool isVirtual() const { return m_virtual; }
|
||||
void isVirtual(bool flag) {
|
||||
m_virtual = flag;
|
||||
if (flag) v3Global.setHasVirtIfaces();
|
||||
}
|
||||
bool isVirtual() const { return m_virtual; }
|
||||
FileLine* modportFileline() const { return m_modportFileline; }
|
||||
string cellName() const { return m_cellName; }
|
||||
void cellName(const string& name) { m_cellName = name; }
|
||||
@@ -956,8 +957,8 @@ public:
|
||||
int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); }
|
||||
// METHODS
|
||||
void name(const string& name) override { m_name = name; }
|
||||
void tag(const string& text) override { m_tag = text; }
|
||||
string tag() const override { return m_tag; }
|
||||
void tag(const string& text) override { m_tag = text; }
|
||||
int lsb() const { return m_lsb; }
|
||||
void lsb(int lsb) { m_lsb = lsb; }
|
||||
bool isCompound() const override {
|
||||
|
||||
+140
-76
@@ -213,8 +213,8 @@ public:
|
||||
bool isOutputter() override { return !isPure(); }
|
||||
AstCFunc* funcp() const { return m_funcp; }
|
||||
void funcp(AstCFunc* funcp) { m_funcp = funcp; }
|
||||
void argTypes(const string& str) { m_argTypes = str; }
|
||||
string argTypes() const { return m_argTypes; }
|
||||
void argTypes(const string& str) { m_argTypes = str; }
|
||||
|
||||
string emitVerilog() final override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() final override { V3ERROR_NA_RETURN(""); }
|
||||
@@ -420,6 +420,7 @@ public:
|
||||
const V3Number& ths) override;
|
||||
string emitVerilog() override { return "%k(%l %f? %r %k: %t)"; }
|
||||
string emitC() override { return "VL_COND_%nq%lq%rq%tq(%nw, %P, %li, %ri, %ti)"; }
|
||||
string emitSMT() const override { return "(ite %l %r %t)"; }
|
||||
bool cleanOut() const override { return false; } // clean if e1 & e2 clean
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return false; }
|
||||
@@ -809,19 +810,38 @@ class AstConsDynArray final : public AstNodeExpr {
|
||||
// Construct a queue and return object, '{}. '{lhs}, '{lhs. rhs}
|
||||
// @astgen op1 := lhsp : Optional[AstNode]
|
||||
// @astgen op2 := rhsp : Optional[AstNode]
|
||||
const bool m_lhsIsValue = false; // LHS constructs value inside the queue, not concat
|
||||
const bool m_rhsIsValue = false; // RHS constructs value inside the queue, not concat
|
||||
public:
|
||||
explicit AstConsDynArray(FileLine* fl, AstNode* lhsp = nullptr, AstNode* rhsp = nullptr)
|
||||
: ASTGEN_SUPER_ConsDynArray(fl) {
|
||||
explicit AstConsDynArray(FileLine* fl)
|
||||
: ASTGEN_SUPER_ConsDynArray(fl) {}
|
||||
explicit AstConsDynArray(FileLine* fl, bool lhsIsValue, AstNode* lhsp)
|
||||
: ASTGEN_SUPER_ConsDynArray(fl)
|
||||
, m_lhsIsValue(lhsIsValue) {
|
||||
this->lhsp(lhsp);
|
||||
}
|
||||
explicit AstConsDynArray(FileLine* fl, bool lhsIsValue, AstNode* lhsp, bool rhsIsValue,
|
||||
AstNode* rhsp)
|
||||
: ASTGEN_SUPER_ConsDynArray(fl)
|
||||
, m_lhsIsValue(lhsIsValue)
|
||||
, m_rhsIsValue(rhsIsValue) {
|
||||
this->lhsp(lhsp);
|
||||
this->rhsp(rhsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstConsDynArray;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string emitVerilog() override { return "'{%l, %r}"; }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstConsDynArray* const sp = VN_DBG_AS(samep, ConsDynArray);
|
||||
return m_lhsIsValue == sp->m_lhsIsValue && m_rhsIsValue == sp->m_rhsIsValue;
|
||||
}
|
||||
bool lhsIsValue() const { return m_lhsIsValue; }
|
||||
bool rhsIsValue() const { return m_rhsIsValue; }
|
||||
};
|
||||
class AstConsPackMember final : public AstNodeExpr {
|
||||
// Construct a packed array single emement [member1: value1]
|
||||
@@ -872,19 +892,38 @@ class AstConsQueue final : public AstNodeExpr {
|
||||
// Construct a queue and return object, '{}. '{lhs}, '{lhs. rhs}
|
||||
// @astgen op1 := lhsp : Optional[AstNode]
|
||||
// @astgen op2 := rhsp : Optional[AstNode]
|
||||
const bool m_lhsIsValue = false; // LHS constructs value inside the queue, not concat
|
||||
const bool m_rhsIsValue = false; // RHS constructs value inside the queue, not concat
|
||||
public:
|
||||
explicit AstConsQueue(FileLine* fl, AstNode* lhsp = nullptr, AstNode* rhsp = nullptr)
|
||||
: ASTGEN_SUPER_ConsQueue(fl) {
|
||||
explicit AstConsQueue(FileLine* fl)
|
||||
: ASTGEN_SUPER_ConsQueue(fl) {}
|
||||
explicit AstConsQueue(FileLine* fl, bool lhsIsValue, AstNode* lhsp)
|
||||
: ASTGEN_SUPER_ConsQueue(fl)
|
||||
, m_lhsIsValue(lhsIsValue) {
|
||||
this->lhsp(lhsp);
|
||||
}
|
||||
explicit AstConsQueue(FileLine* fl, bool lhsIsValue, AstNode* lhsp, bool rhsIsValue,
|
||||
AstNode* rhsp)
|
||||
: ASTGEN_SUPER_ConsQueue(fl)
|
||||
, m_lhsIsValue(lhsIsValue)
|
||||
, m_rhsIsValue(rhsIsValue) {
|
||||
this->lhsp(lhsp);
|
||||
this->rhsp(rhsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstConsQueue;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string emitVerilog() override { return "'{%l, %r}"; }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstConsQueue* const sp = VN_DBG_AS(samep, ConsQueue);
|
||||
return m_lhsIsValue == sp->m_lhsIsValue && m_rhsIsValue == sp->m_rhsIsValue;
|
||||
}
|
||||
bool lhsIsValue() const { return m_lhsIsValue; }
|
||||
bool rhsIsValue() const { return m_rhsIsValue; }
|
||||
};
|
||||
class AstConsWildcard final : public AstNodeExpr {
|
||||
// Construct a wildcard assoc array and return object, '{}
|
||||
@@ -1063,83 +1102,89 @@ public:
|
||||
};
|
||||
class AstConstraintRef final : public AstNodeExpr {
|
||||
// A reference to a constraint identifier
|
||||
// Not saving pointer to constraint yet, as constraint_mode is an unsupported construct
|
||||
// @astgen op4 := scopeNamep : Optional[AstScopeName]
|
||||
// @astgen op1 := fromp : Optional[AstNodeExpr]
|
||||
//
|
||||
// @astgen ptr := m_constrp : AstConstraint // The constraint pointed to
|
||||
// @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Class/package of constraint
|
||||
string m_name; // Name of constraint
|
||||
|
||||
public:
|
||||
AstConstraintRef(FileLine* fl, const string& name)
|
||||
: ASTGEN_SUPER_ConstraintRef(fl)
|
||||
, m_name{name} {
|
||||
AstConstraintRef(FileLine* fl, AstNodeExpr* fromp, AstConstraint* constrp)
|
||||
: ASTGEN_SUPER_ConstraintRef(fl) {
|
||||
this->fromp(fromp);
|
||||
this->constrp(constrp);
|
||||
dtypep(findConstraintRefDType());
|
||||
}
|
||||
ASTGEN_MEMBERS_AstConstraintRef;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
void name(const string& name) override { m_name = name; }
|
||||
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
|
||||
string name() const override VL_MT_STABLE;
|
||||
AstConstraint* constrp() const VL_MT_STABLE { return m_constrp; }
|
||||
void constrp(AstConstraint* nodep) { m_constrp = nodep; }
|
||||
AstNodeModule* classOrPackagep() const VL_MT_STABLE { return m_classOrPackagep; }
|
||||
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
|
||||
|
||||
string emitVerilog() final override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() final override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const final override { V3ERROR_NA_RETURN(true); }
|
||||
};
|
||||
class AstCvtDynArrayToPacked final : public AstNodeExpr {
|
||||
class AstCvtArrayToPacked final : public AstNodeExpr {
|
||||
// Cast from dynamic queue data type to packed array
|
||||
// @astgen op1 := fromp : AstNodeExpr
|
||||
public:
|
||||
AstCvtDynArrayToPacked(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_CvtDynArrayToPacked(fl) {
|
||||
AstCvtArrayToPacked(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_CvtArrayToPacked(fl) {
|
||||
this->fromp(fromp);
|
||||
dtypeFrom(dtp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCvtDynArrayToPacked;
|
||||
ASTGEN_MEMBERS_AstCvtArrayToPacked;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { return "VL_PACK_%nq%rq(%nw, %rw, %P, %li)"; }
|
||||
bool cleanOut() const override { return true; }
|
||||
};
|
||||
class AstCvtPackedToArray final : public AstNodeExpr {
|
||||
// Cast from packed array to dynamic/unpacked queue data type
|
||||
// @astgen op1 := fromp : AstNodeExpr
|
||||
public:
|
||||
AstCvtPackedToArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_CvtPackedToArray(fl) {
|
||||
this->fromp(fromp);
|
||||
dtypeFrom(dtp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCvtPackedToArray;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
};
|
||||
class AstCvtPackedToDynArray final : public AstNodeExpr {
|
||||
// Cast from packed array to dynamic queue data type
|
||||
// @astgen op1 := fromp : AstNodeExpr
|
||||
class AstDist final : public AstNodeExpr {
|
||||
// @astgen op1 := exprp : AstNodeExpr
|
||||
// @astgen op2 := itemsp : List[AstDistItem]
|
||||
public:
|
||||
AstCvtPackedToDynArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_CvtPackedToDynArray(fl) {
|
||||
this->fromp(fromp);
|
||||
dtypeFrom(dtp);
|
||||
AstDist(FileLine* fl, AstNodeExpr* exprp, AstDistItem* itemsp)
|
||||
: ASTGEN_SUPER_Inside(fl) {
|
||||
this->exprp(exprp);
|
||||
this->addItemsp(itemsp);
|
||||
dtypeSetBit();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCvtPackedToDynArray;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
ASTGEN_MEMBERS_AstDist;
|
||||
string emitVerilog() override { return "%l dist { %r }"; }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanOut() const override { return false; } // NA
|
||||
};
|
||||
class AstCvtPackedToUnpackArray final : public AstNodeExpr {
|
||||
// Cast from packed array to dynamic queue data type
|
||||
// @astgen op1 := fromp : AstNodeExpr
|
||||
class AstDistItem final : public AstNodeExpr {
|
||||
// Constraint distribution item
|
||||
// @astgen op1 := rangep : AstNodeExpr
|
||||
// @astgen op2 := weightp : AstNodeExpr
|
||||
bool m_isWhole = false; // True for weight ':/', false for ':='
|
||||
public:
|
||||
AstCvtPackedToUnpackArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_CvtPackedToUnpackArray(fl) {
|
||||
this->fromp(fromp);
|
||||
dtypeFrom(dtp);
|
||||
AstDistItem(FileLine* fl, AstNodeExpr* rangep, AstNodeExpr* weightp)
|
||||
: ASTGEN_SUPER_DistItem(fl) {
|
||||
this->rangep(rangep);
|
||||
this->weightp(weightp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCvtPackedToUnpackArray;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
ASTGEN_MEMBERS_AstDistItem;
|
||||
string emitVerilog() override { return "%l "s + (m_isWhole ? ":/" : ":=") + " %r"; }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
};
|
||||
class AstCvtUnpackArrayToPacked final : public AstNodeExpr {
|
||||
// Cast from dynamic queue data type to packed array
|
||||
// @astgen op1 := fromp : AstNodeExpr
|
||||
public:
|
||||
AstCvtUnpackArrayToPacked(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_CvtUnpackArrayToPacked(fl) {
|
||||
this->fromp(fromp);
|
||||
dtypeFrom(dtp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCvtUnpackArrayToPacked;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanOut() const override { return false; } // NA
|
||||
void isWhole(bool flag) { m_isWhole = flag; }
|
||||
bool isWhole() const { return m_isWhole; }
|
||||
};
|
||||
class AstDot final : public AstNodeExpr {
|
||||
// A dot separating paths in an AstVarXRef, AstFuncRef or AstTaskRef
|
||||
@@ -1531,6 +1576,9 @@ class AstLambdaArgRef final : public AstNodeExpr {
|
||||
// Lambda argument usage
|
||||
// These are not AstVarRefs because we need to be able to delete/clone lambdas during
|
||||
// optimizations and AstVar's are painful to remove.
|
||||
ASTGEN_MEMBERS_AstLambdaArgRef;
|
||||
|
||||
private:
|
||||
string m_name; // Name of variable
|
||||
bool m_index; // Index, not value
|
||||
|
||||
@@ -1539,7 +1587,6 @@ public:
|
||||
: ASTGEN_SUPER_LambdaArgRef(fl)
|
||||
, m_name{name}
|
||||
, m_index(index) {}
|
||||
ASTGEN_MEMBERS_AstLambdaArgRef;
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
string emitVerilog() override { return name(); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
@@ -1563,12 +1610,7 @@ public:
|
||||
this->fromp(fromp);
|
||||
dtypep(nullptr); // V3Width will resolve
|
||||
}
|
||||
AstMemberSel(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_MemberSel(fl)
|
||||
, m_name{dtp->name()} {
|
||||
this->fromp(fromp);
|
||||
dtypep(dtp);
|
||||
}
|
||||
AstMemberSel(FileLine* fl, AstNodeExpr* fromp, AstVar* varp);
|
||||
ASTGEN_MEMBERS_AstMemberSel;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
@@ -1744,13 +1786,27 @@ public:
|
||||
: (m_urandom ? "%f$urandom()" : "%f$random()");
|
||||
}
|
||||
string emitC() override {
|
||||
return m_reset ? "VL_RAND_RESET_%nq(%nw, %P)"
|
||||
: seedp()
|
||||
// cppcheck-has-bug-suppress knownConditionTrueFalse
|
||||
? (urandom() ? "VL_URANDOM_SEEDED_%nq%lq(%li)" //
|
||||
: "VL_RANDOM_SEEDED_%nq%lq(%li)")
|
||||
: (isWide() ? "VL_RANDOM_%nq(%nw, %P)" //
|
||||
: "VL_RANDOM_%nq()");
|
||||
if (m_reset) {
|
||||
if (v3Global.opt.xAssign() == "unique") {
|
||||
return "VL_RAND_RESET_ASSIGN_%nq(%nw, %P)";
|
||||
} else {
|
||||
// This follows xInitial randomization
|
||||
return "VL_RAND_RESET_%nq(%nw, %P)";
|
||||
}
|
||||
}
|
||||
if (seedp()) {
|
||||
if (urandom()) {
|
||||
return "VL_URANDOM_SEEDED_%nq%lq(%li)";
|
||||
} else {
|
||||
return "VL_RANDOM_SEEDED_%nq%lq(%li)";
|
||||
}
|
||||
}
|
||||
|
||||
if (isWide()) {
|
||||
return "VL_RANDOM_%nq(%nw, %P)";
|
||||
} else {
|
||||
return "VL_RANDOM_%nq()";
|
||||
}
|
||||
}
|
||||
bool cleanOut() const override { return false; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
@@ -1847,11 +1903,11 @@ public:
|
||||
return (name().find("%m") != string::npos || name().find("%M") != string::npos);
|
||||
}
|
||||
bool hidden() const { return m_hidden; }
|
||||
void hasFormat(bool flag) { m_hasFormat = flag; }
|
||||
bool hasFormat() const { return m_hasFormat; }
|
||||
void hasFormat(bool flag) { m_hasFormat = flag; }
|
||||
char missingArgChar() const { return m_missingArgChar; }
|
||||
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
|
||||
VTimescale timeunit() const { return m_timeunit; }
|
||||
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
|
||||
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
@@ -2135,6 +2191,10 @@ public:
|
||||
: ASTGEN_SUPER_ThisRef(fl) {
|
||||
childDTypep(dtypep);
|
||||
}
|
||||
AstThisRef(FileLine* fl, AstClassRefDType* dtypep)
|
||||
: ASTGEN_SUPER_ThisRef(fl) {
|
||||
this->dtypep(dtypep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstThisRef;
|
||||
string emitC() override { return "this"; }
|
||||
string emitVerilog() override { return "this"; }
|
||||
@@ -2173,8 +2233,8 @@ public:
|
||||
bool cleanOut() const override { return true; }
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
|
||||
VTimescale timeunit() const { return m_timeunit; }
|
||||
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
|
||||
};
|
||||
class AstUCFunc final : public AstNodeExpr {
|
||||
// User's $c function
|
||||
@@ -4197,7 +4257,7 @@ public:
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
int instrCount() const override { return widthInstrs() * 2; }
|
||||
};
|
||||
@@ -4218,7 +4278,7 @@ public:
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return false; }
|
||||
bool cleanRhs() const override { return false; }
|
||||
bool sizeMattersLhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
int instrCount() const override { return widthInstrs() * 2; }
|
||||
};
|
||||
@@ -4439,8 +4499,8 @@ public:
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
|
||||
VTimescale timeunit() const { return m_timeunit; }
|
||||
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
|
||||
};
|
||||
class AstTimeD final : public AstNodeTermop {
|
||||
VTimescale m_timeunit; // Parent module time unit
|
||||
@@ -4460,8 +4520,8 @@ public:
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
|
||||
VTimescale timeunit() const { return m_timeunit; }
|
||||
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
|
||||
};
|
||||
|
||||
// === AstNodeTriop ===
|
||||
@@ -4619,6 +4679,7 @@ public:
|
||||
: isWide() ? "VL_SEL_%nq%lq%rq%tq(%nw,%lw, %P, %li, %ri, %ti)"
|
||||
: "VL_SEL_%nq%lq%rq%tq(%lw, %P, %li, %ri, %ti)";
|
||||
}
|
||||
string emitSMT() const override { return "((_ extract %t %r) %l)"; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
@@ -5355,8 +5416,8 @@ public:
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
|
||||
VTimescale timeunit() const { return m_timeunit; }
|
||||
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
|
||||
};
|
||||
class AstToLowerN final : public AstNodeUniop {
|
||||
// string.tolower()
|
||||
@@ -5616,6 +5677,8 @@ public:
|
||||
// This form only allowed post-link because output/wire compression may
|
||||
// lead to deletion of AstVar's
|
||||
inline AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access);
|
||||
inline AstVarRef(FileLine* fl, AstNodeModule* classOrPackagep, AstVar* varp,
|
||||
const VAccess& access);
|
||||
// This form only allowed post-link (see above)
|
||||
inline AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access);
|
||||
ASTGEN_MEMBERS_AstVarRef;
|
||||
@@ -5645,6 +5708,7 @@ public:
|
||||
inline AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access);
|
||||
ASTGEN_MEMBERS_AstVarXRef;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
void name(const std::string& name) override { m_name = name; } // * = Var name
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string dotted() const { return m_dotted; }
|
||||
|
||||
+134
-90
@@ -138,48 +138,48 @@ public:
|
||||
void dpiOpenParentInc() { ++m_dpiOpenParent; }
|
||||
void dpiOpenParentClear() { m_dpiOpenParent = 0; }
|
||||
uint64_t dpiOpenParent() const { return m_dpiOpenParent; }
|
||||
void taskPublic(bool flag) { m_taskPublic = flag; }
|
||||
bool taskPublic() const { return m_taskPublic; }
|
||||
void attrIsolateAssign(bool flag) { m_attrIsolateAssign = flag; }
|
||||
void taskPublic(bool flag) { m_taskPublic = flag; }
|
||||
bool attrIsolateAssign() const { return m_attrIsolateAssign; }
|
||||
void classMethod(bool flag) { m_classMethod = flag; }
|
||||
void attrIsolateAssign(bool flag) { m_attrIsolateAssign = flag; }
|
||||
bool classMethod() const { return m_classMethod; }
|
||||
void isExternProto(bool flag) { m_externProto = flag; }
|
||||
void classMethod(bool flag) { m_classMethod = flag; }
|
||||
bool isExternProto() const { return m_externProto; }
|
||||
void isExternDef(bool flag) { m_externDef = flag; }
|
||||
void isExternProto(bool flag) { m_externProto = flag; }
|
||||
bool isExternDef() const { return m_externDef; }
|
||||
void prototype(bool flag) { m_prototype = flag; }
|
||||
void isExternDef(bool flag) { m_externDef = flag; }
|
||||
bool prototype() const { return m_prototype; }
|
||||
void dpiExport(bool flag) { m_dpiExport = flag; }
|
||||
void prototype(bool flag) { m_prototype = flag; }
|
||||
bool dpiExport() const { return m_dpiExport; }
|
||||
void dpiImport(bool flag) { m_dpiImport = flag; }
|
||||
void dpiExport(bool flag) { m_dpiExport = flag; }
|
||||
bool dpiImport() const { return m_dpiImport; }
|
||||
void dpiContext(bool flag) { m_dpiContext = flag; }
|
||||
void dpiImport(bool flag) { m_dpiImport = flag; }
|
||||
bool dpiContext() const { return m_dpiContext; }
|
||||
void dpiOpenChild(bool flag) { m_dpiOpenChild = flag; }
|
||||
void dpiContext(bool flag) { m_dpiContext = flag; }
|
||||
bool dpiOpenChild() const { return m_dpiOpenChild; }
|
||||
void dpiTask(bool flag) { m_dpiTask = flag; }
|
||||
void dpiOpenChild(bool flag) { m_dpiOpenChild = flag; }
|
||||
bool dpiTask() const { return m_dpiTask; }
|
||||
void isConstructor(bool flag) { m_isConstructor = flag; }
|
||||
void dpiTask(bool flag) { m_dpiTask = flag; }
|
||||
bool isConstructor() const { return m_isConstructor; }
|
||||
void isConstructor(bool flag) { m_isConstructor = flag; }
|
||||
bool isHideLocal() const { return m_isHideLocal; }
|
||||
void isHideLocal(bool flag) { m_isHideLocal = flag; }
|
||||
bool isHideProtected() const { return m_isHideProtected; }
|
||||
void isHideProtected(bool flag) { m_isHideProtected = flag; }
|
||||
void dpiPure(bool flag) { m_dpiPure = flag; }
|
||||
bool dpiPure() const { return m_dpiPure; }
|
||||
void pureVirtual(bool flag) { m_pureVirtual = flag; }
|
||||
void dpiPure(bool flag) { m_dpiPure = flag; }
|
||||
bool pureVirtual() const { return m_pureVirtual; }
|
||||
void recursive(bool flag) { m_recursive = flag; }
|
||||
void pureVirtual(bool flag) { m_pureVirtual = flag; }
|
||||
bool recursive() const { return m_recursive; }
|
||||
void isStatic(bool flag) { m_static = flag; }
|
||||
void recursive(bool flag) { m_recursive = flag; }
|
||||
bool isStatic() const { return m_static; }
|
||||
void underGenerate(bool flag) { m_underGenerate = flag; }
|
||||
void isStatic(bool flag) { m_static = flag; }
|
||||
bool underGenerate() const { return m_underGenerate; }
|
||||
void isVirtual(bool flag) { m_virtual = flag; }
|
||||
void underGenerate(bool flag) { m_underGenerate = flag; }
|
||||
bool isVirtual() const { return m_virtual; }
|
||||
void setNeedProcess() { m_needProcess = true; }
|
||||
void isVirtual(bool flag) { m_virtual = flag; }
|
||||
bool needProcess() const { return m_needProcess; }
|
||||
void setNeedProcess() { m_needProcess = true; }
|
||||
void baseOverride(const VBaseOverride& flag) { m_baseOverride = flag; }
|
||||
VBaseOverride baseOverride() const { return m_baseOverride; }
|
||||
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
|
||||
@@ -238,6 +238,7 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
|
||||
bool m_hasGParam : 1; // Has global parameter (for link)
|
||||
bool m_hasParameterList : 1; // Has #() for parameter declaration
|
||||
bool m_hierBlock : 1; // Hierarchical Block marked by HIER_BLOCK pragma
|
||||
bool m_hierParams : 1; // Block containing params for parametrized hier blocks
|
||||
bool m_internal : 1; // Internally created
|
||||
bool m_recursive : 1; // Recursive module
|
||||
bool m_recursiveClone : 1; // If recursive, what module it clones, otherwise nullptr
|
||||
@@ -253,6 +254,7 @@ protected:
|
||||
, m_hasGParam{false}
|
||||
, m_hasParameterList{false}
|
||||
, m_hierBlock{false}
|
||||
, m_hierParams{false}
|
||||
, m_internal{false}
|
||||
, m_recursive{false}
|
||||
, m_recursiveClone{false} {}
|
||||
@@ -274,30 +276,32 @@ public:
|
||||
void level(int level) { m_level = level; }
|
||||
int level() const VL_MT_SAFE { return m_level; }
|
||||
bool isTop() const VL_MT_SAFE { return level() == 1; }
|
||||
void modPublic(bool flag) { m_modPublic = flag; }
|
||||
bool modPublic() const { return m_modPublic; }
|
||||
void modTrace(bool flag) { m_modTrace = flag; }
|
||||
void modPublic(bool flag) { m_modPublic = flag; }
|
||||
bool modTrace() const { return m_modTrace; }
|
||||
void dead(bool flag) { m_dead = flag; }
|
||||
void modTrace(bool flag) { m_modTrace = flag; }
|
||||
bool dead() const { return m_dead; }
|
||||
void hasGParam(bool flag) { m_hasGParam = flag; }
|
||||
void dead(bool flag) { m_dead = flag; }
|
||||
bool hasGParam() const { return m_hasGParam; }
|
||||
void hasParameterList(bool flag) { m_hasParameterList = flag; }
|
||||
void hasGParam(bool flag) { m_hasGParam = flag; }
|
||||
bool hasParameterList() const { return m_hasParameterList; }
|
||||
void hierBlock(bool flag) { m_hierBlock = flag; }
|
||||
void hasParameterList(bool flag) { m_hasParameterList = flag; }
|
||||
bool hierBlock() const { return m_hierBlock; }
|
||||
void internal(bool flag) { m_internal = flag; }
|
||||
void hierBlock(bool flag) { m_hierBlock = flag; }
|
||||
bool hierParams() const { return m_hierParams; }
|
||||
void hierParams(bool flag) { m_hierParams = flag; }
|
||||
bool internal() const { return m_internal; }
|
||||
void recursive(bool flag) { m_recursive = flag; }
|
||||
void internal(bool flag) { m_internal = flag; }
|
||||
bool recursive() const { return m_recursive; }
|
||||
void recursive(bool flag) { m_recursive = flag; }
|
||||
void recursiveClone(bool flag) { m_recursiveClone = flag; }
|
||||
bool recursiveClone() const { return m_recursiveClone; }
|
||||
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
|
||||
VLifetime lifetime() const { return m_lifetime; }
|
||||
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
|
||||
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
|
||||
VTimescale timeunit() const { return m_timeunit; }
|
||||
void unconnectedDrive(const VOptionBool flag) { m_unconnectedDrive = flag; }
|
||||
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
|
||||
VOptionBool unconnectedDrive() const { return m_unconnectedDrive; }
|
||||
void unconnectedDrive(const VOptionBool flag) { m_unconnectedDrive = flag; }
|
||||
};
|
||||
class AstNodeProcedure VL_NOT_FINAL : public AstNode {
|
||||
// IEEE procedure: initial, final, always
|
||||
@@ -398,13 +402,16 @@ class AstNodeCoverOrAssert VL_NOT_FINAL : public AstNodeStmt {
|
||||
// op3 used by some sub-types only
|
||||
// @astgen op4 := passsp: List[AstNode] // Statements when propp is passing/truthly
|
||||
string m_name; // Name to report
|
||||
const bool m_immediate; // Immediate assertion/cover
|
||||
const VAssertType m_type; // Assertion/cover type
|
||||
const VAssertDirectiveType m_directive; // Assertion directive type
|
||||
|
||||
public:
|
||||
AstNodeCoverOrAssert(VNType t, FileLine* fl, AstNode* propp, AstNode* passsp, bool immediate,
|
||||
const string& name = "")
|
||||
AstNodeCoverOrAssert(VNType t, FileLine* fl, AstNode* propp, AstNode* passsp, VAssertType type,
|
||||
VAssertDirectiveType directive, const string& name = "")
|
||||
: AstNodeStmt{t, fl}
|
||||
, m_name{name}
|
||||
, m_immediate{immediate} {
|
||||
, m_type{type}
|
||||
, m_directive{directive} {
|
||||
this->propp(propp);
|
||||
this->addPasssp(passsp);
|
||||
}
|
||||
@@ -414,7 +421,14 @@ public:
|
||||
void name(const string& name) override { m_name = name; }
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
bool immediate() const { return m_immediate; }
|
||||
VAssertType type() const { return m_type; }
|
||||
VAssertDirectiveType directive() const { return m_directive; }
|
||||
bool immediate() const {
|
||||
return this->type().containsAny(VAssertType::SIMPLE_IMMEDIATE
|
||||
| VAssertType::OBSERVED_DEFERRED_IMMEDIATE
|
||||
| VAssertType::FINAL_DEFERRED_IMMEDIATE)
|
||||
|| this->type() == VAssertType::INTERNAL;
|
||||
}
|
||||
};
|
||||
class AstNodeFor VL_NOT_FINAL : public AstNodeStmt {
|
||||
// @astgen op1 := initsp : List[AstNode]
|
||||
@@ -926,8 +940,7 @@ class AstClocking final : public AstNode {
|
||||
// Children: SENITEM, CLOCKING ITEMs, VARs
|
||||
// @astgen op1 := sensesp : AstSenItem
|
||||
// @astgen op2 := itemsp : List[AstClockingItem]
|
||||
// @astgen op3 := varsp : List[AstVar]
|
||||
// @astgen op4 := eventp : Optional[AstVar]
|
||||
// @astgen op3 := eventp : Optional[AstVar]
|
||||
std::string m_name; // Clocking block name
|
||||
const bool m_isDefault = false; // True if default clocking
|
||||
const bool m_isGlobal = false; // True if global clocking
|
||||
@@ -1022,6 +1035,7 @@ public:
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Scope name
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
void isStatic(bool flag) { m_isStatic = flag; }
|
||||
bool isStatic() const { return m_isStatic; }
|
||||
@@ -1059,24 +1073,6 @@ public:
|
||||
bool same(const AstNode*) const override { return true; }
|
||||
string path() const { return m_path; }
|
||||
};
|
||||
class AstDistItem final : public AstNode {
|
||||
// Constraint distribution item
|
||||
// @astgen op1 := rangep : AstNodeExpr
|
||||
// @astgen op2 := weightp : AstNodeExpr
|
||||
bool m_isWhole = false; // True for weight ':/', false for ':='
|
||||
public:
|
||||
AstDistItem(FileLine* fl, AstNodeExpr* rangep, AstNodeExpr* weightp)
|
||||
: ASTGEN_SUPER_DistItem(fl) {
|
||||
this->rangep(rangep);
|
||||
this->weightp(weightp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstDistItem;
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
void isWhole(bool flag) { m_isWhole = flag; }
|
||||
bool isWhole() const { return m_isWhole; }
|
||||
};
|
||||
class AstDpiExport final : public AstNode {
|
||||
// We could put an AstNodeFTaskRef instead of the verilog function name,
|
||||
// however we're not *calling* it, so that seems somehow wrong.
|
||||
@@ -1106,7 +1102,7 @@ public:
|
||||
BROKEN_RTN(!fmtp());
|
||||
return nullptr;
|
||||
}
|
||||
string verilogKwd() const override { return string{"$"} + string{displayType().ascii()}; }
|
||||
string verilogKwd() const override { return "$"s + string{displayType().ascii()}; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
@@ -1771,6 +1767,7 @@ class AstVar final : public AstNode {
|
||||
VDirection m_declDirection; // Declared direction input/output etc
|
||||
VLifetime m_lifetime; // Lifetime
|
||||
VVarAttrClocker m_attrClocker;
|
||||
VRandAttr m_rand; // Randomizability of this variable (rand, randc, etc)
|
||||
int m_pinNum = 0; // For XML, if non-zero the connection pin number
|
||||
bool m_ansi : 1; // Params or pins declared in the module header, rather than the body
|
||||
bool m_declTyped : 1; // Declared as type (for dedup check)
|
||||
@@ -1794,8 +1791,6 @@ class AstVar final : public AstNode {
|
||||
bool m_attrSFormat : 1; // User sformat attribute
|
||||
bool m_attrSplitVar : 1; // declared with split_var metacomment
|
||||
bool m_fileDescr : 1; // File descriptor
|
||||
bool m_isRand : 1; // Random variable
|
||||
bool m_isRandC : 1; // Random cyclic variable (isRand also set)
|
||||
bool m_isConst : 1; // Table contains constant data
|
||||
bool m_isContinuously : 1; // Ever assigned continuously (for force/release)
|
||||
bool m_hasStrengthAssignment : 1; // Is on LHS of assignment with strength specifier
|
||||
@@ -1840,8 +1835,6 @@ class AstVar final : public AstNode {
|
||||
m_attrSFormat = false;
|
||||
m_attrSplitVar = false;
|
||||
m_fileDescr = false;
|
||||
m_isRand = false;
|
||||
m_isRandC = false;
|
||||
m_isConst = false;
|
||||
m_isContinuously = false;
|
||||
m_hasStrengthAssignment = false;
|
||||
@@ -1966,6 +1959,7 @@ public:
|
||||
void attrIsolateAssign(bool flag) { m_attrIsolateAssign = flag; }
|
||||
void attrSFormat(bool flag) { m_attrSFormat = flag; }
|
||||
void attrSplitVar(bool flag) { m_attrSplitVar = flag; }
|
||||
void rand(const VRandAttr flag) { m_rand = flag; }
|
||||
void usedClock(bool flag) { m_usedClock = flag; }
|
||||
void usedParam(bool flag) { m_usedParam = flag; }
|
||||
void usedLoopIdx(bool flag) { m_usedLoopIdx = flag; }
|
||||
@@ -1982,11 +1976,6 @@ public:
|
||||
void sc(bool flag) { m_sc = flag; }
|
||||
void scSensitive(bool flag) { m_scSensitive = flag; }
|
||||
void primaryIO(bool flag) { m_primaryIO = flag; }
|
||||
void isRand(bool flag) { m_isRand = flag; }
|
||||
void isRandC(bool flag) {
|
||||
m_isRandC = flag;
|
||||
if (flag) isRand(true);
|
||||
}
|
||||
void isConst(bool flag) { m_isConst = flag; }
|
||||
void isContinuously(bool flag) { m_isContinuously = flag; }
|
||||
void isStatic(bool flag) { m_isStatic = flag; }
|
||||
@@ -2030,6 +2019,7 @@ public:
|
||||
bool isContinuously() const { return m_isContinuously; }
|
||||
bool isDeclTyped() const { return m_declTyped; }
|
||||
bool isInoutish() const { return m_direction.isInoutish(); }
|
||||
bool isInput() const { return m_direction.isInput(); }
|
||||
bool isNonOutput() const { return m_direction.isNonOutput(); }
|
||||
bool isReadOnly() const VL_MT_SAFE { return m_direction.isReadOnly(); }
|
||||
bool isConstRef() const VL_MT_SAFE { return m_direction.isConstRef(); }
|
||||
@@ -2067,6 +2057,7 @@ public:
|
||||
bool isScQuad() const;
|
||||
bool isScBv() const;
|
||||
bool isScUint() const;
|
||||
bool isScUintBool() const;
|
||||
bool isScBigUint() const;
|
||||
bool isScSensitive() const { return m_scSensitive; }
|
||||
bool isSigPublic() const;
|
||||
@@ -2074,8 +2065,8 @@ public:
|
||||
bool isSigUserRdPublic() const { return m_sigUserRdPublic; }
|
||||
bool isSigUserRWPublic() const { return m_sigUserRWPublic; }
|
||||
bool isTrace() const { return m_trace; }
|
||||
bool isRand() const { return m_isRand; }
|
||||
bool isRandC() const { return m_isRandC; }
|
||||
bool isRand() const { return m_rand.isRand(); }
|
||||
bool isRandC() const { return m_rand.isRandC(); }
|
||||
bool isConst() const VL_MT_SAFE { return m_isConst; }
|
||||
bool isStatic() const VL_MT_SAFE { return m_isStatic; }
|
||||
bool isLatched() const { return m_isLatched; }
|
||||
@@ -2092,6 +2083,7 @@ public:
|
||||
bool attrIsolateAssign() const { return m_attrIsolateAssign; }
|
||||
AstIface* sensIfacep() const { return m_sensIfacep; }
|
||||
VVarAttrClocker attrClocker() const { return m_attrClocker; }
|
||||
VRandAttr rand() const { return m_rand; }
|
||||
string verilogKwd() const override;
|
||||
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
|
||||
VLifetime lifetime() const { return m_lifetime; }
|
||||
@@ -2191,8 +2183,8 @@ public:
|
||||
ASTGEN_MEMBERS_AstBegin;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
void generate(bool flag) { m_generate = flag; }
|
||||
bool generate() const { return m_generate; }
|
||||
void generate(bool flag) { m_generate = flag; }
|
||||
void setNeedProcess() { m_needProcess = true; }
|
||||
bool needProcess() const { return m_needProcess; }
|
||||
bool implied() const { return m_implied; }
|
||||
@@ -2346,6 +2338,42 @@ public:
|
||||
// Return the lowest class extended from, or this class
|
||||
AstClass* baseMostClassp();
|
||||
static bool isCacheableChild(const AstNode* nodep);
|
||||
// Iterates top level members of the class, taking into account inheritance (starting from the
|
||||
// root superclass). Note: after V3Scope, several children are moved under an AstScope and will
|
||||
// not be found by this.
|
||||
template <typename Callable>
|
||||
void foreachMember(const Callable& f) {
|
||||
using T_Node = typename FunctionArgNoPointerNoCV<Callable, 1>::type;
|
||||
static_assert(
|
||||
vlstd::is_invocable<Callable, AstClass*, T_Node*>::value
|
||||
&& std::is_base_of<AstNode, T_Node>::value,
|
||||
"Callable 'f' must have a signature compatible with 'void(AstClass*, T_Node*)', "
|
||||
"with 'T_Node' being a subtype of 'AstNode'");
|
||||
if (AstClassExtends* const extendsp = this->extendsp()) {
|
||||
extendsp->classp()->foreachMember(f);
|
||||
}
|
||||
for (AstNode* stmtp = stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
if (AstNode::privateTypeTest<T_Node>(stmtp)) f(this, static_cast<T_Node*>(stmtp));
|
||||
}
|
||||
}
|
||||
// Same as above, but stops after first match
|
||||
template <typename Callable>
|
||||
bool existsMember(const Callable& p) const {
|
||||
using T_Node = typename FunctionArgNoPointerNoCV<Callable, 1>::type;
|
||||
static_assert(vlstd::is_invocable_r<bool, Callable, const AstClass*, const T_Node*>::value
|
||||
&& std::is_base_of<AstNode, T_Node>::value,
|
||||
"Predicate 'p' must have a signature compatible with 'bool(const AstClass*, "
|
||||
"const T_Node*)', with 'T_Node' being a subtype of 'AstNode'");
|
||||
if (AstClassExtends* const extendsp = this->extendsp()) {
|
||||
if (extendsp->classp()->existsMember(p)) return true;
|
||||
}
|
||||
for (AstNode* stmtp = stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
if (AstNode::privateTypeTest<T_Node>(stmtp)) {
|
||||
if (p(this, static_cast<T_Node*>(stmtp))) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
class AstClassPackage final : public AstNodeModule {
|
||||
// The static information portion of a class (treated similarly to a package)
|
||||
@@ -2588,16 +2616,18 @@ public:
|
||||
};
|
||||
class AstAssertCtl final : public AstNodeStmt {
|
||||
// @astgen op1 := controlTypep : AstNodeExpr
|
||||
// @astgen op2 := levelp : AstNodeExpr
|
||||
// @astgen op3 := itemsp : List[AstNodeExpr]
|
||||
// @astgen op2 := assertTypesp : Optional[AstNodeExpr]
|
||||
// @astgen op3 := directiveTypesp : Optional[AstNodeExpr]
|
||||
// Type of assertcontrol task; either known from parser or from evaluated
|
||||
// controlTypep expression.
|
||||
VAssertCtlType m_ctlType; // $assert keyword type
|
||||
VAssertCtlType m_ctlType; // $assert keyword type (control_type)
|
||||
VAssertType m_assertTypes; // Types of assertions affected
|
||||
VAssertDirectiveType m_directiveTypes; // Types of directives affected
|
||||
|
||||
public:
|
||||
AstAssertCtl(FileLine* fl, VAssertCtlType ctlType, AstNodeExpr* levelp = nullptr,
|
||||
AstNodeExpr* itemsp = nullptr);
|
||||
AstAssertCtl(FileLine* fl, AstNodeExpr* controlTypep, AstNodeExpr* assertionTypep = nullptr,
|
||||
AstAssertCtl(FileLine* fl, AstNodeExpr* controlTypep, AstNodeExpr* assertTypesp = nullptr,
|
||||
AstNodeExpr* directiveTypep = nullptr, AstNodeExpr* levelp = nullptr,
|
||||
AstNodeExpr* itemsp = nullptr);
|
||||
ASTGEN_MEMBERS_AstAssertCtl;
|
||||
@@ -2608,6 +2638,10 @@ public:
|
||||
bool isOutputter() override { return true; }
|
||||
VAssertCtlType ctlType() const { return m_ctlType; }
|
||||
void ctlType(int32_t type) { m_ctlType = VAssertCtlType{type}; }
|
||||
VAssertType ctlAssertTypes() const { return m_assertTypes; }
|
||||
void ctlAssertTypes(VAssertType types) { m_assertTypes = types; }
|
||||
VAssertDirectiveType ctlDirectiveTypes() const { return m_directiveTypes; }
|
||||
void ctlDirectiveTypes(VAssertDirectiveType types) { m_directiveTypes = types; }
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
};
|
||||
@@ -2688,10 +2722,10 @@ public:
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
void isDisableSoft(bool flag) { m_isDisableSoft = flag; }
|
||||
bool isDisableSoft() const { return m_isDisableSoft; }
|
||||
void isSoft(bool flag) { m_isSoft = flag; }
|
||||
void isDisableSoft(bool flag) { m_isDisableSoft = flag; }
|
||||
bool isSoft() const { return m_isSoft; }
|
||||
void isSoft(bool flag) { m_isSoft = flag; }
|
||||
};
|
||||
class AstConstraintUnique final : public AstNodeStmt {
|
||||
// Constraint unique statement
|
||||
@@ -2747,8 +2781,8 @@ public:
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
void binNum(int flag) { m_binNum = flag; }
|
||||
int binNum() const { return m_binNum; }
|
||||
void binNum(int flag) { m_binNum = flag; }
|
||||
int offset() const { return m_offset; }
|
||||
const string& comment() const { return m_text; } // text to insert in code
|
||||
const string& linescov() const { return m_linescov; }
|
||||
@@ -2786,7 +2820,7 @@ public:
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isOutputter() override { return true; }
|
||||
// but isPure() true
|
||||
bool isPure() override { return false; }
|
||||
AstCoverDecl* declp() const { return m_declp; } // Where defined
|
||||
};
|
||||
class AstCoverToggle final : public AstNodeStmt {
|
||||
@@ -2883,8 +2917,8 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
string verilogKwd() const override {
|
||||
return (filep() ? string{"$f"} + string{displayType().ascii()}
|
||||
: string{"$"} + string{displayType().ascii()});
|
||||
return (filep() ? "$f"s + string{displayType().ascii()}
|
||||
: "$"s + string{displayType().ascii()});
|
||||
}
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
@@ -3021,6 +3055,7 @@ class AstJumpBlock final : public AstNodeStmt {
|
||||
//
|
||||
// @astgen ptr := m_labelp : AstJumpLabel // [After V3Jump] Pointer to declaration
|
||||
int m_labelNum = 0; // Set by V3EmitCSyms to tell final V3Emit what to increment
|
||||
VIsCached m_purity; // Pure state
|
||||
public:
|
||||
// After construction must call ->labelp to associate with appropriate label
|
||||
AstJumpBlock(FileLine* fl, AstNode* stmtsp)
|
||||
@@ -3036,6 +3071,10 @@ public:
|
||||
void labelNum(int flag) { m_labelNum = flag; }
|
||||
AstJumpLabel* labelp() const { return m_labelp; }
|
||||
void labelp(AstJumpLabel* labelp) { m_labelp = labelp; }
|
||||
bool isPure() override;
|
||||
|
||||
private:
|
||||
bool getPurityRecurse() const;
|
||||
};
|
||||
class AstJumpGo final : public AstNodeStmt {
|
||||
// Jump point; branch down to a JumpLabel
|
||||
@@ -3227,6 +3266,7 @@ public:
|
||||
this->exprp(exprp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstStmtExpr;
|
||||
bool isPure() override { return exprp()->isPure(); }
|
||||
};
|
||||
class AstStop final : public AstNodeStmt {
|
||||
public:
|
||||
@@ -3379,7 +3419,7 @@ public:
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isOutputter() override { return true; }
|
||||
// but isPure() true
|
||||
bool isPure() override { return false; }
|
||||
AstTraceDecl* declp() const { return m_declp; }
|
||||
VTraceType traceType() const { return m_traceType; }
|
||||
uint32_t baseCode() const { return m_baseCode; }
|
||||
@@ -3461,8 +3501,8 @@ public:
|
||||
// Stop statement searchback here
|
||||
void addNextStmt(AstNode* newp, AstNode* belowp) override;
|
||||
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
|
||||
void unrollFull(const VOptionBool flag) { m_unrollFull = flag; }
|
||||
VOptionBool unrollFull() const { return m_unrollFull; }
|
||||
void unrollFull(const VOptionBool flag) { m_unrollFull = flag; }
|
||||
};
|
||||
|
||||
// === AstNodeAssign ===
|
||||
@@ -3619,11 +3659,12 @@ public:
|
||||
// === AstNodeCoverOrAssert ===
|
||||
class AstAssert final : public AstNodeCoverOrAssert {
|
||||
// @astgen op3 := failsp: List[AstNode] // Statements when propp is failing/falsey
|
||||
|
||||
public:
|
||||
ASTGEN_MEMBERS_AstAssert;
|
||||
AstAssert(FileLine* fl, AstNode* propp, AstNode* passsp, AstNode* failsp, bool immediate,
|
||||
const string& name = "")
|
||||
: ASTGEN_SUPER_Assert(fl, propp, passsp, immediate, name) {
|
||||
AstAssert(FileLine* fl, AstNode* propp, AstNode* passsp, AstNode* failsp, VAssertType type,
|
||||
VAssertDirectiveType directive, const string& name = "")
|
||||
: ASTGEN_SUPER_Assert(fl, propp, passsp, type, directive, name) {
|
||||
this->addFailsp(failsp);
|
||||
}
|
||||
};
|
||||
@@ -3633,8 +3674,10 @@ class AstAssertIntrinsic final : public AstNodeCoverOrAssert {
|
||||
public:
|
||||
ASTGEN_MEMBERS_AstAssertIntrinsic;
|
||||
AstAssertIntrinsic(FileLine* fl, AstNode* propp, AstNode* passsp, AstNode* failsp,
|
||||
bool immediate, const string& name = "")
|
||||
: ASTGEN_SUPER_AssertIntrinsic(fl, propp, passsp, immediate, name) {
|
||||
const string& name = "")
|
||||
// Intrinsic asserts are always enabled thus 'type' field is set to INTERNAL.
|
||||
: ASTGEN_SUPER_AssertIntrinsic(fl, propp, passsp, VAssertType::INTERNAL,
|
||||
VAssertDirectiveType::INTRINSIC, name) {
|
||||
this->addFailsp(failsp);
|
||||
}
|
||||
};
|
||||
@@ -3642,16 +3685,17 @@ class AstCover final : public AstNodeCoverOrAssert {
|
||||
// @astgen op3 := coverincsp: List[AstNode] // Coverage node
|
||||
public:
|
||||
ASTGEN_MEMBERS_AstCover;
|
||||
AstCover(FileLine* fl, AstNode* propp, AstNode* stmtsp, bool immediate,
|
||||
AstCover(FileLine* fl, AstNode* propp, AstNode* stmtsp, VAssertType type,
|
||||
const string& name = "")
|
||||
: ASTGEN_SUPER_Cover(fl, propp, stmtsp, immediate, name) {}
|
||||
virtual bool immediate() const { return false; }
|
||||
: ASTGEN_SUPER_Cover(fl, propp, stmtsp, type, VAssertDirectiveType::COVER, name) {}
|
||||
};
|
||||
class AstRestrict final : public AstNodeCoverOrAssert {
|
||||
public:
|
||||
ASTGEN_MEMBERS_AstRestrict;
|
||||
AstRestrict(FileLine* fl, AstNode* propp)
|
||||
: ASTGEN_SUPER_Restrict(fl, propp, nullptr, false, "") {}
|
||||
// Intrinsic asserts are always ignored thus 'type' field is set to INTERNAL.
|
||||
: ASTGEN_SUPER_Restrict(fl, propp, nullptr, VAssertType::INTERNAL,
|
||||
VAssertDirectiveType::RESTRICT) {}
|
||||
};
|
||||
|
||||
// === AstNodeFor ===
|
||||
@@ -3792,8 +3836,8 @@ public:
|
||||
: ASTGEN_SUPER_TextBlock(fl, textp, tracking)
|
||||
, m_commas(commas) {}
|
||||
ASTGEN_MEMBERS_AstTextBlock;
|
||||
void commas(bool flag) { m_commas = flag; }
|
||||
bool commas() const { return m_commas; }
|
||||
void commas(bool flag) { m_commas = flag; }
|
||||
void addText(FileLine* fl, const string& textp, bool tracking = false) {
|
||||
addNodesp(new AstText{fl, textp, tracking});
|
||||
}
|
||||
|
||||
+60
-23
@@ -322,6 +322,28 @@ AstNodeExpr* AstInsideRange::newAndFromInside(AstNodeExpr* exprp, AstNodeExpr* l
|
||||
return new AstLogAnd{fileline(), ap, bp};
|
||||
}
|
||||
|
||||
void AstConsDynArray::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (lhsIsValue()) str << " [LVAL]";
|
||||
if (rhsIsValue()) str << " [RVAL]";
|
||||
}
|
||||
void AstConsDynArray::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFunc(str, lhsIsValue);
|
||||
dumpJsonBoolFunc(str, rhsIsValue);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
|
||||
void AstConsQueue::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (lhsIsValue()) str << " [LVAL]";
|
||||
if (rhsIsValue()) str << " [RVAL]";
|
||||
}
|
||||
void AstConsQueue::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFunc(str, lhsIsValue);
|
||||
dumpJsonBoolFunc(str, rhsIsValue);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
|
||||
AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) {
|
||||
bool success = false;
|
||||
if (literal[0] == '"') {
|
||||
@@ -352,6 +374,8 @@ AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
string AstConstraintRef::name() const { return constrp()->name(); }
|
||||
|
||||
AstNetlist::AstNetlist()
|
||||
: ASTGEN_SUPER_Netlist(new FileLine{FileLine::builtInFilename()})
|
||||
, m_typeTablep{new AstTypeTable{fileline()}}
|
||||
@@ -380,6 +404,9 @@ bool AstVar::isScBv() const {
|
||||
bool AstVar::isScUint() const {
|
||||
return ((isSc() && v3Global.opt.pinsScUint() && width() >= 2 && width() <= 64) && !isScBv());
|
||||
}
|
||||
bool AstVar::isScUintBool() const {
|
||||
return (isSc() && v3Global.opt.pinsScUintBool() && width() == 1);
|
||||
}
|
||||
bool AstVar::isScBigUint() const {
|
||||
return ((isSc() && v3Global.opt.pinsScBigUint() && width() >= 65 && width() <= 512)
|
||||
&& !isScBv());
|
||||
@@ -669,14 +696,13 @@ string AstVar::dpiTmpVarType(const string& varName) const {
|
||||
|
||||
string AstVar::scType() const {
|
||||
if (isScBigUint()) {
|
||||
return (string{"sc_dt::sc_biguint<"} + cvtToStr(widthMin())
|
||||
return ("sc_dt::sc_biguint<"s + cvtToStr(widthMin())
|
||||
+ "> "); // Keep the space so don't get >>
|
||||
} else if (isScUint()) {
|
||||
return (string{"sc_dt::sc_uint<"} + cvtToStr(widthMin())
|
||||
} else if (isScUint() || isScUintBool()) {
|
||||
return ("sc_dt::sc_uint<"s + cvtToStr(widthMin())
|
||||
+ "> "); // Keep the space so don't get >>
|
||||
} else if (isScBv()) {
|
||||
return (string{"sc_dt::sc_bv<"} + cvtToStr(widthMin())
|
||||
+ "> "); // Keep the space so don't get >>
|
||||
return ("sc_dt::sc_bv<"s + cvtToStr(widthMin()) + "> "); // Keep the space so don't get >>
|
||||
} else if (widthMin() == 1) {
|
||||
return "bool";
|
||||
} else if (widthMin() <= VL_IDATASIZE) {
|
||||
@@ -978,6 +1004,16 @@ const char* AstJumpBlock::broken() const {
|
||||
BROKEN_RTN(!labelp()->brokeExistsBelow());
|
||||
return nullptr;
|
||||
}
|
||||
bool AstJumpBlock::isPure() {
|
||||
if (!m_purity.isCached()) m_purity.set(getPurityRecurse());
|
||||
return m_purity.get();
|
||||
}
|
||||
bool AstJumpBlock::getPurityRecurse() const {
|
||||
for (AstNode* stmtp = this->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
if (!stmtp->isPure()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
string AstScope::nameDotless() const {
|
||||
string result = shortName();
|
||||
@@ -1461,23 +1497,20 @@ void AstAlways::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStr(str, "keyword", keyword().ascii());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
AstAssertCtl::AstAssertCtl(FileLine* fl, VAssertCtlType ctlType, AstNodeExpr* levelp,
|
||||
AstNodeExpr* itemsp)
|
||||
AstAssertCtl::AstAssertCtl(FileLine* fl, VAssertCtlType ctlType, AstNodeExpr*, AstNodeExpr*)
|
||||
: ASTGEN_SUPER_AssertCtl(fl)
|
||||
, m_ctlType{ctlType} {
|
||||
controlTypep(new AstConst{fl, ctlType});
|
||||
if (!levelp) levelp = new AstConst{fl, 0};
|
||||
this->levelp(levelp);
|
||||
addItemsp(itemsp);
|
||||
}
|
||||
AstAssertCtl::AstAssertCtl(FileLine* fl, AstNodeExpr* controlTypep, AstNodeExpr*, AstNodeExpr*,
|
||||
AstNodeExpr* levelp, AstNodeExpr* itemsp)
|
||||
AstAssertCtl::AstAssertCtl(FileLine* fl, AstNodeExpr* controlTypep, AstNodeExpr* assertTypesp,
|
||||
AstNodeExpr* directiveTypep, AstNodeExpr*, AstNodeExpr*)
|
||||
: ASTGEN_SUPER_AssertCtl(fl)
|
||||
, m_ctlType{VAssertCtlType::_TO_BE_EVALUATED} {
|
||||
, m_ctlType{VAssertCtlType::_TO_BE_EVALUATED}
|
||||
, m_assertTypes{VAssertType::INTERNAL}
|
||||
, m_directiveTypes{VAssertDirectiveType::INTERNAL} {
|
||||
this->controlTypep(controlTypep);
|
||||
if (!levelp) levelp = new AstConst{fl, 0};
|
||||
this->levelp(levelp);
|
||||
addItemsp(itemsp);
|
||||
this->assertTypesp(assertTypesp);
|
||||
this->directiveTypesp(directiveTypep);
|
||||
}
|
||||
void AstAssertCtl::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
@@ -1636,10 +1669,10 @@ string AstClassRefDType::prettyDTypeName(bool) const { return "class{}"s + prett
|
||||
string AstClassRefDType::name() const { return classp() ? classp()->name() : "<unlinked>"; }
|
||||
void AstNodeCoverOrAssert::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
if (immediate()) str << " [IMMEDIATE]";
|
||||
str << " ["s + this->type().ascii() + "]";
|
||||
}
|
||||
void AstNodeCoverOrAssert::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFunc(str, immediate);
|
||||
dumpJsonStr(str, "type", "["s + this->type().ascii() + "]");
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstClocking::dump(std::ostream& str) const {
|
||||
@@ -1826,6 +1859,13 @@ AstNodeUOrStructDType* AstMemberDType::getChildStructp() const {
|
||||
return VN_CAST(subdtp->skipRefp(), NodeUOrStructDType); // Maybe nullptr
|
||||
}
|
||||
|
||||
AstMemberSel::AstMemberSel(FileLine* fl, AstNodeExpr* fromp, AstVar* varp)
|
||||
: ASTGEN_SUPER_MemberSel(fl)
|
||||
, m_name{varp->name()} {
|
||||
this->fromp(fromp);
|
||||
this->varp(varp);
|
||||
dtypep(varp->dtypep());
|
||||
}
|
||||
bool AstMemberSel::same(const AstNode* samep) const {
|
||||
const AstMemberSel* const sp = VN_DBG_AS(samep, MemberSel);
|
||||
return sp != nullptr && access() == sp->access() && fromp()->isSame(sp->fromp())
|
||||
@@ -2357,11 +2397,7 @@ void AstVar::dump(std::ostream& str) const {
|
||||
if (isInternal()) str << " [INTERNAL]";
|
||||
if (isLatched()) str << " [LATCHED]";
|
||||
if (isUsedLoopIdx()) str << " [LOOP]";
|
||||
if (isRandC()) {
|
||||
str << " [RANDC]";
|
||||
} else if (isRand()) {
|
||||
str << " [RAND]";
|
||||
}
|
||||
if (rand().isRandomizable()) str << rand();
|
||||
if (noReset()) str << " [!RST]";
|
||||
if (attrIsolateAssign()) str << " [aISO]";
|
||||
if (attrFileDescr()) str << " [aFD]";
|
||||
@@ -2801,6 +2837,7 @@ void AstCMethodHard::setPurity() {
|
||||
{"reverse", false},
|
||||
{"rsort", false},
|
||||
{"set", false},
|
||||
{"set_randmode", false},
|
||||
{"shuffle", false},
|
||||
{"size", true},
|
||||
{"slice", true},
|
||||
|
||||
+151
-1
@@ -134,6 +134,9 @@ class BeginVisitor final : public VNVisitor {
|
||||
dotNames(nodep, "__FORK__");
|
||||
nodep->name("");
|
||||
}
|
||||
void visit(AstForeach* nodep) override {
|
||||
VL_DO_DANGLING(V3Begin::convertToWhile(nodep), nodep);
|
||||
}
|
||||
void visit(AstNodeAssign* nodep) override {
|
||||
// Keep begin under assignment (in nodep->timingControlp())
|
||||
VL_RESTORER(m_keepBegins);
|
||||
@@ -274,7 +277,7 @@ class BeginVisitor final : public VNVisitor {
|
||||
// To keep correct visual order, must add before other Text's
|
||||
AstText* const afterp = nodep->scopeAttrp();
|
||||
if (afterp) afterp->unlinkFrBackWithNext();
|
||||
nodep->addScopeAttrp(new AstText{nodep->fileline(), string{"__DOT__"} + scname});
|
||||
nodep->addScopeAttrp(new AstText{nodep->fileline(), "__DOT__"s + scname});
|
||||
if (afterp) nodep->addScopeAttrp(afterp);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
@@ -327,6 +330,7 @@ private:
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNodeFTaskRef* nodep) override {
|
||||
UASSERT_OBJ(nodep->taskp(), nodep, "unlinked");
|
||||
if (nodep->taskp()->user1()) { // It was converted
|
||||
UINFO(9, " relinkFTask " << nodep << endl);
|
||||
nodep->name(nodep->taskp()->name());
|
||||
@@ -368,3 +372,149 @@ void V3Begin::debeginAll(AstNetlist* nodep) {
|
||||
} // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("begin", 0, dumpTreeEitherLevel() >= 3);
|
||||
}
|
||||
|
||||
static AstNode* createForeachLoop(AstNodeForeach* nodep, AstNode* bodysp, AstVar* varp,
|
||||
AstNodeExpr* leftp, AstNodeExpr* rightp, VNType nodeType) {
|
||||
FileLine* const fl = varp->fileline();
|
||||
AstNodeExpr* varRefp = new AstVarRef{fl, varp, VAccess::READ};
|
||||
AstNodeExpr* condp;
|
||||
bool inc = true;
|
||||
switch (nodeType) {
|
||||
case VNType::atLteS: condp = new AstLteS{fl, varRefp, rightp}; break;
|
||||
case VNType::atLt: condp = new AstLt{fl, varRefp, rightp}; break;
|
||||
case VNType::atGteS:
|
||||
condp = new AstGteS{fl, varRefp, rightp};
|
||||
inc = false;
|
||||
break;
|
||||
default: UASSERT_OBJ(0, varp, "Missing comparison handling"); break;
|
||||
}
|
||||
AstNodeExpr* incp;
|
||||
if (inc)
|
||||
incp = new AstAdd{fl, varRefp->cloneTree(false), new AstConst{fl, 1}};
|
||||
else
|
||||
incp = new AstSub{fl, varRefp->cloneTree(false), new AstConst{fl, 1}};
|
||||
|
||||
AstWhile* const whilep = new AstWhile{
|
||||
fl, condp, bodysp, new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, incp}};
|
||||
AstNode* const stmtsp = varp; // New statements for outer loop
|
||||
stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, leftp});
|
||||
stmtsp->addNext(whilep);
|
||||
return stmtsp;
|
||||
}
|
||||
static AstNode* createForeachLoopRanged(AstNodeForeach* nodep, AstNode* bodysp, AstVar* varp,
|
||||
const VNumRange& declRange) {
|
||||
FileLine* const fl = varp->fileline();
|
||||
V3Number left{nodep, 32}, right{nodep, 32};
|
||||
left.isSigned(true);
|
||||
right.isSigned(true);
|
||||
left.setLongS(declRange.left());
|
||||
right.setLongS(declRange.right());
|
||||
AstNodeExpr* const leftp = new AstConst{fl, left};
|
||||
AstNodeExpr* const rightp = new AstConst{fl, right};
|
||||
return createForeachLoop(nodep, bodysp, varp, leftp, rightp,
|
||||
declRange.left() <= declRange.right() ? VNType::atLteS
|
||||
: VNType::atGteS);
|
||||
}
|
||||
AstNode* V3Begin::convertToWhile(AstForeach* nodep) {
|
||||
// if (debug()) dumpTree(cout, "- foreach-old: ");
|
||||
const AstSelLoopVars* const loopsp = VN_CAST(nodep->arrayp(), SelLoopVars);
|
||||
UASSERT_OBJ(loopsp, nodep, "No loop variables under foreach");
|
||||
AstNodeExpr* const fromp = loopsp->fromp();
|
||||
UASSERT_OBJ(fromp->dtypep(), fromp, "Missing data type");
|
||||
AstNodeDType* fromDtp = fromp->dtypep()->skipRefp();
|
||||
// Split into for loop
|
||||
// We record where the body needs to eventually go with bodyPointp
|
||||
AstNode* bodyPointp = new AstBegin{nodep->fileline(), "[EditWrapper]", nullptr};
|
||||
AstNode* newp = nullptr;
|
||||
AstNode* lastp = nodep;
|
||||
|
||||
// Major dimension first
|
||||
for (AstNode *argsp = loopsp->elementsp(), *next_argsp; argsp; argsp = next_argsp) {
|
||||
next_argsp = argsp->nextp();
|
||||
const bool empty = VN_IS(argsp, Empty);
|
||||
AstVar* const varp = VN_CAST(argsp, Var);
|
||||
UASSERT_OBJ(varp || empty, argsp, "Missing foreach loop variable");
|
||||
if (varp) varp->unlinkFrBack()->usedLoopIdx(true);
|
||||
UASSERT_OBJ(fromDtp, argsp, "more loop vars than dimensions");
|
||||
fromDtp = fromDtp->skipRefp();
|
||||
|
||||
FileLine* const fl = argsp->fileline();
|
||||
if (varp) {
|
||||
AstNode* loopp = nullptr;
|
||||
VNRelinker handle;
|
||||
lastp->unlinkFrBack(&handle);
|
||||
if (const AstNodeArrayDType* const adtypep = VN_CAST(fromDtp, NodeArrayDType)) {
|
||||
loopp = createForeachLoopRanged(nodep, bodyPointp, varp, adtypep->declRange());
|
||||
} else if (AstBasicDType* const adtypep = VN_CAST(fromDtp, BasicDType)) {
|
||||
if (adtypep->isString()) {
|
||||
AstConst* const leftp = new AstConst{fl, 0};
|
||||
AstNodeExpr* const rightp = new AstLenN{fl, fromp->cloneTreePure(false)};
|
||||
loopp
|
||||
= createForeachLoop(nodep, bodyPointp, varp, leftp, rightp, VNType::atLt);
|
||||
} else {
|
||||
UASSERT_OBJ(adtypep->isRanged(), varp, "foreach on basic " << adtypep);
|
||||
loopp = createForeachLoopRanged(nodep, bodyPointp, varp, adtypep->declRange());
|
||||
}
|
||||
} else if (VN_IS(fromDtp, DynArrayDType) || VN_IS(fromDtp, QueueDType)) {
|
||||
AstConst* const leftp = new AstConst{fl, 0};
|
||||
AstNodeExpr* const rightp
|
||||
= new AstCMethodHard{fl, fromp->cloneTreePure(false), "size"};
|
||||
rightp->dtypeSetSigned32();
|
||||
rightp->protect(false);
|
||||
loopp = createForeachLoop(nodep, bodyPointp, varp, leftp, rightp, VNType::atLt);
|
||||
} else if (VN_IS(fromDtp, AssocArrayDType)) {
|
||||
// Make this: var KEY_TYPE index;
|
||||
// bit index__Vfirst;
|
||||
// index__Vfirst = 0;
|
||||
// if (0 != array.first(index))
|
||||
// do body while (index__Vfirst || 0 != array.next(index))
|
||||
AstVar* const first_varp = new AstVar{
|
||||
fl, VVarType::BLOCKTEMP, varp->name() + "__Vfirst", VFlagBitPacked{}, 1};
|
||||
first_varp->usedLoopIdx(true);
|
||||
first_varp->lifetime(VLifetime::AUTOMATIC);
|
||||
AstNodeExpr* const firstp
|
||||
= new AstCMethodHard{fl, fromp->cloneTreePure(false), "first",
|
||||
new AstVarRef{fl, varp, VAccess::READWRITE}};
|
||||
firstp->dtypeSetSigned32();
|
||||
AstNodeExpr* const nextp
|
||||
= new AstCMethodHard{fl, fromp->cloneTreePure(false), "next",
|
||||
new AstVarRef{fl, varp, VAccess::READWRITE}};
|
||||
nextp->dtypeSetSigned32();
|
||||
AstNode* const first_clearp
|
||||
= new AstAssign{fl, new AstVarRef{fl, first_varp, VAccess::WRITE},
|
||||
new AstConst{fl, AstConst::BitFalse{}}};
|
||||
auto* const orp = new AstLogOr{fl, new AstVarRef{fl, first_varp, VAccess::READ},
|
||||
new AstNeq{fl, new AstConst{fl, 0}, nextp}};
|
||||
AstNode* const whilep = new AstWhile{fl, orp, first_clearp};
|
||||
first_clearp->addNext(bodyPointp);
|
||||
AstNode* const ifbodyp
|
||||
= new AstAssign{fl, new AstVarRef{fl, first_varp, VAccess::WRITE},
|
||||
new AstConst{fl, AstConst::BitTrue{}}};
|
||||
ifbodyp->addNext(whilep);
|
||||
loopp = varp;
|
||||
loopp->addNext(first_varp);
|
||||
loopp->addNext(
|
||||
new AstIf{fl, new AstNeq{fl, new AstConst{fl, 0}, firstp}, ifbodyp});
|
||||
}
|
||||
UASSERT_OBJ(loopp, argsp, "unable to foreach " << fromDtp);
|
||||
// New loop goes UNDER previous loop
|
||||
handle.relink(loopp);
|
||||
lastp = bodyPointp;
|
||||
if (!newp) newp = loopp;
|
||||
}
|
||||
// Prep for next
|
||||
fromDtp = fromDtp->subDTypep();
|
||||
}
|
||||
// The parser validates we don't have "foreach (array[,,,])"
|
||||
AstNode* const bodyp = nodep->stmtsp();
|
||||
UASSERT_OBJ(newp, nodep, "foreach has no non-empty loop variable");
|
||||
if (bodyp) {
|
||||
bodyPointp->replaceWith(bodyp->unlinkFrBackWithNext());
|
||||
} else {
|
||||
bodyPointp->unlinkFrBack();
|
||||
}
|
||||
VL_DO_DANGLING(bodyPointp->deleteTree(), bodyPointp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
// if (debug()) newp->dumpTreeAndNext(cout, "- foreach-new: ");
|
||||
return newp;
|
||||
}
|
||||
|
||||
@@ -23,12 +23,15 @@
|
||||
#include "V3ThreadSafety.h"
|
||||
|
||||
class AstNetlist;
|
||||
class AstNode;
|
||||
class AstForeach;
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3Begin final {
|
||||
public:
|
||||
static void debeginAll(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
static AstNode* convertToWhile(AstForeach* nodep) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+9
-50
@@ -68,14 +68,10 @@ public:
|
||||
|
||||
class ClockVisitor final : public VNVisitor {
|
||||
// NODE STATE
|
||||
// ???
|
||||
const VNUser1InUse m_user1InUse;
|
||||
// STATE
|
||||
AstCFunc* m_evalp = nullptr; // The '_eval' function
|
||||
AstScope* m_scopep = nullptr; // Current scope
|
||||
AstSenTree* m_lastSenp = nullptr; // Last sensitivity match, so we can detect duplicates.
|
||||
AstIf* m_lastIfp = nullptr; // Last sensitivity if active to add more under
|
||||
bool m_inSampled = false; // True inside a sampled expression
|
||||
|
||||
// METHODS
|
||||
|
||||
@@ -88,25 +84,6 @@ class ClockVisitor final : public VNVisitor {
|
||||
}
|
||||
return senEqnp;
|
||||
}
|
||||
AstVarScope* createSampledVar(AstVarScope* vscp) {
|
||||
if (vscp->user1p()) return VN_AS(vscp->user1p(), VarScope);
|
||||
const AstVar* const varp = vscp->varp();
|
||||
const string newvarname
|
||||
= string{"__Vsampled__"} + vscp->scopep()->nameDotless() + "__" + varp->name();
|
||||
FileLine* const flp = vscp->fileline();
|
||||
AstVar* const newvarp = new AstVar{flp, VVarType::MODULETEMP, newvarname, varp->dtypep()};
|
||||
newvarp->noReset(true); // Reset by below assign
|
||||
m_scopep->modp()->addStmtsp(newvarp);
|
||||
AstVarScope* const newvscp = new AstVarScope{flp, m_scopep, newvarp};
|
||||
vscp->user1p(newvscp);
|
||||
m_scopep->addVarsp(newvscp);
|
||||
// At the top of _eval, assign them
|
||||
AstAssign* const finalp = new AstAssign{flp, new AstVarRef{flp, newvscp, VAccess::WRITE},
|
||||
new AstVarRef{flp, vscp, VAccess::READ}};
|
||||
m_evalp->addInitsp(finalp);
|
||||
UINFO(4, "New Sampled: " << newvscp << endl);
|
||||
return newvscp;
|
||||
}
|
||||
AstIf* makeActiveIf(AstSenTree* sensesp) {
|
||||
AstNodeExpr* const senEqnp = createSenseEquation(sensesp->sensesp());
|
||||
UASSERT_OBJ(senEqnp, sensesp, "No sense equation, shouldn't be in sequent activation.");
|
||||
@@ -180,33 +157,15 @@ class ClockVisitor final : public VNVisitor {
|
||||
clearLastSen();
|
||||
}
|
||||
|
||||
//========== Create sampled values
|
||||
void visit(AstScope* nodep) override {
|
||||
VL_RESTORER(m_scopep);
|
||||
{
|
||||
m_scopep = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstSampled* nodep) override {
|
||||
VL_RESTORER(m_inSampled);
|
||||
{
|
||||
m_inSampled = true;
|
||||
iterateChildren(nodep);
|
||||
nodep->replaceWith(nodep->exprp()->unlinkFrBack());
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstVarRef* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (m_inSampled && !nodep->user1SetOnce()) {
|
||||
UASSERT_OBJ(nodep->access().isReadOnly(), nodep, "Should have failed in V3Access");
|
||||
AstVarScope* const varscp = nodep->varScopep();
|
||||
AstVarScope* const lastscp = createSampledVar(varscp);
|
||||
AstNode* const newp = new AstVarRef{nodep->fileline(), lastscp, VAccess::READ};
|
||||
newp->user1SetOnce(); // Don't sample this one
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
//========== Move sampled assignments
|
||||
void visit(AstVarScope* nodep) override {
|
||||
AstVar* varp = nodep->varp();
|
||||
if (varp->valuep() && varp->name().substr(0, strlen("__Vsampled")) == "__Vsampled") {
|
||||
m_evalp->addInitsp(new AstAssign{
|
||||
nodep->fileline(), new AstVarRef{nodep->fileline(), nodep, VAccess::WRITE},
|
||||
VN_AS(varp->valuep()->unlinkFrBack(), NodeExpr)});
|
||||
varp->direction(VDirection::NONE); // Restore defaults
|
||||
varp->primaryIO(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+29
-29
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "V3String.h"
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
||||
@@ -34,11 +35,12 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
// function that takes a reference of this type to join multiple entities into one.
|
||||
template <typename T>
|
||||
class V3ConfigWildcardResolver final {
|
||||
using Map = std::map<const std::string, T>;
|
||||
|
||||
mutable V3Mutex m_mutex; // protects members
|
||||
Map m_mapWildcard VL_GUARDED_BY(m_mutex); // Wildcard strings to entities
|
||||
Map m_mapResolved VL_GUARDED_BY(m_mutex); // Resolved strings to converged entities
|
||||
// Pattern strings (wildcard, or simple name) to entities
|
||||
std::map<const std::string, T> m_mapPatterns VL_GUARDED_BY(m_mutex);
|
||||
// Resolved strings to converged entities - nullptr, iff none of the patterns applies
|
||||
std::map<const std::string, std::unique_ptr<T>> m_mapResolved VL_GUARDED_BY(m_mutex);
|
||||
|
||||
public:
|
||||
V3ConfigWildcardResolver() = default;
|
||||
~V3ConfigWildcardResolver() = default;
|
||||
@@ -48,41 +50,36 @@ public:
|
||||
VL_EXCLUDES(other.m_mutex) {
|
||||
V3LockGuard lock{m_mutex};
|
||||
V3LockGuard otherLock{other.m_mutex};
|
||||
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);
|
||||
// Clear the resolved cache, as 'other' might add new patterns that need to be applied as
|
||||
// well.
|
||||
m_mapResolved.clear();
|
||||
for (const auto& itr : other.m_mapPatterns) m_mapPatterns[itr.first].update(itr.second);
|
||||
}
|
||||
|
||||
// Access and create a (wildcard) entity
|
||||
// Access and create a pattern entry
|
||||
T& at(const string& name) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
V3LockGuard lock{m_mutex};
|
||||
// Don't store into wildcards if the name is not a wildcard string
|
||||
return m_mapWildcard[name];
|
||||
// We might be adding a new entry under this, so clear the cache.
|
||||
m_mapResolved.clear();
|
||||
return m_mapPatterns[name];
|
||||
}
|
||||
// Access an entity and resolve wildcards that match it
|
||||
// Access an entity and resolve patterns that match it
|
||||
T* resolve(const string& name) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
V3LockGuard lock{m_mutex};
|
||||
// Lookup if it was resolved before, typically not
|
||||
auto it = m_mapResolved.find(name);
|
||||
if (VL_UNLIKELY(it != m_mapResolved.end())) return &it->second;
|
||||
|
||||
T* newp = nullptr;
|
||||
// Cannot be resolved, create if matched
|
||||
|
||||
// Update this entity with all matches in the wildcards
|
||||
for (const auto& wildent : m_mapWildcard) {
|
||||
if (VString::wildmatch(name, wildent.first)) {
|
||||
if (!newp) {
|
||||
newp = &m_mapResolved[name]; // Emplace and get pointer
|
||||
const auto pair = m_mapResolved.emplace(name, nullptr);
|
||||
std::unique_ptr<T>& entryr = pair.first->second;
|
||||
// Resolve entry when first requested, cache the result
|
||||
if (pair.second) {
|
||||
// Update the entity with all matches in the patterns
|
||||
for (const auto& patEnt : m_mapPatterns) {
|
||||
if (VString::wildmatch(name, patEnt.first)) {
|
||||
if (!entryr) entryr.reset(new T{});
|
||||
entryr->update(patEnt.second);
|
||||
}
|
||||
newp->update(wildent.second);
|
||||
}
|
||||
}
|
||||
return newp;
|
||||
}
|
||||
// Flush on update
|
||||
void flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
V3LockGuard lock{m_mutex};
|
||||
m_mapResolved.clear();
|
||||
return entryr.get();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -203,6 +200,9 @@ public:
|
||||
modp->addStmtsp(nodep);
|
||||
}
|
||||
for (const auto& itr : m_modPragmas) {
|
||||
// Catch hier param modules to mark their attributes before they are
|
||||
// flagged dead in LinkDot.
|
||||
if (itr == VPragmaType::HIER_PARAMS) modp->hierParams(true);
|
||||
AstNode* const nodep = new AstPragma{modp->fileline(), itr};
|
||||
modp->addStmtsp(nodep);
|
||||
}
|
||||
@@ -336,6 +336,7 @@ public:
|
||||
}
|
||||
}
|
||||
bool waive(V3ErrorCode code, const string& match) {
|
||||
if (code.hardError()) return false;
|
||||
for (const auto& itr : m_waivers) {
|
||||
if ((code.isUnder(itr.first) || (itr.first == V3ErrorCode::I_LINT))
|
||||
&& VString::wildmatch(match, itr.second)) {
|
||||
@@ -508,7 +509,6 @@ void V3Config::addIgnore(V3ErrorCode code, bool on, const string& filename, int
|
||||
} else {
|
||||
V3ConfigResolver::s().files().at(filename).addIgnore(code, min, on);
|
||||
if (max) V3ConfigResolver::s().files().at(filename).addIgnore(code, max, !on);
|
||||
V3ConfigResolver::s().files().flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+21
-38
@@ -125,13 +125,13 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
|
||||
void updateBitRange(const AstShiftR* shiftp) {
|
||||
m_lsb += VN_AS(shiftp->rhsp(), Const)->toUInt();
|
||||
}
|
||||
int wordIdx() const { return m_wordIdx; }
|
||||
void wordIdx(int i) { m_wordIdx = i; }
|
||||
bool polarity() const { return m_polarity; }
|
||||
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; }
|
||||
bool missingWordSel() const {
|
||||
// When V3Expand is skipped, WordSel is not inserted.
|
||||
return m_refp->isWide() && m_wordIdx == -1;
|
||||
@@ -853,7 +853,8 @@ public:
|
||||
// of the masking node e.g. by the "AND with all ones" rule. If the result width happens
|
||||
// to be 1, we still need to ensure the AstAnd is not dropped, so use a wider mask in this
|
||||
// special case.
|
||||
const int maskWidth = resultWidth == 1 ? VL_IDATASIZE : resultWidth;
|
||||
const int maskWidth
|
||||
= std::max(resultp->width(), resultWidth == 1 ? VL_IDATASIZE : resultWidth);
|
||||
|
||||
// Apply final polarity flip
|
||||
if (needsFlip) {
|
||||
@@ -2170,26 +2171,17 @@ class ConstVisitor final : public VNVisitor {
|
||||
AstNodeExpr* srcp = streamp->lhsp()->unlinkFrBack();
|
||||
AstNodeDType* const srcDTypep = srcp->dtypep();
|
||||
if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)) {
|
||||
if (nodep->lhsp()->widthMin() > 64) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Assignment of stream of dynamic "
|
||||
"array to a variable of size greater than 64");
|
||||
}
|
||||
srcp = new AstCvtDynArrayToPacked{srcp->fileline(), srcp, srcDTypep};
|
||||
srcp = new AstCvtArrayToPacked{srcp->fileline(), srcp, nodep->dtypep()};
|
||||
} else if (VN_IS(srcDTypep, UnpackArrayDType)) {
|
||||
if (nodep->lhsp()->widthMin() > 64) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Assignment of stream of dynamic "
|
||||
"array to a variable of size greater than 64");
|
||||
}
|
||||
srcp = new AstCvtUnpackArrayToPacked{srcp->fileline(), srcp,
|
||||
nodep->lhsp()->dtypep()};
|
||||
srcp = new AstCvtArrayToPacked{srcp->fileline(), srcp, srcDTypep};
|
||||
// Handling the case where lhs is wider than rhs by inserting zeros. StreamL does
|
||||
// not require this, since the left streaming operator implicitly handles this.
|
||||
const uint32_t packedBits = nodep->lhsp()->widthMin();
|
||||
const uint32_t unpackBits
|
||||
const int packedBits = nodep->lhsp()->widthMin();
|
||||
const int unpackBits
|
||||
= srcDTypep->arrayUnpackedElements() * srcDTypep->subDTypep()->widthMin();
|
||||
const uint32_t offset = packedBits > unpackBits ? packedBits - unpackBits : 0;
|
||||
srcp = new AstShiftL{srcp->fileline(), srcp,
|
||||
new AstConst{srcp->fileline(), offset}, 64};
|
||||
new AstConst{srcp->fileline(), offset}, packedBits};
|
||||
}
|
||||
nodep->rhsp(srcp);
|
||||
VL_DO_DANGLING(pushDeletep(streamp), streamp);
|
||||
@@ -2211,19 +2203,18 @@ class ConstVisitor final : public VNVisitor {
|
||||
streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(), VSigning::UNSIGNED);
|
||||
}
|
||||
if (VN_IS(dstp->dtypep(), UnpackArrayDType)) {
|
||||
streamp
|
||||
= new AstCvtPackedToUnpackArray{nodep->fileline(), streamp, dstp->dtypep()};
|
||||
streamp = new AstCvtPackedToArray{nodep->fileline(), streamp, dstp->dtypep()};
|
||||
} else {
|
||||
UASSERT(sWidth >= dWidth, "sWidth >= dWidth should have caused an error earlier");
|
||||
if (dWidth == 0) {
|
||||
streamp
|
||||
= new AstCvtPackedToDynArray{nodep->fileline(), streamp, dstp->dtypep()};
|
||||
streamp = new AstCvtPackedToArray{nodep->fileline(), streamp, dstp->dtypep()};
|
||||
} else if (sWidth >= dWidth) {
|
||||
streamp = new AstSel{streamp->fileline(), streamp, sWidth - dWidth, dWidth};
|
||||
}
|
||||
}
|
||||
nodep->lhsp(dstp);
|
||||
nodep->rhsp(streamp);
|
||||
nodep->dtypep(dstp->dtypep());
|
||||
return true;
|
||||
} else if (m_doV && VN_IS(nodep->lhsp(), StreamR)) {
|
||||
// The right stream operator on lhs of assignment statement does
|
||||
@@ -2244,17 +2235,18 @@ class ConstVisitor final : public VNVisitor {
|
||||
srcp
|
||||
= new AstSel{streamp->fileline(), srcp, sWidth - dstBitWidth, dstBitWidth};
|
||||
}
|
||||
srcp = new AstCvtPackedToUnpackArray{nodep->fileline(), srcp, dstp->dtypep()};
|
||||
srcp = new AstCvtPackedToArray{nodep->fileline(), srcp, dstp->dtypep()};
|
||||
} else {
|
||||
UASSERT(sWidth >= dWidth, "sWidth >= dWidth should have caused an error earlier");
|
||||
if (dWidth == 0) {
|
||||
srcp = new AstCvtPackedToDynArray{nodep->fileline(), srcp, dstp->dtypep()};
|
||||
srcp = new AstCvtPackedToArray{nodep->fileline(), srcp, dstp->dtypep()};
|
||||
} else if (sWidth >= dWidth) {
|
||||
srcp = new AstSel{streamp->fileline(), srcp, sWidth - dWidth, dWidth};
|
||||
}
|
||||
}
|
||||
nodep->lhsp(dstp);
|
||||
nodep->rhsp(srcp);
|
||||
nodep->dtypep(dstp->dtypep());
|
||||
VL_DO_DANGLING(pushDeletep(streamp), streamp);
|
||||
// Further reduce, any of the nodes may have more reductions.
|
||||
return true;
|
||||
@@ -2263,21 +2255,10 @@ class ConstVisitor final : public VNVisitor {
|
||||
AstStreamL* streamp = VN_AS(nodep->rhsp(), StreamL);
|
||||
AstNodeExpr* const srcp = streamp->lhsp();
|
||||
const AstNodeDType* const srcDTypep = srcp->dtypep();
|
||||
if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)) {
|
||||
if (lhsDtypep->widthMin() > 64) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Assignment of stream of dynamic "
|
||||
"array to a variable of size greater than 64");
|
||||
}
|
||||
srcp->unlinkFrBack();
|
||||
streamp->lhsp(new AstCvtDynArrayToPacked{srcp->fileline(), srcp, lhsDtypep});
|
||||
streamp->dtypeFrom(lhsDtypep);
|
||||
} else if (VN_IS(srcDTypep, UnpackArrayDType)) {
|
||||
if (lhsDtypep->widthMin() > 64) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Assignment of stream of unpacked "
|
||||
"array to a variable of size greater than 64");
|
||||
}
|
||||
srcp->unlinkFrBack();
|
||||
streamp->lhsp(new AstCvtUnpackArrayToPacked{srcp->fileline(), srcp, lhsDtypep});
|
||||
if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)
|
||||
|| VN_IS(srcDTypep, UnpackArrayDType)) {
|
||||
streamp->lhsp(new AstCvtArrayToPacked{srcp->fileline(), srcp->unlinkFrBack(),
|
||||
nodep->dtypep()});
|
||||
streamp->dtypeFrom(lhsDtypep);
|
||||
}
|
||||
} else if (m_doV && replaceAssignMultiSel(nodep)) {
|
||||
@@ -2849,6 +2830,8 @@ class ConstVisitor final : public VNVisitor {
|
||||
void visit(AstSenItem* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (m_doNConst
|
||||
&& !v3Global.opt.timing().isSetTrue() // If --timing, V3Sched would turn this into an
|
||||
// infinite loop. See #5080
|
||||
&& (VN_IS(nodep->sensp(), Const) || VN_IS(nodep->sensp(), EnumItemRef)
|
||||
|| (nodep->varrefp() && nodep->varrefp()->varp()->isParam()))) {
|
||||
// Constants in sensitivity lists may be removed (we'll simplify later)
|
||||
|
||||
+1
-1
@@ -311,7 +311,7 @@ class DeadVisitor final : public VNVisitor {
|
||||
// still get deleted.
|
||||
}
|
||||
void visit(AstNode* nodep) override {
|
||||
if (nodep->isOutputter()) m_sideEffect = true;
|
||||
if (!m_sideEffect && !nodep->isPure()) m_sideEffect = true;
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
}
|
||||
|
||||
+4
-12
@@ -120,8 +120,6 @@ class DelayedVisitor final : public VNVisitor {
|
||||
AstVarScope* delayVscp = nullptr;
|
||||
// Points to AstActive block of the shadow variable 'delayVscp/post block 'postp'
|
||||
AstActive* activep = nullptr;
|
||||
// Post block for this variable used in suspendable processes
|
||||
AstAlwaysPost* suspPostp = nullptr;
|
||||
// Post block for this variable
|
||||
AstAlwaysPost* postp = nullptr;
|
||||
// First reference encountered to the VarScope
|
||||
@@ -386,17 +384,11 @@ class DelayedVisitor final : public VNVisitor {
|
||||
}
|
||||
|
||||
// Get/Create 'Post' ordered block to commit the delayed value
|
||||
AstAlwaysPost* postp = m_vscpAux(vscp).suspPostp;
|
||||
if (!postp) {
|
||||
postp = new AstAlwaysPost{flp};
|
||||
if (!m_procp->user2p()) {
|
||||
m_procp->user2p(createActiveLike(lhsComponents.refp->fileline(), m_activep));
|
||||
// TODO: Somebody needs to explain me how it makes sense to set this
|
||||
// inside this 'if'. Shouldn't it be outside this 'if'? See #5084
|
||||
m_vscpAux(vscp).suspPostp = postp;
|
||||
}
|
||||
VN_AS(m_procp->user2p(), Active)->addStmtsp(postp);
|
||||
AstAlwaysPost* postp = new AstAlwaysPost{flp};
|
||||
if (!m_procp->user2p()) {
|
||||
m_procp->user2p(createActiveLike(lhsComponents.refp->fileline(), m_activep));
|
||||
}
|
||||
VN_AS(m_procp->user2p(), Active)->addStmtsp(postp);
|
||||
|
||||
// Create the flag denoting an update is pending - no reuse here
|
||||
AstVarScope* const setVscp = createNewVarScope(vscp, "__VdlySet" + baseName, 1);
|
||||
|
||||
+1
-1
@@ -193,7 +193,7 @@ class DescopeVisitor final : public VNVisitor {
|
||||
// to come up with some return value
|
||||
// newfuncp->addStmtsp(new AstDisplay(newfuncp->fileline(),
|
||||
// VDisplayType::DT_WARNING,
|
||||
// string{"%%Error: "}+name+"() called with bad
|
||||
// "%%Error: "s+name+"() called with bad
|
||||
// scope", nullptr));
|
||||
// newfuncp->addStmtsp(new AstStop(newfuncp->fileline()));
|
||||
if (debug() >= 9) newfuncp->dumpTree("- newfunc: ");
|
||||
|
||||
+8
-9
@@ -189,19 +189,19 @@ void DfgGraph::dumpDot(std::ostream& os, const string& label) const {
|
||||
os << "}\n";
|
||||
}
|
||||
|
||||
void DfgGraph::dumpDotFile(const string& fileName, const string& label) const {
|
||||
void DfgGraph::dumpDotFile(const string& filename, const string& label) const {
|
||||
// This generates a file used by graphviz, https://www.graphviz.org
|
||||
// "hardcoded" parameters:
|
||||
const std::unique_ptr<std::ofstream> os{V3File::new_ofstream(fileName)};
|
||||
if (os->fail()) v3fatal("Cannot write to file: " << fileName);
|
||||
const std::unique_ptr<std::ofstream> os{V3File::new_ofstream(filename)};
|
||||
if (os->fail()) v3fatal("Cannot write to file: " << filename);
|
||||
dumpDot(*os.get(), label);
|
||||
os->close();
|
||||
}
|
||||
|
||||
void DfgGraph::dumpDotFilePrefixed(const string& label) const {
|
||||
string fileName = name();
|
||||
if (!label.empty()) fileName += "-" + label;
|
||||
dumpDotFile(v3Global.debugFilename(fileName) + ".dot", label);
|
||||
string filename = name();
|
||||
if (!label.empty()) filename += "-" + label;
|
||||
dumpDotFile(v3Global.debugFilename(filename) + ".dot", label);
|
||||
}
|
||||
|
||||
// Dump upstream logic cone starting from given vertex
|
||||
@@ -360,8 +360,8 @@ bool DfgVertex::equals(const DfgVertex& that, EqualsCache& cache) const {
|
||||
|
||||
const auto key = (this < &that) ? EqualsCache::key_type{this, &that} //
|
||||
: EqualsCache::key_type{&that, this};
|
||||
// Note: the recursive invocation can cause a re-hash of the cache which invalidates iterators
|
||||
uint8_t result = cache[key];
|
||||
// Note: the recursive invocation can cause a re-hash but that will not invalidate references
|
||||
uint8_t& result = cache[key];
|
||||
if (!result) {
|
||||
result = 2; // Assume equals
|
||||
auto thisPair = this->sourceEdges();
|
||||
@@ -380,7 +380,6 @@ bool DfgVertex::equals(const DfgVertex& that, EqualsCache& cache) const {
|
||||
break;
|
||||
}
|
||||
}
|
||||
cache[key] = result;
|
||||
}
|
||||
return result >> 1;
|
||||
}
|
||||
|
||||
+4
-4
@@ -705,15 +705,15 @@ public:
|
||||
// Dump graph in Graphviz format into the given stream 'os'. 'label' is added to the name of
|
||||
// the graph which is included in the output.
|
||||
void dumpDot(std::ostream& os, const string& label = "") const VL_MT_DISABLED;
|
||||
// Dump graph in Graphviz format into a new file with the given 'fileName'. 'label' is added to
|
||||
// Dump graph in Graphviz format into a new file with the given 'filename'. 'label' is added to
|
||||
// the name of the graph which is included in the output.
|
||||
void dumpDotFile(const string& fileName, const string& label = "") const VL_MT_DISABLED;
|
||||
void dumpDotFile(const string& filename, const string& label = "") const VL_MT_DISABLED;
|
||||
// Dump graph in Graphviz format into a new automatically numbered debug file. 'label' is
|
||||
// added to the name of the graph, which is included in the file name and the output.
|
||||
void dumpDotFilePrefixed(const string& label = "") const VL_MT_DISABLED;
|
||||
// Dump upstream (source) logic cone starting from given vertex into a file with the given
|
||||
// 'fileName'. 'name' is the name of the graph, which is included in the output.
|
||||
void dumpDotUpstreamCone(const string& fileName, const DfgVertex& vtx,
|
||||
// 'filename'. 'name' is the name of the graph, which is included in the output.
|
||||
void dumpDotUpstreamCone(const string& filename, const DfgVertex& vtx,
|
||||
const string& name = "") const VL_MT_DISABLED;
|
||||
// Dump all individual logic cones driving external variables in Graphviz format into separate
|
||||
// new automatically numbered debug files. 'label' is added to the name of the graph, which is
|
||||
|
||||
+3
-6
@@ -160,13 +160,10 @@ void V3DfgPasses::inlineVars(DfgGraph& dfg) {
|
||||
DfgVertex* const driverp = varp->source(0);
|
||||
|
||||
// We must keep the original driver in certain cases, when swapping them would
|
||||
// not be functionally or technically (implementation reasons) equivalent
|
||||
// not be functionally or technically (implementation reasons) equivalent:
|
||||
// 1. If driven from a SystemC variable (assignment is non-trivial)
|
||||
if (DfgVertexVar* const driverVarp = driverp->cast<DfgVarPacked>()) {
|
||||
const AstVar* const astVarp = driverVarp->varp();
|
||||
// If driven from a SystemC variable
|
||||
if (astVarp->isSc()) continue;
|
||||
// If the variable is forced
|
||||
if (astVarp->isForced()) continue;
|
||||
if (driverVarp->varp()->isSc()) continue;
|
||||
}
|
||||
|
||||
varp->forEachSinkEdge([=](DfgEdge& edge) { edge.relinkSource(driverp); });
|
||||
|
||||
+47
-2
@@ -35,6 +35,42 @@ EmitCParentModule::EmitCParentModule() {
|
||||
setAll(v3Global.rootp()->constPoolp()->modp());
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// EmitCBase implementation
|
||||
|
||||
string EmitCBase::prefixNameProtect(const AstNode* nodep) VL_MT_STABLE {
|
||||
const string prefix = v3Global.opt.modPrefix() + "_" + VIdProtect::protect(nodep->name());
|
||||
// If all-uppercase prefix conflicts with a previous usage of the
|
||||
// prefix with different capitalization, rename to avoid conflict.
|
||||
// This is to support OSes where filename compares are non-case significant.
|
||||
// STATIC:
|
||||
static V3Mutex s_mutex;
|
||||
const V3LockGuard lock{s_mutex}; // Otherwise map access is unsafe
|
||||
static std::map<std::string, std::string> s_memoized;
|
||||
static std::map<std::string, std::string> s_ucToPrefix;
|
||||
// Memoize results
|
||||
const auto mit = s_memoized.find(prefix);
|
||||
if (mit != s_memoized.end()) return mit->second;
|
||||
//
|
||||
// Check capitalization
|
||||
const string prefixUpper = VString::upcase(prefix);
|
||||
string result;
|
||||
{
|
||||
const auto it = s_ucToPrefix.find(prefixUpper);
|
||||
if (it == s_ucToPrefix.end()) {
|
||||
s_ucToPrefix.emplace(prefixUpper, prefix);
|
||||
result = prefix;
|
||||
} else if (it->second == prefix) {
|
||||
result = prefix; // Same capitialization as last time
|
||||
} else {
|
||||
VHashSha256 hash{prefix};
|
||||
result = prefix + "__Vphsh" + hash.digestSymbol();
|
||||
}
|
||||
}
|
||||
s_memoized.emplace(prefix, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// EmitCBaseVisitor implementation
|
||||
|
||||
@@ -211,11 +247,11 @@ void EmitCBaseVisitorConst::emitVarDecl(const AstVar* nodep, bool asRef) {
|
||||
if (nodep->isWide()) puts("," + cvtToStr(nodep->widthWords()));
|
||||
puts(");\n");
|
||||
} else {
|
||||
// strings and other fundamental c types
|
||||
// Strings and other fundamental C types
|
||||
if (nodep->isFuncLocal() && nodep->isString()) {
|
||||
const string name = nodep->name();
|
||||
const string suffix = V3Task::dpiTemporaryVarSuffix();
|
||||
// string temporary variable for DPI-C needs to be static because c_str() will be
|
||||
// String temporary variable for DPI-C needs to be static because c_str() will be
|
||||
// passed to C code and the lifetime of the variable must be long enough. See also
|
||||
// Issue 2622.
|
||||
const bool beStatic = name.size() >= suffix.size()
|
||||
@@ -227,6 +263,15 @@ void EmitCBaseVisitorConst::emitVarDecl(const AstVar* nodep, bool asRef) {
|
||||
}
|
||||
}
|
||||
|
||||
void EmitCBaseVisitorConst::emitVarAccessors(const AstVar* nodep) {
|
||||
assert(nodep->name().rfind("__Vm_sig_") == 0 && nodep->isIO());
|
||||
const string privateName = nodep->name();
|
||||
const string publicName = nodep->name().substr(strlen("__Vm_sig_"));
|
||||
|
||||
puts("decltype("s + privateName + ") "s + publicName + "() {return "s + privateName + ";}\n");
|
||||
puts("void "s + publicName + "(decltype(" + privateName + ") v) {"s + privateName + "=v;}\n");
|
||||
}
|
||||
|
||||
void EmitCBaseVisitorConst::emitModCUse(const AstNodeModule* modp, VUseType useType) {
|
||||
bool nl = false;
|
||||
forModCUse(modp, useType, [&](string entry) {
|
||||
|
||||
+3
-3
@@ -66,9 +66,8 @@ public:
|
||||
static string topClassName() VL_MT_SAFE { // Return name of top wrapper module
|
||||
return v3Global.opt.prefix();
|
||||
}
|
||||
static string prefixNameProtect(const AstNode* nodep) VL_MT_STABLE { // C++ name with prefix
|
||||
return v3Global.opt.modPrefix() + "_" + VIdProtect::protect(nodep->name());
|
||||
}
|
||||
// Return C++ class name for a module/class object
|
||||
static string prefixNameProtect(const AstNode* nodep) VL_MT_STABLE;
|
||||
static bool isAnonOk(const AstVar* varp) {
|
||||
AstNodeDType* const dtp = varp->dtypep()->skipRefp();
|
||||
return v3Global.opt.compLimitMembers() != 0 // Enabled
|
||||
@@ -118,6 +117,7 @@ public:
|
||||
void emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp, bool withScope);
|
||||
void emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* modp, bool cLinkage = false);
|
||||
void emitVarDecl(const AstVar* nodep, bool asRef = false);
|
||||
void emitVarAccessors(const AstVar* nodep);
|
||||
template <typename F>
|
||||
static void forModCUse(const AstNodeModule* modp, VUseType useType, F action) {
|
||||
for (AstNode* itemp = modp->stmtsp(); itemp; itemp = itemp->nextp()) {
|
||||
|
||||
@@ -48,8 +48,7 @@ protected:
|
||||
VL_RESTORER(m_inUnpacked);
|
||||
VL_RESTORER(m_unpackedWord);
|
||||
m_inUnpacked = true;
|
||||
if (const AstAssocArrayDType* const dtypep
|
||||
= VN_CAST(nodep->dtypep()->skipRefp(), AssocArrayDType)) {
|
||||
if (VN_IS(nodep->dtypep()->skipRefp(), AssocArrayDType)) {
|
||||
// Note the double {{ initializer. The first { starts the initializer of the
|
||||
// VlUnpacked, and the second starts the initializer of m_storage within the
|
||||
// VlUnpacked.
|
||||
|
||||
+10
-6
@@ -274,9 +274,9 @@ void EmitCFunc::displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const
|
||||
double dchars = mantissabits / 3.321928094887362 + 1.0;
|
||||
if (fmtLetter == 'd') dchars++; // space for sign
|
||||
const int nchars = int(dchars);
|
||||
pfmt = string{"%"} + cvtToStr(nchars) + fmtLetter;
|
||||
pfmt = "%"s + cvtToStr(nchars) + fmtLetter;
|
||||
} else {
|
||||
pfmt = string{"%"} + vfmt + fmtLetter;
|
||||
pfmt = "%"s + vfmt + fmtLetter;
|
||||
}
|
||||
m_emitDispState.pushFormat(pfmt);
|
||||
if (!ignore) {
|
||||
@@ -445,8 +445,12 @@ void EmitCFunc::emitDereference(AstNode* nodep, const string& pointer) {
|
||||
putns(nodep, pointer.substr(2, pointer.length() - 3));
|
||||
puts(".");
|
||||
} else {
|
||||
putns(nodep, pointer);
|
||||
puts("->");
|
||||
if (pointer == "vlSelf" && m_usevlSelfRef) {
|
||||
puts("vlSelfRef.");
|
||||
} else {
|
||||
putns(nodep, pointer);
|
||||
puts("->");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -691,7 +695,7 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP
|
||||
} else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
UASSERT_OBJ(adtypep->hi() >= adtypep->lo(), varp,
|
||||
"Should have swapped msb & lsb earlier.");
|
||||
const string ivar = string{"__Vi"} + cvtToStr(depth);
|
||||
const string ivar = "__Vi"s + cvtToStr(depth);
|
||||
const string pre = ("for (int " + ivar + " = " + cvtToStr(0) + "; " + ivar + " < "
|
||||
+ cvtToStr(adtypep->elementsConst()) + "; ++" + ivar + ") {\n");
|
||||
const string below = emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(),
|
||||
@@ -764,7 +768,7 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP
|
||||
}
|
||||
return out;
|
||||
}
|
||||
} else {
|
||||
} else { // LCOV_EXCL_BR_LINE
|
||||
v3fatalSrc("Unknown node type in reset generator: " << varp->prettyTypeName());
|
||||
}
|
||||
return "";
|
||||
|
||||
+46
-60
@@ -148,6 +148,7 @@ class EmitCFunc VL_NOT_FINAL : public EmitCConstInit {
|
||||
protected:
|
||||
EmitCLazyDecls m_lazyDecls; // Visitor for emitting lazy declarations
|
||||
bool m_useSelfForThis = false; // Replace "this" with "vlSelf"
|
||||
bool m_usevlSelfRef = false; // Use vlSelfRef reference instead of vlSelf pointer
|
||||
const AstNodeModule* m_modp = nullptr; // Current module being emitted
|
||||
const AstCFunc* m_cfuncp = nullptr; // Current function being emitted
|
||||
bool m_instantiatesOwnProcess = false;
|
||||
@@ -192,10 +193,10 @@ public:
|
||||
}
|
||||
void emitScIQW(AstVar* nodep) {
|
||||
UASSERT_OBJ(nodep->isSc(), nodep, "emitting SystemC operator on non-SC variable");
|
||||
puts(nodep->isScBigUint() ? "SB"
|
||||
: nodep->isScUint() ? "SU"
|
||||
: nodep->isScBv() ? "SW"
|
||||
: (nodep->isScQuad() ? "SQ" : "SI"));
|
||||
puts(nodep->isScBigUint() ? "SB"
|
||||
: (nodep->isScUint() || nodep->isScUintBool()) ? "SU"
|
||||
: nodep->isScBv() ? "SW"
|
||||
: (nodep->isScQuad() ? "SQ" : "SI"));
|
||||
}
|
||||
void emitDatap(AstNode* nodep) {
|
||||
// When passing to a function with va_args the compiler doesn't
|
||||
@@ -343,6 +344,21 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (m_useSelfForThis) {
|
||||
m_usevlSelfRef = true;
|
||||
/*
|
||||
* Using reference to the vlSelf pointer will help the C++
|
||||
* compiler to have dereferenceable hints, which can help to
|
||||
* reduce the need for branch instructions in the generated
|
||||
* code to allow the compiler to generate load store after the
|
||||
* if condition (including short-circuit evaluation)
|
||||
* speculatively and also reduce the data cache pollution when
|
||||
* executing in the wrong path to make verilator-generated code
|
||||
* run faster.
|
||||
*/
|
||||
puts("auto &vlSelfRef = std::ref(*vlSelf).get();\n");
|
||||
}
|
||||
|
||||
if (nodep->initsp()) {
|
||||
putsDecoration(nodep, "// Init\n");
|
||||
iterateAndNextConstNull(nodep->initsp());
|
||||
@@ -358,6 +374,8 @@ public:
|
||||
iterateAndNextConstNull(nodep->finalsp());
|
||||
}
|
||||
|
||||
m_usevlSelfRef = false;
|
||||
|
||||
puts("}\n");
|
||||
if (nodep->ifdef() != "") puts("#endif // " + nodep->ifdef() + "\n");
|
||||
}
|
||||
@@ -367,32 +385,9 @@ public:
|
||||
emitVarDecl(nodep);
|
||||
}
|
||||
|
||||
void visit(AstCvtDynArrayToPacked* nodep) override {
|
||||
putns(nodep, "VL_DYN_TO_");
|
||||
emitIQW(nodep);
|
||||
const AstNodeDType* const elemDTypep = nodep->fromp()->dtypep()->subDTypep();
|
||||
putns(elemDTypep, "<");
|
||||
putbs(elemDTypep->cType("", false, false));
|
||||
puts(">(");
|
||||
iterateAndNextConstNull(nodep->fromp());
|
||||
puts(", ");
|
||||
putns(elemDTypep, cvtToStr(elemDTypep->widthMin()));
|
||||
puts(")");
|
||||
}
|
||||
|
||||
void visit(AstCvtUnpackArrayToPacked* nodep) override {
|
||||
putns(nodep, "VL_UNPACK_TO_");
|
||||
emitIQW(nodep);
|
||||
const AstNodeDType* const elemDTypep = nodep->fromp()->dtypep()->subDTypep();
|
||||
putns(elemDTypep, "<");
|
||||
putbs(elemDTypep->cType("", false, false));
|
||||
puts(",");
|
||||
puts(cvtToStr(nodep->fromp()->dtypep()->arrayUnpackedElements()));
|
||||
puts(">(");
|
||||
iterateAndNextConstNull(nodep->fromp());
|
||||
puts(", ");
|
||||
putns(elemDTypep, cvtToStr(elemDTypep->widthMin()));
|
||||
puts(")");
|
||||
void visit(AstCvtArrayToPacked* nodep) override {
|
||||
AstNodeDType* const elemDTypep = nodep->fromp()->dtypep()->subDTypep();
|
||||
emitOpName(nodep, nodep->emitC(), nodep->fromp(), elemDTypep, nullptr);
|
||||
}
|
||||
|
||||
void visit(AstNodeAssign* nodep) override {
|
||||
@@ -449,30 +444,18 @@ public:
|
||||
puts(cvtToStr(nodep->widthMin()) + ",");
|
||||
iterateAndNextConstNull(nodep->lhsp());
|
||||
puts(", ");
|
||||
} else if (const AstCvtPackedToDynArray* const castp
|
||||
= VN_CAST(nodep->rhsp(), CvtPackedToDynArray)) {
|
||||
putns(castp, "VL_ASSIGN_DYN_Q<");
|
||||
putbs(castp->dtypep()->subDTypep()->cType("", false, false));
|
||||
puts(">(");
|
||||
iterateAndNextConstNull(nodep->lhsp());
|
||||
puts(", ");
|
||||
} else if (const AstCvtPackedToArray* const castp
|
||||
= VN_CAST(nodep->rhsp(), CvtPackedToArray)) {
|
||||
putns(castp, "VL_UNPACK_");
|
||||
emitIQW(nodep->dtypep()->subDTypep());
|
||||
emitIQW(castp->fromp());
|
||||
puts("(");
|
||||
putns(castp->dtypep(), cvtToStr(castp->dtypep()->subDTypep()->widthMin()));
|
||||
puts(", ");
|
||||
puts(cvtToStr(castp->fromp()->widthMin()));
|
||||
puts(", ");
|
||||
rhs = false;
|
||||
iterateAndNextConstNull(castp->fromp());
|
||||
} else if (const AstCvtPackedToUnpackArray* const castp
|
||||
= VN_CAST(nodep->rhsp(), CvtPackedToUnpackArray)) {
|
||||
putns(castp, "VL_ASSIGN_UNPACK_Q<");
|
||||
putbs(castp->dtypep()->subDTypep()->cType("", false, false));
|
||||
puts(", ");
|
||||
puts(cvtToStr(castp->dtypep()->arrayUnpackedElements()));
|
||||
puts(">(");
|
||||
iterateAndNextConstNull(nodep->lhsp());
|
||||
puts(", ");
|
||||
putns(castp->dtypep(), cvtToStr(castp->dtypep()->subDTypep()->widthMin()));
|
||||
puts(", ");
|
||||
rhs = false;
|
||||
iterateAndNextConstNull(castp->fromp());
|
||||
} else if (nodep->isWide() && VN_IS(nodep->lhsp(), VarRef) //
|
||||
@@ -620,7 +603,7 @@ public:
|
||||
if (!v3Global.opt.protectIds()) return;
|
||||
}
|
||||
if (!(nodep->protect() && v3Global.opt.protectIds())) {
|
||||
putsDecoration(nodep, string{"// "} + nodep->name() + at + "\n");
|
||||
putsDecoration(nodep, "// "s + nodep->name() + at + "\n");
|
||||
}
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
@@ -1280,8 +1263,7 @@ public:
|
||||
return;
|
||||
}
|
||||
}
|
||||
emitOpName(nodep, "VL_STREAML_%nq%lq%rq(%lw, %P, %li, %ri)", nodep->lhsp(), nodep->rhsp(),
|
||||
nullptr);
|
||||
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), nullptr);
|
||||
}
|
||||
void visit(AstCastDynamic* nodep) override {
|
||||
putnbs(nodep, "VL_CAST_DYNAMIC(");
|
||||
@@ -1402,9 +1384,12 @@ public:
|
||||
void visit(AstConsDynArray* nodep) override {
|
||||
putnbs(nodep, nodep->dtypep()->cType("", false, false));
|
||||
if (!nodep->lhsp()) {
|
||||
putns(nodep, "()");
|
||||
putns(nodep, "{}");
|
||||
} else {
|
||||
putns(nodep, "::cons(");
|
||||
puts("::cons");
|
||||
puts(nodep->lhsIsValue() ? "V" : "C");
|
||||
if (nodep->rhsp()) puts(nodep->rhsIsValue() ? "V" : "C");
|
||||
puts("(");
|
||||
iterateAndNextConstNull(nodep->lhsp());
|
||||
if (nodep->rhsp()) {
|
||||
puts(", ");
|
||||
@@ -1433,9 +1418,12 @@ public:
|
||||
void visit(AstConsQueue* nodep) override {
|
||||
putnbs(nodep, nodep->dtypep()->cType("", false, false));
|
||||
if (!nodep->lhsp()) {
|
||||
puts("()");
|
||||
puts("{}");
|
||||
} else {
|
||||
puts("::cons(");
|
||||
puts("::cons");
|
||||
puts(nodep->lhsIsValue() ? "V" : "C");
|
||||
if (nodep->rhsp()) puts(nodep->rhsIsValue() ? "V" : "C");
|
||||
puts("(");
|
||||
iterateAndNextConstNull(nodep->lhsp());
|
||||
if (nodep->rhsp()) {
|
||||
puts(", ");
|
||||
@@ -1457,15 +1445,13 @@ public:
|
||||
}
|
||||
|
||||
// Default
|
||||
void visit(AstNode* nodep) override {
|
||||
putns(nodep, string{"\n???? // "} + nodep->prettyTypeName() + "\n");
|
||||
void visit(AstNode* nodep) override { // LCOV_EXCL_START
|
||||
putns(nodep, "\n???? // "s + nodep->prettyTypeName() + "\n");
|
||||
iterateChildrenConst(nodep);
|
||||
// LCOV_EXCL_START
|
||||
if (!v3Global.opt.lintOnly()) { // An internal problem, so suppress
|
||||
nodep->v3fatalSrc("Unknown node type reached emitter: " << nodep->prettyTypeName());
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
EmitCFunc()
|
||||
: m_lazyDecls(*this) {}
|
||||
|
||||
+3
-3
@@ -398,7 +398,7 @@ class EmitCImp final : EmitCFunc {
|
||||
const int vecnum = vects++;
|
||||
UASSERT_OBJ(arrayp->hi() >= arrayp->lo(), varp,
|
||||
"Should have swapped msb & lsb earlier.");
|
||||
const string ivar = string{"__Vi"} + cvtToStr(vecnum);
|
||||
const string ivar = "__Vi"s + cvtToStr(vecnum);
|
||||
puts("for (int __Vi" + cvtToStr(vecnum) + " = " + cvtToStr(0));
|
||||
puts("; " + ivar + " < " + cvtToStr(arrayp->elementsConst()));
|
||||
puts("; ++" + ivar + ") {\n");
|
||||
@@ -412,7 +412,7 @@ class EmitCImp final : EmitCFunc {
|
||||
if (elementp->isWide()
|
||||
&& !(basicp && basicp->keyword() == VBasicDTypeKwd::STRING)) {
|
||||
const int vecnum = vects++;
|
||||
const string ivar = string{"__Vi"} + cvtToStr(vecnum);
|
||||
const string ivar = "__Vi"s + cvtToStr(vecnum);
|
||||
puts("for (int __Vi" + cvtToStr(vecnum) + " = " + cvtToStr(0));
|
||||
puts("; " + ivar + " < " + cvtToStr(elementp->widthWords()));
|
||||
puts("; ++" + ivar + ") {\n");
|
||||
@@ -694,7 +694,7 @@ class EmitCTrace final : EmitCFunc {
|
||||
const AstVarRef* const varrefp = VN_CAST(nodep->declp()->valuep(), VarRef);
|
||||
if (!varrefp) return false;
|
||||
AstVar* const varp = varrefp->varp();
|
||||
return varp->isSc() && varp->isScUint();
|
||||
return varp->isSc() && (varp->isScUint() || varp->isScUintBool());
|
||||
}
|
||||
|
||||
void emitTraceInitOne(AstTraceDecl* nodep, int enumNum) {
|
||||
|
||||
+8
-5
@@ -81,7 +81,10 @@ class CMakeEmitter final {
|
||||
|
||||
*of << "\n### Constants...\n";
|
||||
cmake_set(*of, "PERL", V3OutFormatter::quoteNameControls(V3Options::getenvPERL()),
|
||||
"FILEPATH", "Perl executable (from $PERL)");
|
||||
"FILEPATH", "Perl executable (from $PERL, defaults to 'perl' if not set)");
|
||||
cmake_set(*of, "PYTHON3", V3OutFormatter::quoteNameControls(V3Options::getenvPYTHON3()),
|
||||
"FILEPATH",
|
||||
"Python3 executable (from $PYTHON3, defaults to 'python3' if not set)");
|
||||
cmake_set(*of, "VERILATOR_ROOT",
|
||||
V3OutFormatter::quoteNameControls(V3Options::getenvVERILATOR_ROOT()), "PATH",
|
||||
"Path to Verilator kit (from $VERILATOR_ROOT)");
|
||||
@@ -212,7 +215,7 @@ class CMakeEmitter final {
|
||||
<< hblockp->modp()->name() << " DIRECTORY "
|
||||
<< v3Global.opt.makeDir() + "/" + prefix << " SOURCES ";
|
||||
for (const auto& childr : children) {
|
||||
*of << " " << v3Global.opt.makeDir() + "/" + childr->hierWrapper(true);
|
||||
*of << " " << v3Global.opt.makeDir() + "/" + childr->hierWrapperFilename(true);
|
||||
}
|
||||
*of << " ";
|
||||
const string vFile = hblockp->vFileIfNecessary();
|
||||
@@ -220,7 +223,7 @@ class CMakeEmitter final {
|
||||
const V3StringList& vFiles = v3Global.opt.vFiles();
|
||||
for (const string& i : vFiles) *of << V3Os::filenameRealPath(i) << " ";
|
||||
*of << " VERILATOR_ARGS ";
|
||||
*of << "-f " << hblockp->commandArgsFileName(true)
|
||||
*of << "-f " << hblockp->commandArgsFilename(true)
|
||||
<< " -CFLAGS -fPIC" // hierarchical block will be static, but may be linked
|
||||
// with .so
|
||||
<< ")\n";
|
||||
@@ -230,11 +233,11 @@ class CMakeEmitter final {
|
||||
<< v3Global.rootp()->topModulep()->name() << " DIRECTORY "
|
||||
<< v3Global.opt.makeDir() << " SOURCES ";
|
||||
for (const auto& itr : *planp) {
|
||||
*of << " " << v3Global.opt.makeDir() + "/" + itr.second->hierWrapper(true);
|
||||
*of << " " << v3Global.opt.makeDir() + "/" + itr.second->hierWrapperFilename(true);
|
||||
}
|
||||
*of << " " << cmake_list(v3Global.opt.vFiles());
|
||||
*of << " VERILATOR_ARGS ";
|
||||
*of << "-f " << planp->topCommandArgsFileName(true);
|
||||
*of << "-f " << planp->topCommandArgsFilename(true);
|
||||
*of << ")\n";
|
||||
}
|
||||
}
|
||||
|
||||
+13
-2
@@ -118,6 +118,17 @@ class EmitCModel final : public EmitCFunc {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (v3Global.opt.emitAccessors()) {
|
||||
puts("\n// ACCESSORS\n"
|
||||
"// The application code should use these methods to\n"
|
||||
"// propagate new values into/out from the Verilated model\n"
|
||||
"// instead of using signal variables directly.\n");
|
||||
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
if (const AstVar* const varp = VN_CAST(nodep, Var)) {
|
||||
if (varp->isPrimaryIO()) emitVarAccessors(varp);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (optSystemC() && v3Global.usesTiming()) puts("sc_core::sc_event trigger_eval;\n");
|
||||
|
||||
// Cells instantiated by the top level (for access to /* verilator public */)
|
||||
@@ -540,11 +551,11 @@ class EmitCModel final : public EmitCFunc {
|
||||
"0.\");\n");
|
||||
puts("}\n");
|
||||
puts("vlSymsp->__Vm_baseCode = code;\n");
|
||||
puts("tracep->pushPrefix(std::string{vlSymsp->name()}, "
|
||||
puts("if (strlen(vlSymsp->name())) tracep->pushPrefix(std::string{vlSymsp->name()}, "
|
||||
"VerilatedTracePrefixType::SCOPE_MODULE);\n");
|
||||
puts(topModNameProtected + "__" + protect("trace_decl_types") + "(tracep);\n");
|
||||
puts(topModNameProtected + "__" + protect("trace_init_top") + "(vlSelf, tracep);\n");
|
||||
puts("tracep->popPrefix();\n");
|
||||
puts("if (strlen(vlSymsp->name())) tracep->popPrefix();\n");
|
||||
puts("}\n");
|
||||
|
||||
// Forward declaration
|
||||
|
||||
@@ -57,6 +57,11 @@ public:
|
||||
of.puts("#include \"" + symClassName() + ".h\"\n");
|
||||
of.puts("#include \"" + topClassName() + ".h\"\n");
|
||||
|
||||
of.puts("\n// Additional include files added using '--compiler-include'\n");
|
||||
for (const string& filename : v3Global.opt.compilerIncludes()) {
|
||||
of.puts("#include \"" + filename + "\"\n");
|
||||
}
|
||||
|
||||
of.putsEndGuard();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -537,7 +537,7 @@ void EmitCSyms::emitSymHdr() {
|
||||
puts("\n// CONSTRUCTORS\n");
|
||||
puts(symClassName() + "(VerilatedContext* contextp, const char* namep, " + topClassName()
|
||||
+ "* modelp);\n");
|
||||
puts(string{"~"} + symClassName() + "();\n");
|
||||
puts("~"s + symClassName() + "();\n");
|
||||
|
||||
for (const auto& i : m_usesVfinal) {
|
||||
puts("void " + symClassName() + "_" + cvtToStr(i.first) + "(");
|
||||
|
||||
+36
-26
@@ -151,15 +151,18 @@ public:
|
||||
of.puts("default: lib" + v3Global.opt.prefix() + "\n");
|
||||
}
|
||||
of.puts("\n### Constants...\n");
|
||||
of.puts("# Perl executable (from $PERL)\n");
|
||||
of.puts("# Perl executable (from $PERL, defaults to 'perl' if not set)\n");
|
||||
of.puts("PERL = " + V3OutFormatter::quoteNameControls(V3Options::getenvPERL()) + "\n");
|
||||
of.puts("# Python3 executable (from $PYTHON3, defaults to 'python3' if not set)\n");
|
||||
of.puts("PYTHON3 = " + V3OutFormatter::quoteNameControls(V3Options::getenvPYTHON3())
|
||||
+ "\n");
|
||||
of.puts("# Path to Verilator kit (from $VERILATOR_ROOT)\n");
|
||||
of.puts("VERILATOR_ROOT = "
|
||||
+ V3OutFormatter::quoteNameControls(V3Options::getenvVERILATOR_ROOT()) + "\n");
|
||||
of.puts("# SystemC include directory with systemc.h (from $SYSTEMC_INCLUDE)\n");
|
||||
of.puts(string{"SYSTEMC_INCLUDE ?= "} + V3Options::getenvSYSTEMC_INCLUDE() + "\n");
|
||||
of.puts("SYSTEMC_INCLUDE ?= "s + V3Options::getenvSYSTEMC_INCLUDE() + "\n");
|
||||
of.puts("# SystemC library directory with libsystemc.a (from $SYSTEMC_LIBDIR)\n");
|
||||
of.puts(string{"SYSTEMC_LIBDIR ?= "} + V3Options::getenvSYSTEMC_LIBDIR() + "\n");
|
||||
of.puts("SYSTEMC_LIBDIR ?= "s + V3Options::getenvSYSTEMC_LIBDIR() + "\n");
|
||||
|
||||
// Only check it if we really need the value
|
||||
if (v3Global.opt.systemC() && !V3Options::systemCFound()) {
|
||||
@@ -170,22 +173,22 @@ public:
|
||||
|
||||
of.puts("\n### Switches...\n");
|
||||
of.puts("# C++ code coverage 0/1 (from --prof-c)\n");
|
||||
of.puts(string{"VM_PROFC = "} + ((v3Global.opt.profC()) ? "1" : "0") + "\n");
|
||||
of.puts("VM_PROFC = "s + ((v3Global.opt.profC()) ? "1" : "0") + "\n");
|
||||
of.puts("# SystemC output mode? 0/1 (from --sc)\n");
|
||||
of.puts(string{"VM_SC = "} + ((v3Global.opt.systemC()) ? "1" : "0") + "\n");
|
||||
of.puts("VM_SC = "s + ((v3Global.opt.systemC()) ? "1" : "0") + "\n");
|
||||
of.puts("# Legacy or SystemC output mode? 0/1 (from --sc)\n");
|
||||
of.puts(string{"VM_SP_OR_SC = $(VM_SC)\n"});
|
||||
of.puts("VM_SP_OR_SC = $(VM_SC)\n");
|
||||
of.puts("# Deprecated\n");
|
||||
of.puts(string{"VM_PCLI = "} + (v3Global.opt.systemC() ? "0" : "1") + "\n");
|
||||
of.puts("VM_PCLI = "s + (v3Global.opt.systemC() ? "0" : "1") + "\n");
|
||||
of.puts(
|
||||
"# Deprecated: SystemC architecture to find link library path (from $SYSTEMC_ARCH)\n");
|
||||
of.puts(string{"VM_SC_TARGET_ARCH = "} + V3Options::getenvSYSTEMC_ARCH() + "\n");
|
||||
of.puts("VM_SC_TARGET_ARCH = "s + V3Options::getenvSYSTEMC_ARCH() + "\n");
|
||||
|
||||
of.puts("\n### Vars...\n");
|
||||
of.puts("# Design prefix (from --prefix)\n");
|
||||
of.puts(string{"VM_PREFIX = "} + v3Global.opt.prefix() + "\n");
|
||||
of.puts("VM_PREFIX = "s + v3Global.opt.prefix() + "\n");
|
||||
of.puts("# Module prefix (from --prefix)\n");
|
||||
of.puts(string{"VM_MODPREFIX = "} + v3Global.opt.modPrefix() + "\n");
|
||||
of.puts("VM_MODPREFIX = "s + v3Global.opt.modPrefix() + "\n");
|
||||
|
||||
of.puts("# User CFLAGS (from -CFLAGS on Verilator command line)\n");
|
||||
of.puts("VM_USER_CFLAGS = \\\n");
|
||||
@@ -209,7 +212,7 @@ public:
|
||||
of.puts("VM_USER_CLASSES = \\\n");
|
||||
const V3StringSet& cppFiles = v3Global.opt.cppFiles();
|
||||
for (const auto& cppfile : cppFiles) {
|
||||
of.puts("\t" + V3Os::filenameNonExt(cppfile) + " \\\n");
|
||||
of.puts("\t" + V3Os::filenameNonDirExt(cppfile) + " \\\n");
|
||||
const string dir = V3Os::filenameDir(cppfile);
|
||||
dirs.insert(dir);
|
||||
}
|
||||
@@ -236,11 +239,18 @@ public:
|
||||
of.puts("\n");
|
||||
}
|
||||
|
||||
const string compilerIncludePch
|
||||
= v3Global.opt.compilerIncludes().empty() ? "" : "$(VK_PCH_H).fast.gch";
|
||||
const string compilerIncludeFlag
|
||||
= v3Global.opt.compilerIncludes().empty() ? "" : "$(VK_PCH_I_FAST)";
|
||||
for (const string& cppfile : cppFiles) {
|
||||
const string basename = V3Os::filenameNonExt(cppfile);
|
||||
const string basename = V3Os::filenameNonDirExt(cppfile);
|
||||
// NOLINTNEXTLINE(performance-inefficient-string-concatenation)
|
||||
of.puts(basename + ".o: " + cppfile + "\n");
|
||||
of.puts("\t$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) -c -o $@ $<\n");
|
||||
of.puts(basename + ".o: " + cppfile + " " + compilerIncludePch + "\n");
|
||||
|
||||
// NOLINTNEXTLINE(performance-inefficient-string-concatenation)
|
||||
of.puts("\t$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) "
|
||||
+ compilerIncludeFlag + " -c -o $@ $<\n");
|
||||
}
|
||||
|
||||
if (v3Global.opt.exe()) {
|
||||
@@ -346,7 +356,7 @@ class EmitMkHierVerilation final {
|
||||
= m_planp->hierBlocksSorted(); // leaf comes first
|
||||
// List in order of leaf-last order so that linker can resolve dependency
|
||||
for (const auto& block : vlstd::reverse_view(blocks)) {
|
||||
of.puts("\t" + block->hierLib(true) + " \\\n");
|
||||
of.puts("\t" + block->hierLibFilename(true) + " \\\n");
|
||||
}
|
||||
of.puts("\n");
|
||||
|
||||
@@ -365,12 +375,12 @@ class EmitMkHierVerilation final {
|
||||
|
||||
// Top level module
|
||||
{
|
||||
const string argsFile = v3Global.hierPlanp()->topCommandArgsFileName(false);
|
||||
const string argsFile = v3Global.hierPlanp()->topCommandArgsFilename(false);
|
||||
of.puts("\n# Verilate the top module\n");
|
||||
of.puts(v3Global.opt.prefix()
|
||||
+ ".mk: $(VM_HIER_INPUT_FILES) $(VM_HIER_VERILOG_LIBS) ");
|
||||
of.puts(V3Os::filenameNonDir(argsFile) + " ");
|
||||
for (const auto& itr : *m_planp) of.puts(itr.second->hierWrapper(true) + " ");
|
||||
for (const auto& itr : *m_planp) of.puts(itr.second->hierWrapperFilename(true) + " ");
|
||||
of.puts("\n");
|
||||
emitLaunchVerilator(of, argsFile);
|
||||
}
|
||||
@@ -379,29 +389,29 @@ class EmitMkHierVerilation final {
|
||||
of.puts("\n# Verilate hierarchical blocks\n");
|
||||
for (const V3HierBlock* const blockp : m_planp->hierBlocksSorted()) {
|
||||
const string prefix = blockp->hierPrefix();
|
||||
const string argsFile = blockp->commandArgsFileName(false);
|
||||
of.puts(blockp->hierGenerated(true));
|
||||
const string argsFilename = blockp->commandArgsFilename(false);
|
||||
of.puts(blockp->hierGeneratedFilenames(true));
|
||||
of.puts(": $(VM_HIER_INPUT_FILES) $(VM_HIER_VERILOG_LIBS) ");
|
||||
of.puts(V3Os::filenameNonDir(argsFile) + " ");
|
||||
of.puts(V3Os::filenameNonDir(argsFilename) + " ");
|
||||
const V3HierBlock::HierBlockSet& children = blockp->children();
|
||||
for (V3HierBlock::HierBlockSet::const_iterator child = children.begin();
|
||||
child != children.end(); ++child) {
|
||||
of.puts((*child)->hierWrapper(true) + " ");
|
||||
of.puts((*child)->hierWrapperFilename(true) + " ");
|
||||
}
|
||||
of.puts("\n");
|
||||
emitLaunchVerilator(of, argsFile);
|
||||
emitLaunchVerilator(of, argsFilename);
|
||||
|
||||
// Rule to build lib*.a
|
||||
of.puts(blockp->hierLib(true));
|
||||
of.puts(blockp->hierLibFilename(true));
|
||||
of.puts(": ");
|
||||
of.puts(blockp->hierMk(true));
|
||||
of.puts(blockp->hierMkFilename(true));
|
||||
of.puts(" ");
|
||||
for (V3HierBlock::HierBlockSet::const_iterator child = children.begin();
|
||||
child != children.end(); ++child) {
|
||||
of.puts((*child)->hierLib(true));
|
||||
of.puts((*child)->hierLibFilename(true));
|
||||
of.puts(" ");
|
||||
}
|
||||
of.puts("\n\t$(MAKE) -f " + blockp->hierMk(false) + " -C " + prefix);
|
||||
of.puts("\n\t$(MAKE) -f " + blockp->hierMkFilename(false) + " -C " + prefix);
|
||||
of.puts(" VM_PREFIX=" + prefix);
|
||||
of.puts("\n\n");
|
||||
}
|
||||
|
||||
+8
-2
@@ -602,7 +602,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
|
||||
}
|
||||
void visit(AstTypedef* nodep) override {
|
||||
putfs(nodep, "typedef ");
|
||||
iterateAndNextConstNull(nodep->dtypep());
|
||||
iterateAndNextConstNull(nodep->subDTypep());
|
||||
puts(" ");
|
||||
puts(nodep->prettyName());
|
||||
puts(";\n");
|
||||
@@ -630,7 +630,13 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
|
||||
iterateConst(nodep->subDTypep());
|
||||
iterateAndNextConstNull(nodep->rangep());
|
||||
}
|
||||
void visit(AstRefDType* nodep) override { iterateConst(nodep->skipRefp()); }
|
||||
void visit(AstRefDType* nodep) override {
|
||||
if (nodep->subDTypep()) {
|
||||
iterateConst(nodep->skipRefp());
|
||||
} else {
|
||||
puts("\n???? // "s + nodep->prettyTypeName() + " -> UNLINKED\n");
|
||||
}
|
||||
}
|
||||
void visit(AstNodeUOrStructDType* nodep) override {
|
||||
puts(nodep->verilogKwd() + " ");
|
||||
if (nodep->packed()) puts("packed ");
|
||||
|
||||
+6
-20
@@ -53,25 +53,11 @@ V3ErrorCode::V3ErrorCode(const char* msgp) {
|
||||
//
|
||||
|
||||
bool V3ErrorGuarded::isError(V3ErrorCode code, bool supp) VL_REQUIRES(m_mutex) {
|
||||
if (supp) {
|
||||
return false;
|
||||
} else if (code == V3ErrorCode::USERINFO) {
|
||||
return false;
|
||||
} else if (code == V3ErrorCode::EC_INFO) {
|
||||
return false;
|
||||
} else if (code == V3ErrorCode::EC_FATAL) {
|
||||
return true;
|
||||
} else if (code == V3ErrorCode::EC_FATALEXIT) {
|
||||
return true;
|
||||
} else if (code == V3ErrorCode::EC_FATALSRC) {
|
||||
return true;
|
||||
} else if (code == V3ErrorCode::EC_ERROR) {
|
||||
return true;
|
||||
} else if (code < V3ErrorCode::EC_FIRST_WARN || pretendError(code)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (code.hardError()) return true;
|
||||
if (supp) return false;
|
||||
if (code == V3ErrorCode::USERINFO || code == V3ErrorCode::EC_INFO) return false;
|
||||
if (pretendError(code)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
string V3ErrorGuarded::msgPrefix() VL_REQUIRES(m_mutex) {
|
||||
@@ -180,7 +166,7 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra)
|
||||
}
|
||||
if (!m_describedEachWarn[m_errorCode] && !m_pretendError[m_errorCode]) {
|
||||
m_describedEachWarn[m_errorCode] = true;
|
||||
if (m_errorCode >= V3ErrorCode::EC_FIRST_WARN && !m_describedWarnings) {
|
||||
if (!m_errorCode.hardError() && !m_describedWarnings) {
|
||||
m_describedWarnings = true;
|
||||
std::cerr << warnMore() << "... Use \"/* verilator lint_off "
|
||||
<< m_errorCode.ascii()
|
||||
|
||||
+8
-4
@@ -226,6 +226,10 @@ public:
|
||||
}
|
||||
// Warnings that warn about nasty side effects
|
||||
bool dangerous() const VL_MT_SAFE { return (m_e == COMBDLY); }
|
||||
// Insuppressible error codes that should always stop elaboration
|
||||
bool hardError() const VL_MT_SAFE {
|
||||
return (m_e != EC_INFO && m_e < V3ErrorCode::EC_FIRST_WARN);
|
||||
}
|
||||
// Warnings we'll present to the user as errors
|
||||
// Later -Werror- options may make more of these.
|
||||
bool pretendError() const VL_MT_SAFE {
|
||||
@@ -351,8 +355,8 @@ public:
|
||||
void execErrorExitCb() VL_REQUIRES(m_mutex) {
|
||||
if (m_errorExitCb) m_errorExitCb();
|
||||
}
|
||||
void errorExitCb(ErrorExitCb cb) VL_REQUIRES(m_mutex) { m_errorExitCb = cb; }
|
||||
ErrorExitCb errorExitCb() VL_REQUIRES(m_mutex) { return m_errorExitCb; }
|
||||
void errorExitCb(ErrorExitCb cb) VL_REQUIRES(m_mutex) { m_errorExitCb = cb; }
|
||||
bool isError(V3ErrorCode code, bool supp) VL_REQUIRES(m_mutex);
|
||||
void vlAbortOrExit() VL_REQUIRES(m_mutex);
|
||||
void errorContexted(bool flag) VL_REQUIRES(m_mutex) { m_errorContexted = flag; }
|
||||
@@ -379,12 +383,12 @@ public:
|
||||
}
|
||||
m_pretendError[code] = flag;
|
||||
}
|
||||
void debugDefault(int level) VL_MT_UNSAFE { m_debugDefault = level; }
|
||||
int debugDefault() VL_MT_SAFE { return m_debugDefault; }
|
||||
void errorLimit(int level) VL_REQUIRES(m_mutex) { m_errorLimit = level; }
|
||||
void debugDefault(int level) VL_MT_UNSAFE { m_debugDefault = level; }
|
||||
int errorLimit() VL_REQUIRES(m_mutex) { return m_errorLimit; }
|
||||
void warnFatal(bool flag) VL_REQUIRES(m_mutex) { m_warnFatal = flag; }
|
||||
void errorLimit(int level) VL_REQUIRES(m_mutex) { m_errorLimit = level; }
|
||||
bool warnFatal() VL_REQUIRES(m_mutex) { return m_warnFatal; }
|
||||
void warnFatal(bool flag) VL_REQUIRES(m_mutex) { m_warnFatal = flag; }
|
||||
V3ErrorCode errorCode() VL_REQUIRES(m_mutex) { return m_errorCode; }
|
||||
bool errorContexted() VL_REQUIRES(m_mutex) { return m_errorContexted; }
|
||||
int warnCount() VL_REQUIRES(m_mutex) { return m_warnCount; }
|
||||
|
||||
+1
-1
@@ -52,8 +52,8 @@ public:
|
||||
void priority(uint32_t pri) { m_priority = pri; }
|
||||
uint32_t cost() const { return m_cost; }
|
||||
void cost(uint32_t cost) { m_cost = cost; }
|
||||
void predictStart(uint64_t time) { m_predictStart = time; }
|
||||
uint64_t predictStart() const { return m_predictStart; }
|
||||
void predictStart(uint64_t time) { m_predictStart = time; }
|
||||
string name() const override VL_MT_STABLE { return "mt"s + std::to_string(id()); }
|
||||
string hashName() const { return m_hashName; }
|
||||
void dump(std::ostream& str) const;
|
||||
|
||||
+1
-1
@@ -371,7 +371,7 @@ bool FileLine::warnOff(const string& msg, bool flag) {
|
||||
return true;
|
||||
}
|
||||
const V3ErrorCode code{cmsg};
|
||||
if (code < V3ErrorCode::EC_FIRST_WARN) {
|
||||
if (code.hardError()) {
|
||||
return false;
|
||||
} else {
|
||||
warnOff(code, flag);
|
||||
|
||||
+35
-18
@@ -46,7 +46,6 @@
|
||||
#include "V3MemberMap.h"
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
@@ -108,7 +107,7 @@ public:
|
||||
|
||||
// Move variables into the class
|
||||
for (AstVar* varp : m_captureOrder) {
|
||||
if (varp->direction() == VDirection::INPUT) {
|
||||
if (varp->direction().isAny()) {
|
||||
varp = varp->cloneTree(false);
|
||||
varp->direction(VDirection::NONE);
|
||||
} else {
|
||||
@@ -162,18 +161,28 @@ public:
|
||||
|
||||
AstNode* initsp = nullptr; // Arguments need to be copied
|
||||
for (AstVar* varp : m_captureOrder) {
|
||||
if (varp->direction() != VDirection::INPUT) continue;
|
||||
if (!varp->direction().isAny()) continue;
|
||||
|
||||
AstMemberSel* const memberselp = new AstMemberSel{
|
||||
varp->fileline(),
|
||||
new AstVarRef{varp->fileline(), m_instance.m_handlep, VAccess::WRITE},
|
||||
varp->dtypep()};
|
||||
memberselp->name(varp->name());
|
||||
memberselp->varp(VN_AS(memberMap.findMember(m_instance.m_classp, varp->name()), Var));
|
||||
AstNode* initAsgnp
|
||||
= new AstAssign{varp->fileline(), memberselp,
|
||||
new AstVarRef{varp->fileline(), varp, VAccess::READ}};
|
||||
initsp = AstNode::addNext(initsp, initAsgnp);
|
||||
if (varp->direction().isNonOutput()) {
|
||||
AstMemberSel* const memberselp = new AstMemberSel{
|
||||
varp->fileline(),
|
||||
new AstVarRef{varp->fileline(), m_instance.m_handlep, VAccess::WRITE},
|
||||
VN_AS(memberMap.findMember(m_instance.m_classp, varp->name()), Var)};
|
||||
AstNode* initAsgnp
|
||||
= new AstAssign{varp->fileline(), memberselp,
|
||||
new AstVarRef{varp->fileline(), varp, VAccess::READ}};
|
||||
initsp = AstNode::addNext(initsp, initAsgnp);
|
||||
}
|
||||
if (varp->direction().isWritable()) {
|
||||
AstMemberSel* const memberselp = new AstMemberSel{
|
||||
varp->fileline(),
|
||||
new AstVarRef{varp->fileline(), m_instance.m_handlep, VAccess::READ},
|
||||
VN_AS(memberMap.findMember(m_instance.m_classp, varp->name()), Var)};
|
||||
AstNode* writebackAsgnp = new AstAssign{
|
||||
varp->fileline(), new AstVarRef{varp->fileline(), varp, VAccess::WRITE},
|
||||
memberselp};
|
||||
stmtp = AstNode::addNext(stmtp, writebackAsgnp);
|
||||
}
|
||||
}
|
||||
if (initsp) AstNode::addNext(asgnp, initsp);
|
||||
|
||||
@@ -307,9 +316,8 @@ class DynScopeVisitor final : public VNVisitor {
|
||||
refp->unlinkFrBack(&handle);
|
||||
AstMemberSel* const membersel = new AstMemberSel{
|
||||
refp->fileline(), new AstVarRef{refp->fileline(), dynScope.m_handlep, refp->access()},
|
||||
refp->dtypep()};
|
||||
membersel->name(refp->varp()->name());
|
||||
if (refp->varp()->direction() == VDirection::INPUT) {
|
||||
refp->varp()};
|
||||
if (refp->varp()->direction().isAny()) {
|
||||
membersel->varp(
|
||||
VN_AS(m_memberMap.findMember(dynScope.m_classp, refp->varp()->name()), Var));
|
||||
} else {
|
||||
@@ -407,8 +415,17 @@ class DynScopeVisitor final : public VNVisitor {
|
||||
void visit(AstVarRef* nodep) override {
|
||||
ForkDynScopeFrame* const framep = frameOf(nodep->varp());
|
||||
if (!framep) return;
|
||||
|
||||
if (needsDynScope(nodep)) {
|
||||
if (m_afterTimingControl && nodep->varp()->isWritable()
|
||||
&& nodep->access().isWriteOrRW()) {
|
||||
// The output variable may not exist after a delay, so we can't just write to it
|
||||
nodep->v3warn(
|
||||
E_UNSUPPORTED,
|
||||
"Unsupported: Writing to a captured "
|
||||
<< (nodep->varp()->isInoutish() ? "inout" : "output") << " variable in a "
|
||||
<< (VN_IS(nodep->backp(), AssignDly) ? "non-blocking assignment" : "fork")
|
||||
<< " after a timing control");
|
||||
}
|
||||
if (!framep->instance().initialized()) framep->createInstancePrototype();
|
||||
framep->captureVarInsert(nodep->varp());
|
||||
}
|
||||
@@ -434,7 +451,7 @@ class DynScopeVisitor final : public VNVisitor {
|
||||
forkp->addStmtsp(nodep);
|
||||
UINFO(9, "assign new fork " << forkp << endl);
|
||||
} else {
|
||||
iterateChildren(nodep);
|
||||
visit(static_cast<AstNodeStmt*>(nodep));
|
||||
}
|
||||
}
|
||||
void visit(AstNode* nodep) override {
|
||||
|
||||
@@ -47,4 +47,12 @@ struct FunctionTraits<ReturnType (ClassType::*)(Args...) const> VL_NOT_FINAL {
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T_Callable, size_t index>
|
||||
struct FunctionArgNoPointerNoCV final {
|
||||
using Traits = FunctionTraits<T_Callable>;
|
||||
using T_Arg = typename Traits::template arg<index>::type;
|
||||
using T_ArgNoPtr = typename std::remove_pointer<T_Arg>::type;
|
||||
using type = typename std::remove_cv<T_ArgNoPtr>::type;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -79,6 +79,13 @@ void V3Global::readFiles() {
|
||||
"Cannot find file containing library module: ");
|
||||
}
|
||||
|
||||
// Read hierarchical type parameter file
|
||||
const string filename = v3Global.opt.hierParamFile();
|
||||
if (!filename.empty()) {
|
||||
parser.parseFile(new FileLine{FileLine::commandLineFilename()}, filename, false,
|
||||
"Cannot open file containing hierarchical parameter declarations: ");
|
||||
}
|
||||
|
||||
// v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("parse.tree"));
|
||||
V3Error::abortIfErrors();
|
||||
|
||||
|
||||
+5
-2
@@ -114,6 +114,7 @@ class V3Global final {
|
||||
bool m_dpi = false; // Need __Dpi include files
|
||||
bool m_hasEvents = false; // Design uses SystemVerilog named events
|
||||
bool m_hasClasses = false; // Design uses SystemVerilog classes
|
||||
bool m_hasSampled = false; // Design uses SAMPLED expresions
|
||||
bool m_hasVirtIfaces = false; // Design uses virtual interfaces
|
||||
bool m_usesProbDist = false; // Uses $dist_*
|
||||
bool m_usesStdPackage = false; // Design uses the std package
|
||||
@@ -168,6 +169,8 @@ public:
|
||||
void setHasEvents() { m_hasEvents = true; }
|
||||
bool hasClasses() const { return m_hasClasses; }
|
||||
void setHasClasses() { m_hasClasses = true; }
|
||||
bool hasSampled() const { return m_hasSampled; }
|
||||
void setHasSampled() { m_hasSampled = true; }
|
||||
bool hasVirtIfaces() const { return m_hasVirtIfaces; }
|
||||
void setHasVirtIfaces() { m_hasVirtIfaces = true; }
|
||||
bool usesProbDist() const { return m_usesProbDist; }
|
||||
@@ -185,10 +188,10 @@ public:
|
||||
UASSERT(!m_hierPlanp, "call once");
|
||||
m_hierPlanp = plan;
|
||||
}
|
||||
void useParallelBuild(bool flag) { m_useParallelBuild = flag; }
|
||||
bool useParallelBuild() const { return m_useParallelBuild; }
|
||||
void useRandomizeMethods(bool flag) { m_useRandomizeMethods = flag; }
|
||||
void useParallelBuild(bool flag) { m_useParallelBuild = flag; }
|
||||
bool useRandomizeMethods() const { return m_useRandomizeMethods; }
|
||||
void useRandomizeMethods(bool flag) { m_useRandomizeMethods = flag; }
|
||||
void saveJsonPtrFieldName(const std::string& fieldName);
|
||||
void ptrNamesDumpJson(std::ostream& os);
|
||||
void idPtrMapDumpJson(std::ostream& os);
|
||||
|
||||
+1
-1
@@ -368,7 +368,7 @@ class HasherVisitor final : public VNVisitorConst {
|
||||
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {});
|
||||
}
|
||||
void visit(AstInitArray* nodep) override {
|
||||
if (const AstAssocArrayDType* const dtypep = VN_CAST(nodep->dtypep(), AssocArrayDType)) {
|
||||
if (VN_IS(nodep->dtypep(), AssocArrayDType)) {
|
||||
if (nodep->defaultp()) {
|
||||
m_hash
|
||||
+= hashNodeAndIterate(nodep->defaultp(), HASH_DTYPE, HASH_CHILDREN, []() {});
|
||||
|
||||
+100
-41
@@ -38,29 +38,36 @@
|
||||
// Here is more detailed internal process.
|
||||
// 1) Parser adds VPragmaType::HIER_BLOCK of AstPragma to modules
|
||||
// that are marked with /*verilator hier_block*/ metacomment in Verilator run a).
|
||||
// 2) AstModule with HIER_BLOCK pragma is marked modp->hier_block(true)
|
||||
// 2) If module type parameters are present, V3Config marks hier param modules
|
||||
// (marked with hier_params verilator config pragma) as modp->hierParams(true).
|
||||
// This is done in run b), de-parametrized modules are mapped with their params one-to-one.
|
||||
// 3) AstModule with HIER_BLOCK pragma is marked modp->hierBlock(true)
|
||||
// in V3LinkResolve.cpp during run a).
|
||||
// 3) In V3LinkCells.cpp, the following things are done during run b) and c).
|
||||
// 3-1) Delete the upper modules of the hierarchical block because the top module in run b) is
|
||||
// 4) In V3LinkCells.cpp, the following things are done during run b) and c).
|
||||
// 4-1) Delete the upper modules of the hierarchical block because the top module in run b) is
|
||||
// hierarchical block, not the top module of run c).
|
||||
// 3-2) If the top module of the run b) or c) instantiates other hierarchical blocks that is
|
||||
// 4-2) If the top module of the run b) or c) instantiates other hierarchical blocks that is
|
||||
// parameterized,
|
||||
// module and task names are renamed to the original name to be compatible with the
|
||||
// hier module to be called.
|
||||
//
|
||||
// Parameterized modules have unique name by V3Param.cpp. The unique name contains '__' and
|
||||
// Verilator encodes '__' when loading such symbols.
|
||||
// 4) V3LinkDot.cpp checks dotted access across hierarchical block boundary.
|
||||
// 5) In V3Dead.cpp, some parameters of parameterized modules are protected not to be deleted even
|
||||
// 5) In V3LinkDot.cpp,
|
||||
// 5-1) Dotted access across hierarchical block boundary is checked. Currently hierarchical
|
||||
// block references are not supported.
|
||||
// 5-2) If present, parameters in hier params module replace parameter values of de-parametrized
|
||||
// module in run b).
|
||||
// 6) In V3Dead.cpp, some parameters of parameterized modules are protected not to be deleted even
|
||||
// if the parameter is not referred. This protection is necessary to match step 6) below.
|
||||
// 6) In V3Param.cpp, use --lib-create wrapper of the parameterized module made in b) and c).
|
||||
// 7) In V3Param.cpp, use --lib-create wrapper of the parameterized module made in b) and c).
|
||||
// If a hierarchical block is a parameterized module and instantiated in multiple locations,
|
||||
// all parameters must exactly match.
|
||||
// 7) In V3HierBlock.cpp, relationship among hierarchical blocks are checked in run a).
|
||||
// 8) In V3HierBlock.cpp, relationships among hierarchical blocks are checked in run a).
|
||||
// (which block uses other blocks..)
|
||||
// 8) In V3EmitMk.cpp, ${prefix}_hier.mk is created in run a).
|
||||
// 9) In V3EmitMk.cpp, ${prefix}_hier.mk is created in run a).
|
||||
//
|
||||
// There are two hidden command options.
|
||||
// There are three hidden command options:
|
||||
// --hierarchical-child is added to Verilator run b).
|
||||
// --hierarchical-block module_name,mangled_name,name0,value0,name1,value1,...
|
||||
// module_name :The original modulename
|
||||
@@ -70,12 +77,18 @@
|
||||
// value :Overridden value of the parameter
|
||||
//
|
||||
// Used for b) and c).
|
||||
// This options is repeated for all instantiating hierarchical blocks.
|
||||
// These options are repeated for all instantiated hierarchical blocks.
|
||||
// --hierarchical-params-file filename
|
||||
// filename :Name of a hierarchical parameters file
|
||||
//
|
||||
// Added in a), used for b).
|
||||
// Each de-parametrized module version has exactly one hier params file specified.
|
||||
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "V3HierBlock.h"
|
||||
|
||||
#include "V3EmitV.h"
|
||||
#include "V3File.h"
|
||||
#include "V3Os.h"
|
||||
#include "V3Stats.h"
|
||||
@@ -88,11 +101,15 @@
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
static string V3HierCommandArgsFileName(const string& prefix, bool forCMake) {
|
||||
static string V3HierCommandArgsFilename(const string& prefix, bool forCMake) {
|
||||
return v3Global.opt.makeDir() + "/" + prefix
|
||||
+ (forCMake ? "__hierCMakeArgs.f" : "__hierMkArgs.f");
|
||||
}
|
||||
|
||||
static string V3HierParametersFileName(const string& prefix) {
|
||||
return v3Global.opt.makeDir() + "/" + prefix + "__hierParameters.v";
|
||||
}
|
||||
|
||||
static void V3HierWriteCommonInputs(const V3HierBlock* hblockp, std::ostream* of, bool forCMake) {
|
||||
string topModuleFile;
|
||||
if (hblockp) topModuleFile = hblockp->vFileIfNecessary();
|
||||
@@ -107,7 +124,8 @@ static void V3HierWriteCommonInputs(const V3HierBlock* hblockp, std::ostream* of
|
||||
|
||||
//######################################################################
|
||||
|
||||
V3HierBlock::StrGParams V3HierBlock::stringifyParams(const GParams& gparams, bool forGOption) {
|
||||
V3HierBlock::StrGParams V3HierBlock::stringifyParams(const V3HierBlockParams::GParams& gparams,
|
||||
bool forGOption) {
|
||||
StrGParams strParams;
|
||||
for (const auto& gparam : gparams) {
|
||||
if (const AstConst* const constp = VN_CAST(gparam->valuep(), Const)) {
|
||||
@@ -159,17 +177,21 @@ V3StringList V3HierBlock::commandArgs(bool forCMake) const {
|
||||
opts.push_back(" --protect-key " + v3Global.opt.protectKeyDefaulted());
|
||||
opts.push_back(" --hierarchical-child " + cvtToStr(v3Global.opt.threads()));
|
||||
|
||||
const StrGParams gparamsStr = stringifyParams(gparams(), true);
|
||||
for (StrGParams::const_iterator paramIt = gparamsStr.begin(); paramIt != gparamsStr.end();
|
||||
++paramIt) {
|
||||
opts.push_back("-G" + paramIt->first + "=" + paramIt->second + "");
|
||||
const StrGParams gparamsStr = stringifyParams(params().gparams(), true);
|
||||
for (const StrGParam& param : gparamsStr) {
|
||||
const string name = param.first;
|
||||
const string value = param.second;
|
||||
opts.push_back("-G" + name + "=" + value + "");
|
||||
}
|
||||
if (!params().gTypeParams().empty())
|
||||
opts.push_back(" --hierarchical-params-file " + typeParametersFilename());
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
V3StringList V3HierBlock::hierBlockArgs() const {
|
||||
V3StringList opts;
|
||||
const StrGParams gparamsStr = stringifyParams(gparams(), false);
|
||||
const StrGParams gparamsStr = stringifyParams(params().gparams(), false);
|
||||
opts.push_back("--hierarchical-block ");
|
||||
string s = modp()->origName(); // origName
|
||||
s += "," + modp()->name(); // mangledName
|
||||
@@ -184,21 +206,27 @@ V3StringList V3HierBlock::hierBlockArgs() const {
|
||||
|
||||
string V3HierBlock::hierPrefix() const { return "V" + modp()->name(); }
|
||||
|
||||
string V3HierBlock::hierSomeFile(bool withDir, const char* prefix, const char* suffix) const {
|
||||
string V3HierBlock::hierSomeFilename(bool withDir, const char* prefix, const char* suffix) const {
|
||||
string s;
|
||||
if (withDir) s = hierPrefix() + '/';
|
||||
s += prefix + modp()->name() + suffix;
|
||||
return s;
|
||||
}
|
||||
|
||||
string V3HierBlock::hierWrapper(bool withDir) const { return hierSomeFile(withDir, "", ".sv"); }
|
||||
string V3HierBlock::hierWrapperFilename(bool withDir) const {
|
||||
return hierSomeFilename(withDir, "", ".sv");
|
||||
}
|
||||
|
||||
string V3HierBlock::hierMk(bool withDir) const { return hierSomeFile(withDir, "V", ".mk"); }
|
||||
string V3HierBlock::hierMkFilename(bool withDir) const {
|
||||
return hierSomeFilename(withDir, "V", ".mk");
|
||||
}
|
||||
|
||||
string V3HierBlock::hierLib(bool withDir) const { return hierSomeFile(withDir, "lib", ".a"); }
|
||||
string V3HierBlock::hierLibFilename(bool withDir) const {
|
||||
return hierSomeFilename(withDir, "lib", ".a");
|
||||
}
|
||||
|
||||
string V3HierBlock::hierGenerated(bool withDir) const {
|
||||
return hierWrapper(withDir) + ' ' + hierMk(withDir);
|
||||
string V3HierBlock::hierGeneratedFilenames(bool withDir) const {
|
||||
return hierWrapperFilename(withDir) + ' ' + hierMkFilename(withDir);
|
||||
}
|
||||
|
||||
string V3HierBlock::vFileIfNecessary() const {
|
||||
@@ -211,12 +239,12 @@ string V3HierBlock::vFileIfNecessary() const {
|
||||
}
|
||||
|
||||
void V3HierBlock::writeCommandArgsFile(bool forCMake) const {
|
||||
const std::unique_ptr<std::ofstream> of{V3File::new_ofstream(commandArgsFileName(forCMake))};
|
||||
const std::unique_ptr<std::ofstream> of{V3File::new_ofstream(commandArgsFilename(forCMake))};
|
||||
*of << "--cc\n";
|
||||
|
||||
if (!forCMake) {
|
||||
for (const auto& hierblockp : m_children) {
|
||||
*of << v3Global.opt.makeDir() << "/" << hierblockp->hierWrapper(true) << "\n";
|
||||
*of << v3Global.opt.makeDir() << "/" << hierblockp->hierWrapperFilename(true) << "\n";
|
||||
}
|
||||
*of << "-Mdir " << v3Global.opt.makeDir() << "/" << hierPrefix() << " \n";
|
||||
}
|
||||
@@ -228,8 +256,31 @@ void V3HierBlock::writeCommandArgsFile(bool forCMake) const {
|
||||
*of << v3Global.opt.allArgsStringForHierBlock(false, forCMake) << "\n";
|
||||
}
|
||||
|
||||
string V3HierBlock::commandArgsFileName(bool forCMake) const {
|
||||
return V3HierCommandArgsFileName(hierPrefix(), forCMake);
|
||||
string V3HierBlock::commandArgsFilename(bool forCMake) const {
|
||||
return V3HierCommandArgsFilename(hierPrefix(), forCMake);
|
||||
}
|
||||
|
||||
string V3HierBlock::typeParametersFilename() const {
|
||||
return V3HierParametersFileName(hierPrefix());
|
||||
}
|
||||
|
||||
void V3HierBlock::writeParametersFile() const {
|
||||
if (m_params.gTypeParams().empty()) return;
|
||||
|
||||
VHashSha256 hash{"type params"};
|
||||
const string moduleName = "Vhsh" + hash.digestSymbol();
|
||||
const std::unique_ptr<std::ofstream> of{V3File::new_ofstream(typeParametersFilename())};
|
||||
*of << "module " << moduleName << ";\n";
|
||||
for (const AstParamTypeDType* const gparam : m_params.gTypeParams()) {
|
||||
AstTypedef* tdefp
|
||||
= new AstTypedef(new FileLine{FileLine::builtInFilename()}, gparam->name(), nullptr,
|
||||
VFlagChildDType{}, gparam->skipRefp()->cloneTreePure(true));
|
||||
V3EmitV::verilogForTree(tdefp, *of);
|
||||
VL_DO_DANGLING(tdefp->deleteTree(), tdefp);
|
||||
}
|
||||
*of << "endmodule\n\n";
|
||||
*of << "`verilator_config\n";
|
||||
*of << "hier_params -module \"" << moduleName << "\"\n";
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
@@ -245,7 +296,7 @@ class HierBlockUsageCollectVisitor final : public VNVisitorConst {
|
||||
AstModule* m_modp = nullptr; // The current module
|
||||
AstModule* m_hierBlockp = nullptr; // The nearest parent module that is a hierarchical block
|
||||
ModuleSet m_referred; // Modules that have hier_block pragma
|
||||
V3HierBlock::GParams m_gparams; // list of variables that is VVarType::GPARAM
|
||||
V3HierBlockParams m_params;
|
||||
|
||||
void visit(AstModule* nodep) override {
|
||||
// Don't visit twice
|
||||
@@ -256,23 +307,23 @@ class HierBlockUsageCollectVisitor final : public VNVisitorConst {
|
||||
VL_RESTORER(m_modp);
|
||||
AstModule* const prevHierBlockp = m_hierBlockp;
|
||||
ModuleSet prevReferred;
|
||||
V3HierBlock::GParams prevGParams;
|
||||
V3HierBlockParams previousParams;
|
||||
m_modp = nodep;
|
||||
if (nodep->hierBlock()) {
|
||||
m_hierBlockp = nodep;
|
||||
prevReferred.swap(m_referred);
|
||||
}
|
||||
prevGParams.swap(m_gparams);
|
||||
previousParams.swap(m_params);
|
||||
|
||||
iterateChildrenConst(nodep);
|
||||
|
||||
if (nodep->hierBlock()) {
|
||||
m_planp->add(nodep, m_gparams);
|
||||
m_planp->add(nodep, m_params);
|
||||
for (const AstModule* modp : m_referred) m_planp->registerUsage(nodep, modp);
|
||||
m_hierBlockp = prevHierBlockp;
|
||||
m_referred = prevReferred;
|
||||
}
|
||||
m_gparams = prevGParams;
|
||||
m_params.swap(previousParams);
|
||||
}
|
||||
void visit(AstCell* nodep) override {
|
||||
// Visit used module here to know that the module is hier_block or not.
|
||||
@@ -288,10 +339,13 @@ class HierBlockUsageCollectVisitor final : public VNVisitorConst {
|
||||
if (m_modp && m_modp->hierBlock() && nodep->isIfaceRef() && !nodep->isIfaceParent()) {
|
||||
nodep->v3error("Modport cannot be used at the hierarchical block boundary");
|
||||
}
|
||||
if (nodep->isGParam() && nodep->overriddenParam()) m_gparams.push_back(nodep);
|
||||
if (nodep->isGParam() && nodep->overriddenParam()) m_params.add(nodep);
|
||||
}
|
||||
|
||||
void visit(AstParamTypeDType* nodep) override { m_params.add(nodep); }
|
||||
|
||||
void visit(AstNodeExpr*) override {} // Accelerate
|
||||
void visit(AstConstPool*) override {} // Accelerate
|
||||
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
||||
|
||||
public:
|
||||
@@ -303,11 +357,12 @@ public:
|
||||
|
||||
//######################################################################
|
||||
|
||||
void V3HierBlockPlan::add(const AstNodeModule* modp, const std::vector<AstVar*>& gparams) {
|
||||
void V3HierBlockPlan::add(const AstNodeModule* modp, const V3HierBlockParams& params) {
|
||||
const auto pair = m_blocks.emplace(modp, nullptr);
|
||||
if (pair.second) {
|
||||
V3HierBlock* hblockp = new V3HierBlock{modp, gparams};
|
||||
UINFO(3, "Add " << modp->prettyNameQ() << " with " << gparams.size() << " parameters"
|
||||
V3HierBlock* hblockp = new V3HierBlock{modp, params};
|
||||
UINFO(3, "Add " << modp->prettyNameQ() << " with " << params.gparams().size()
|
||||
<< " parameters and " << params.gTypeParams().size() << " type parameters"
|
||||
<< std::endl);
|
||||
pair.first->second = hblockp;
|
||||
}
|
||||
@@ -394,11 +449,11 @@ void V3HierBlockPlan::writeCommandArgsFiles(bool forCMake) const {
|
||||
}
|
||||
// For the top module
|
||||
const std::unique_ptr<std::ofstream> of{
|
||||
V3File::new_ofstream(topCommandArgsFileName(forCMake))};
|
||||
V3File::new_ofstream(topCommandArgsFilename(forCMake))};
|
||||
if (!forCMake) {
|
||||
// Load wrappers first not to be overwritten by the original HDL
|
||||
for (const_iterator it = begin(); it != end(); ++it) {
|
||||
*of << it->second->hierWrapper(true) << "\n";
|
||||
*of << it->second->hierWrapperFilename(true) << "\n";
|
||||
}
|
||||
}
|
||||
V3HierWriteCommonInputs(nullptr, of.get(), forCMake);
|
||||
@@ -425,6 +480,10 @@ void V3HierBlockPlan::writeCommandArgsFiles(bool forCMake) const {
|
||||
*of << v3Global.opt.allArgsStringForHierBlock(true, forCMake) << "\n";
|
||||
}
|
||||
|
||||
string V3HierBlockPlan::topCommandArgsFileName(bool forCMake) {
|
||||
return V3HierCommandArgsFileName(v3Global.opt.prefix(), forCMake);
|
||||
string V3HierBlockPlan::topCommandArgsFilename(bool forCMake) {
|
||||
return V3HierCommandArgsFilename(v3Global.opt.prefix(), forCMake);
|
||||
}
|
||||
|
||||
void V3HierBlockPlan::writeParametersFiles() const {
|
||||
for (const auto& block : *this) { block.second->writeParametersFile(); }
|
||||
}
|
||||
|
||||
+46
-15
@@ -32,19 +32,44 @@
|
||||
|
||||
class AstNetlist;
|
||||
class AstNodeModule;
|
||||
class AstParamTypeDType;
|
||||
class AstVar;
|
||||
|
||||
//######################################################################
|
||||
|
||||
class V3HierBlock final {
|
||||
class V3HierBlockParams final {
|
||||
public:
|
||||
using GParams = std::vector<AstVar*>;
|
||||
using GTypeParams = std::vector<AstParamTypeDType*>;
|
||||
|
||||
private:
|
||||
GParams m_params;
|
||||
GTypeParams m_typeParams;
|
||||
|
||||
public:
|
||||
void add(AstVar* param) { m_params.push_back(param); }
|
||||
void add(AstParamTypeDType* param) { m_typeParams.push_back(param); }
|
||||
|
||||
const GParams& gparams() const { return m_params; };
|
||||
const GTypeParams& gTypeParams() const { return m_typeParams; };
|
||||
GParams& gparams() { return m_params; };
|
||||
GTypeParams& gTypeParams() { return m_typeParams; };
|
||||
|
||||
void swap(V3HierBlockParams& other) {
|
||||
m_params.swap(other.m_params);
|
||||
m_typeParams.swap(other.m_typeParams);
|
||||
}
|
||||
};
|
||||
|
||||
class V3HierBlock final {
|
||||
public:
|
||||
using HierBlockSet = std::unordered_set<V3HierBlock*>;
|
||||
|
||||
private:
|
||||
// TYPES
|
||||
// Parameter name, stringified value
|
||||
using StrGParams = std::vector<std::pair<std::string, std::string>>;
|
||||
using StrGParam = std::pair<string, string>;
|
||||
using StrGParams = std::vector<StrGParam>;
|
||||
|
||||
// MEMBERS
|
||||
const AstNodeModule* const m_modp; // Hierarchical block module
|
||||
@@ -53,16 +78,18 @@ private:
|
||||
// Hierarchical blocks that this block directly or indirectly instantiates
|
||||
HierBlockSet m_children;
|
||||
// Parameters that are overridden by #(.param(value)) syntax.
|
||||
const GParams m_gparams;
|
||||
const V3HierBlockParams m_params;
|
||||
|
||||
// METHODS
|
||||
VL_UNCOPYABLE(V3HierBlock);
|
||||
static StrGParams stringifyParams(const GParams& gparams, bool forGOption) VL_MT_DISABLED;
|
||||
static StrGParams stringifyParams(const V3HierBlockParams::GParams& params,
|
||||
bool forGOption) VL_MT_DISABLED;
|
||||
|
||||
public:
|
||||
V3HierBlock(const AstNodeModule* modp, const GParams& gparams)
|
||||
V3HierBlock(const AstNodeModule* modp, const V3HierBlockParams& params)
|
||||
: m_modp{modp}
|
||||
, m_gparams{gparams} {}
|
||||
, m_params{params} {}
|
||||
|
||||
~V3HierBlock() VL_MT_DISABLED;
|
||||
|
||||
void addParent(V3HierBlock* parentp) { m_parents.insert(parentp); }
|
||||
@@ -70,23 +97,26 @@ public:
|
||||
bool hasChild() const { return !m_children.empty(); }
|
||||
const HierBlockSet& parents() const { return m_parents; }
|
||||
const HierBlockSet& children() const { return m_children; }
|
||||
const GParams& gparams() const { return m_gparams; }
|
||||
const V3HierBlockParams& params() const { return m_params; }
|
||||
const AstNodeModule* modp() const { return m_modp; }
|
||||
|
||||
// For emitting Makefile and CMakeLists.txt
|
||||
V3StringList commandArgs(bool forCMake) const VL_MT_DISABLED;
|
||||
V3StringList hierBlockArgs() const VL_MT_DISABLED;
|
||||
string hierPrefix() const VL_MT_DISABLED;
|
||||
string hierSomeFile(bool withDir, const char* prefix, const char* suffix) const VL_MT_DISABLED;
|
||||
string hierWrapper(bool withDir) const VL_MT_DISABLED;
|
||||
string hierMk(bool withDir) const VL_MT_DISABLED;
|
||||
string hierLib(bool withDir) const VL_MT_DISABLED;
|
||||
string hierGenerated(bool withDir) const VL_MT_DISABLED;
|
||||
string hierSomeFilename(bool withDir, const char* prefix,
|
||||
const char* suffix) const VL_MT_DISABLED;
|
||||
string hierWrapperFilename(bool withDir) const VL_MT_DISABLED;
|
||||
string hierMkFilename(bool withDir) const VL_MT_DISABLED;
|
||||
string hierLibFilename(bool withDir) const VL_MT_DISABLED;
|
||||
string hierGeneratedFilenames(bool withDir) const VL_MT_DISABLED;
|
||||
// Returns the original HDL file if it is not included in v3Global.opt.vFiles().
|
||||
string vFileIfNecessary() const VL_MT_DISABLED;
|
||||
// Write command line arguments to .f file for this hierarchical block
|
||||
void writeCommandArgsFile(bool forCMake) const VL_MT_DISABLED;
|
||||
string commandArgsFileName(bool forCMake) const VL_MT_DISABLED;
|
||||
void writeParametersFile() const VL_MT_DISABLED;
|
||||
string commandArgsFilename(bool forCMake) const VL_MT_DISABLED;
|
||||
string typeParametersFilename() const VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -104,7 +134,7 @@ public:
|
||||
using const_iterator = HierMap::const_iterator;
|
||||
using HierVector = std::vector<const V3HierBlock*>;
|
||||
|
||||
void add(const AstNodeModule* modp, const std::vector<AstVar*>& gparams) VL_MT_DISABLED;
|
||||
void add(const AstNodeModule* modp, const V3HierBlockParams& params) VL_MT_DISABLED;
|
||||
void registerUsage(const AstNodeModule* parentp, const AstNodeModule* childp) VL_MT_DISABLED;
|
||||
|
||||
const_iterator begin() const { return m_blocks.begin(); }
|
||||
@@ -117,7 +147,8 @@ public:
|
||||
|
||||
// Write command line arguments to .f files for child Verilation run
|
||||
void writeCommandArgsFiles(bool forCMake) const VL_MT_DISABLED;
|
||||
static string topCommandArgsFileName(bool forCMake) VL_MT_DISABLED;
|
||||
void writeParametersFiles() const VL_MT_DISABLED;
|
||||
static string topCommandArgsFilename(bool forCMake) VL_MT_DISABLED;
|
||||
|
||||
static void createPlan(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
+3
-5
@@ -46,17 +46,13 @@ class LifeState final {
|
||||
public:
|
||||
VDouble0 m_statAssnDel; // Statistic tracking
|
||||
VDouble0 m_statAssnCon; // Statistic tracking
|
||||
std::vector<AstNode*> m_unlinkps;
|
||||
|
||||
// CONSTRUCTORS
|
||||
LifeState() = default;
|
||||
~LifeState() {
|
||||
V3Stats::addStatSum("Optimizations, Lifetime assign deletions", m_statAssnDel);
|
||||
V3Stats::addStatSum("Optimizations, Lifetime constant prop", m_statAssnCon);
|
||||
for (AstNode* ip : m_unlinkps) VL_DO_DANGLING(ip->unlinkFrBack()->deleteTree(), ip);
|
||||
}
|
||||
// METHODS
|
||||
void pushUnlinkDeletep(AstNode* nodep) { m_unlinkps.push_back(nodep); }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -124,6 +120,7 @@ class LifeBlock final {
|
||||
LifeBlock* const m_aboveLifep; // Upper life, or nullptr
|
||||
LifeState* const m_statep; // Current global state
|
||||
bool m_replacedVref = false; // Replaced a variable reference since last clearing
|
||||
VNDeleter m_deleter; // Used to delay deletion of nodes
|
||||
|
||||
public:
|
||||
LifeBlock(LifeBlock* aboveLifep, LifeState* statep)
|
||||
@@ -145,7 +142,8 @@ public:
|
||||
// above our current iteration point.
|
||||
if (debug() > 4) oldassp->dumpTree("- REMOVE/SAMEBLK: ");
|
||||
entp->complexAssign();
|
||||
VL_DO_DANGLING(m_statep->pushUnlinkDeletep(oldassp), oldassp);
|
||||
oldassp->unlinkFrBack();
|
||||
VL_DO_DANGLING(m_deleter.pushDeletep(oldassp), oldassp);
|
||||
++m_statep->m_statAssnDel;
|
||||
}
|
||||
}
|
||||
|
||||
+44
-5
@@ -398,6 +398,7 @@ class LinkCellsVisitor final : public VNVisitor {
|
||||
for (AstNode* portnodep = nodep->modp()->stmtsp(); portnodep;
|
||||
portnodep = portnodep->nextp()) {
|
||||
if (const AstPort* const portp = VN_CAST(portnodep, Port)) {
|
||||
|
||||
if (ports.find(portp->name()) == ports.end()
|
||||
&& ports.find("__pinNumber" + cvtToStr(portp->pinNum())) == ports.end()) {
|
||||
if (pinStar) {
|
||||
@@ -411,11 +412,49 @@ class LinkCellsVisitor final : public VNVisitor {
|
||||
newp->svImplicit(true);
|
||||
nodep->addPinsp(newp);
|
||||
} else { // warn on the CELL that needs it, not the port
|
||||
nodep->v3warn(PINMISSING,
|
||||
"Cell has missing pin: " << portp->prettyNameQ());
|
||||
AstPin* const newp
|
||||
= new AstPin{nodep->fileline(), 0, portp->name(), nullptr};
|
||||
nodep->addPinsp(newp);
|
||||
|
||||
// We *might* not want to warn on this port, if it happened to be
|
||||
// an input with a default value in the module declaration. Our
|
||||
// AstPort* (portp) doesn't have that information, but the Module
|
||||
// (nodep->modp()) statements do that have information in an AstVar*
|
||||
// with the same name() as the port. We'll look for that in-line here,
|
||||
// if a port is missing on this instance.
|
||||
|
||||
// Get the AstVar for this AstPort, if it exists, using this
|
||||
// inefficient O(n) lookup to match the port name.
|
||||
const AstVar* portp_varp = nullptr;
|
||||
for (AstNode* module_stmtsp = nodep->modp()->stmtsp(); module_stmtsp;
|
||||
module_stmtsp = module_stmtsp->nextp()) {
|
||||
if (const AstVar* const varp = VN_CAST(module_stmtsp, Var)) {
|
||||
if (!varp->isParam() && varp->name() == portp->name()) {
|
||||
// not a parameter, same name, break, this is our varp
|
||||
// (AstVar*)
|
||||
portp_varp = varp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Is the matching Module port: an INPUT, with default value (in
|
||||
// valuep):
|
||||
if (portp_varp && portp_varp->isInput() && portp_varp->valuep()) {
|
||||
// Do not warn
|
||||
// Create b/c not already connected, and it does exist.
|
||||
AstPin* const newp
|
||||
= new AstPin{nodep->fileline(), 0, portp->name(), nullptr};
|
||||
nodep->addPinsp(newp);
|
||||
} else {
|
||||
nodep->v3warn(PINMISSING,
|
||||
"Cell has missing pin: "
|
||||
<< portp->prettyNameQ() << '\n'
|
||||
<< nodep->warnContextPrimary() << '\n'
|
||||
<< portp->warnOther()
|
||||
<< "... Location of port declaration\n"
|
||||
<< portp->warnContextSecondary());
|
||||
AstPin* const newp
|
||||
= new AstPin{nodep->fileline(), 0, portp->name(), nullptr};
|
||||
nodep->addPinsp(newp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+176
-40
@@ -65,8 +65,10 @@
|
||||
|
||||
#include "V3LinkDot.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Graph.h"
|
||||
#include "V3MemberMap.h"
|
||||
#include "V3Parse.h"
|
||||
#include "V3String.h"
|
||||
#include "V3SymTable.h"
|
||||
|
||||
@@ -749,6 +751,8 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||
AstClocking* m_clockingp = nullptr; // Current clocking block
|
||||
VSymEnt* m_modSymp = nullptr; // Symbol Entry for current module
|
||||
VSymEnt* m_curSymp = nullptr; // Symbol Entry for current table, where to lookup/insert
|
||||
string
|
||||
m_hierParamsName; // Name of module with hierarchical type parameters, empty when not used
|
||||
string m_scope; // Scope text
|
||||
const AstNodeBlock* m_blockp = nullptr; // Current Begin/end block
|
||||
const AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
||||
@@ -757,6 +761,7 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||
bool m_explicitNew = false; // Hit a "new" function
|
||||
int m_modBlockNum = 0; // Begin block number in module, 0=none seen
|
||||
int m_modWithNum = 0; // With block number, 0=none seen
|
||||
int m_modArgNum = 0; // Arg block number for randomize(), 0=none seen
|
||||
|
||||
// METHODS
|
||||
void makeImplicitNew(AstClass* nodep) {
|
||||
@@ -869,6 +874,7 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||
VL_RESTORER(m_paramNum);
|
||||
VL_RESTORER(m_modBlockNum);
|
||||
VL_RESTORER(m_modWithNum);
|
||||
VL_RESTORER(m_modArgNum);
|
||||
if (doit && nodep->user2()) {
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Identically recursive module (module instantiates "
|
||||
@@ -895,6 +901,7 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||
m_paramNum = 0;
|
||||
m_modBlockNum = 0;
|
||||
m_modWithNum = 0;
|
||||
m_modArgNum = 0;
|
||||
// m_modSymp/m_curSymp for non-packages set by AstCell above this module
|
||||
// Iterate
|
||||
nodep->user2(true);
|
||||
@@ -914,9 +921,22 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||
iterateChildren(nodep);
|
||||
nodep->user4(true);
|
||||
} else { // !doit
|
||||
// Will be optimized away later
|
||||
// Can't remove now, as our backwards iterator will throw up
|
||||
UINFO(5, "Module not under any CELL or top - dead module: " << nodep << endl);
|
||||
if (nodep->hierParams()) {
|
||||
UINFO(1, "Found module with hier type parameters" << endl);
|
||||
m_hierParamsName = nodep->name();
|
||||
for (const AstNode* node = nodep->op2p(); node; node = node->nextp()) {
|
||||
if (const AstTypedef* const tdef = VN_CAST(node, Typedef)) {
|
||||
UINFO(1, "Inserting hier type parameter typedef: " << tdef << endl);
|
||||
VSymEnt* const upperSymp = m_curSymp ? m_curSymp : m_statep->rootEntp();
|
||||
m_curSymp = m_modSymp = m_statep->insertBlock(upperSymp, nodep->name(),
|
||||
nodep, m_classOrPackagep);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Will be optimized away later
|
||||
// Can't remove now, as our backwards iterator will throw up
|
||||
UINFO(5, "Module not under any CELL or top - dead module: " << nodep << endl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -930,6 +950,7 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||
VL_RESTORER(m_paramNum);
|
||||
VL_RESTORER(m_modBlockNum);
|
||||
VL_RESTORER(m_modWithNum);
|
||||
VL_RESTORER(m_modArgNum);
|
||||
{
|
||||
UINFO(4, " Link Class: " << nodep << endl);
|
||||
VSymEnt* const upperSymp = m_curSymp;
|
||||
@@ -943,6 +964,7 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||
m_paramNum = 0;
|
||||
m_modBlockNum = 0;
|
||||
m_modWithNum = 0;
|
||||
m_modArgNum = 0;
|
||||
m_explicitNew = false;
|
||||
// m_modSymp/m_curSymp for non-packages set by AstCell above this module
|
||||
// Iterate
|
||||
@@ -1360,6 +1382,29 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||
}
|
||||
void visit(AstParamTypeDType* nodep) override {
|
||||
UASSERT_OBJ(m_curSymp, nodep, "Parameter type not under module/package/$unit");
|
||||
|
||||
// Replace missing param types with provided hierarchical type params.
|
||||
if (!m_hierParamsName.empty()) {
|
||||
if (const VSymEnt* const typedefEntp = m_curSymp->findIdFallback(m_hierParamsName)) {
|
||||
const AstModule* modp = VN_CAST(typedefEntp->nodep(), Module);
|
||||
|
||||
for (const AstNode* node = modp ? modp->stmtsp() : nullptr; node;
|
||||
node = node->nextp()) {
|
||||
const AstTypedef* tdefp = VN_CAST(node, Typedef);
|
||||
|
||||
if (tdefp && tdefp->name() == nodep->name() && m_statep->forPrimary()) {
|
||||
UINFO(8, "Replacing type of" << nodep << endl
|
||||
<< " with " << tdefp << endl);
|
||||
AstNodeDType* const newType = tdefp->childDTypep();
|
||||
AstNodeDType* const oldType = nodep->childDTypep();
|
||||
|
||||
oldType->replaceWith(newType->cloneTree(false));
|
||||
oldType->deleteTree();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iterateChildren(nodep);
|
||||
m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_classOrPackagep);
|
||||
if (m_statep->forPrimary() && nodep->isGParam()) {
|
||||
@@ -1486,6 +1531,7 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||
// We use an int type, this might get changed in V3Width when types resolve
|
||||
argrefp = new AstVar{parserefp->fileline(), VVarType::BLOCKTEMP,
|
||||
parserefp->name(), argp->findSigned32DType()};
|
||||
argrefp->lifetime(VLifetime::AUTOMATIC);
|
||||
parserefp->replaceWith(argrefp);
|
||||
VL_DO_DANGLING(parserefp->deleteTree(), parserefp);
|
||||
// Insert argref's name into symbol table
|
||||
@@ -1505,11 +1551,15 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||
|
||||
void visit(AstWithParse* nodep) override {
|
||||
// Change WITHPARSE(FUNCREF, equation) to FUNCREF(WITH(equation))
|
||||
const auto funcrefp = VN_AS(nodep->funcrefp(), NodeFTaskRef);
|
||||
AstNodeFTaskRef* funcrefp = VN_CAST(nodep->funcrefp(), NodeFTaskRef);
|
||||
if (const AstDot* const dotp = VN_CAST(nodep->funcrefp(), Dot))
|
||||
funcrefp = VN_CAST(dotp->rhsp(), NodeFTaskRef);
|
||||
UASSERT_OBJ(funcrefp, nodep, "'with' only can operate on a function/task");
|
||||
string name = "item";
|
||||
FileLine* argFl = nodep->fileline();
|
||||
if (const auto argp = VN_CAST(funcrefp->pinsp(), Arg)) {
|
||||
AstArg* argp = VN_CAST(funcrefp->pinsp(), Arg);
|
||||
if (argp) argp->unlinkFrBackWithNext();
|
||||
if (argp && funcrefp->name() != "randomize") {
|
||||
if (const auto parserefp = VN_CAST(argp->exprp(), ParseRef)) {
|
||||
name = parserefp->name();
|
||||
argFl = parserefp->fileline();
|
||||
@@ -1518,7 +1568,7 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||
}
|
||||
if (argp->nextp())
|
||||
argp->nextp()->v3error("'with' function expects only up to one argument");
|
||||
VL_DO_DANGLING(argp->unlinkFrBackWithNext()->deleteTree(), argp);
|
||||
VL_DO_DANGLING(argp->deleteTree(), argp);
|
||||
}
|
||||
// Type depends on the method used, let V3Width figure it out later
|
||||
if (nodep->exprsp()) { // Else empty expression and pretend no "with"
|
||||
@@ -1528,7 +1578,8 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||
nodep->exprsp()->unlinkFrBackWithNext()};
|
||||
funcrefp->addPinsp(newp);
|
||||
}
|
||||
nodep->replaceWith(funcrefp->unlinkFrBack());
|
||||
funcrefp->addPinsp(argp);
|
||||
nodep->replaceWith(nodep->funcrefp()->unlinkFrBack());
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
void visit(AstWith* nodep) override {
|
||||
@@ -1602,7 +1653,7 @@ class LinkDotParamVisitor final : public VNVisitor {
|
||||
void visit(AstConstPool*) override {}
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
UINFO(5, " " << nodep << endl);
|
||||
if (nodep->dead() || !nodep->user4()) {
|
||||
if ((nodep->dead() || !nodep->user4()) && !nodep->hierParams()) {
|
||||
UINFO(4, "Mark dead module " << nodep << endl);
|
||||
UASSERT_OBJ(m_statep->forPrearray(), nodep,
|
||||
"Dead module persisted past where should have removed");
|
||||
@@ -1839,6 +1890,7 @@ class LinkDotScopeVisitor final : public VNVisitor {
|
||||
symp->fallbackp(m_modSymp);
|
||||
// No recursion, we don't want to pick up variables
|
||||
}
|
||||
void visit(AstConstraintForeach* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstWith* nodep) override {
|
||||
VSymEnt* const symp = m_statep->insertBlock(m_modSymp, nodep->name(), nodep, nullptr);
|
||||
symp->fallbackp(m_modSymp);
|
||||
@@ -2043,11 +2095,14 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
VSymEnt* m_curSymp = nullptr; // SymEnt for current lookup point
|
||||
VSymEnt* m_modSymp = nullptr; // SymEnt for current module
|
||||
VSymEnt* m_pinSymp = nullptr; // SymEnt for pin lookups
|
||||
VSymEnt* m_fromSymp = nullptr; // SymEnt for randomize lookups
|
||||
const AstCell* m_cellp = nullptr; // Current cell
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
||||
AstMethodCall* m_randMethodCallp = nullptr; // Current randomize() method call
|
||||
int m_modportNum = 0; // Uniqueify modport numbers
|
||||
bool m_inSens = false; // True if in senitem
|
||||
bool m_inWith = false; // True if in with
|
||||
std::map<std::string, AstNode*> m_ifClassImpNames; // Names imported from interface class
|
||||
std::set<AstClass*> m_extendsParam; // Classes that have a parameterized super class
|
||||
// (except the default instances)
|
||||
@@ -2209,9 +2264,10 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
new AstVarRef{clockingp->fileline(), eventp, VAccess::WRITE},
|
||||
false}});
|
||||
v3Global.setHasEvents();
|
||||
eventp->user1p(new VSymEnt{m_statep->symsp(), eventp});
|
||||
}
|
||||
return reinterpret_cast<VSymEnt*>(clockingp->eventp()->user1p());
|
||||
AstVar* const eventp = clockingp->eventp();
|
||||
if (!eventp->user1p()) eventp->user1p(new VSymEnt{m_statep->symsp(), eventp});
|
||||
return reinterpret_cast<VSymEnt*>(eventp->user1p());
|
||||
}
|
||||
|
||||
bool isParamedClassRefDType(const AstNode* classp) {
|
||||
@@ -2461,6 +2517,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
UINFO(8, " " << nodep << endl);
|
||||
const DotStates lastStates = m_ds;
|
||||
const bool start = (m_ds.m_dotPos == DP_NONE); // Save, as m_dotp will be changed
|
||||
VL_RESTORER(m_fromSymp);
|
||||
{
|
||||
if (start) { // Starting dot sequence
|
||||
if (debug() >= 9) nodep->dumpTree("- dot-in: ");
|
||||
@@ -2576,6 +2633,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
"ParseRefs should no longer exist");
|
||||
const DotStates lastStates = m_ds;
|
||||
const bool start = (m_ds.m_dotPos == DP_NONE); // Save, as m_dotp will be changed
|
||||
|
||||
if (start) {
|
||||
m_ds.init(m_curSymp);
|
||||
// Note m_ds.m_dot remains nullptr; this is a reference not under a dot
|
||||
@@ -2644,12 +2702,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
classOrPackagep = cpackagerefp->classOrPackagep();
|
||||
UASSERT_OBJ(classOrPackagep, m_ds.m_dotp->lhsp(), "Bad package link");
|
||||
if (cpackagerefp->name() == "local::") {
|
||||
if (m_pinSymp) {
|
||||
m_ds.m_dotSymp = m_curSymp->fallbackp();
|
||||
} else {
|
||||
nodep->v3error("Illegal 'local::' outside 'randomize() with'");
|
||||
m_ds.m_dotErr = true;
|
||||
}
|
||||
m_fromSymp = nullptr;
|
||||
} else {
|
||||
m_ds.m_dotSymp = m_statep->getNodeSym(classOrPackagep);
|
||||
}
|
||||
@@ -2671,6 +2724,34 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
VSymEnt* foundp;
|
||||
string baddot;
|
||||
VSymEnt* okSymp = nullptr;
|
||||
if (m_fromSymp) {
|
||||
foundp = m_fromSymp->findIdFlat(nodep->name());
|
||||
if (foundp) {
|
||||
if (!m_inWith) {
|
||||
UASSERT_OBJ(m_randMethodCallp, nodep, "Expected to be under randomize()");
|
||||
// This will start failing once complex expressions are allowed on the LHS
|
||||
// of randomize() with args
|
||||
UASSERT_OBJ(VN_IS(m_randMethodCallp->fromp(), VarRef), m_randMethodCallp,
|
||||
"Expected simple randomize target");
|
||||
// A ParseRef is used here so that the dot RHS gets resolved
|
||||
nodep->replaceWith(new AstMemberSel{
|
||||
nodep->fileline(),
|
||||
new AstParseRef{nodep->fileline(), VParseRefExp::PX_TEXT,
|
||||
m_randMethodCallp->fromp()->name()},
|
||||
VFlagChildDType{}, nodep->name()});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
UINFO(9, " randomize-with fromSym " << foundp->nodep() << endl);
|
||||
if (m_ds.m_dotPos != DP_NONE) m_ds.m_dotPos = DP_MEMBER;
|
||||
AstLambdaArgRef* const lambdaRefp
|
||||
= new AstLambdaArgRef{nodep->fileline(), "item", false};
|
||||
nodep->replaceWith(new AstMemberSel{nodep->fileline(), lambdaRefp,
|
||||
VFlagChildDType{}, nodep->name()});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (allowScope) {
|
||||
foundp = m_statep->findDotted(nodep->fileline(), m_ds.m_dotSymp, nodep->name(),
|
||||
baddot, okSymp); // Maybe nullptr
|
||||
@@ -2726,8 +2807,12 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
}
|
||||
}
|
||||
} else if (allowFTask && VN_IS(foundp->nodep(), NodeFTask)) {
|
||||
AstTaskRef* const taskrefp
|
||||
= new AstTaskRef{nodep->fileline(), nodep->name(), nullptr};
|
||||
AstNodeFTaskRef* taskrefp;
|
||||
if (VN_IS(foundp->nodep(), Task)) {
|
||||
taskrefp = new AstTaskRef{nodep->fileline(), nodep->name(), nullptr};
|
||||
} else {
|
||||
taskrefp = new AstFuncRef{nodep->fileline(), nodep->name(), nullptr};
|
||||
}
|
||||
nodep->replaceWith(taskrefp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
if (start) m_ds = lastStates;
|
||||
@@ -2847,7 +2932,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
} else if (AstConstraint* const consp = VN_CAST(foundp->nodep(), Constraint)) {
|
||||
AstNode* const newp = new AstConstraintRef{nodep->fileline(), consp->name()};
|
||||
AstNode* const newp = new AstConstraintRef{nodep->fileline(), nullptr, consp};
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
ok = true;
|
||||
@@ -2875,6 +2960,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
}
|
||||
} else if (VN_IS(foundp->nodep(), Clocking)) {
|
||||
m_ds.m_dotSymp = foundp;
|
||||
if (m_ds.m_dotText != "") m_ds.m_dotText += "." + nodep->name();
|
||||
ok = m_ds.m_dotPos == DP_SCOPE;
|
||||
} else if (const AstNodeFTask* const ftaskp = VN_CAST(foundp->nodep(), NodeFTask)) {
|
||||
|
||||
@@ -2974,6 +3060,12 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
UINFO(4, "(Backto) Link ClassOrPackageRef: " << nodep << endl);
|
||||
iterateChildren(nodep);
|
||||
|
||||
if (nodep->name() == "local::") {
|
||||
if (!m_fromSymp) {
|
||||
nodep->v3error("Illegal 'local::' outside 'randomize() with'");
|
||||
m_ds.m_dotErr = true;
|
||||
}
|
||||
}
|
||||
AstClass* const modClassp = VN_CAST(m_modp, Class);
|
||||
if (m_statep->forPrimary() && refClassp && !nodep->paramsp()
|
||||
&& nodep->classOrPackagep()->hasGParam()
|
||||
@@ -3044,7 +3136,12 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
dotSymp = m_statep->findDotted(nodep->fileline(), dotSymp, nodep->dotted(), baddot,
|
||||
okSymp); // Maybe nullptr
|
||||
if (!m_statep->forScopeCreation()) {
|
||||
VSymEnt* const foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot);
|
||||
VSymEnt* foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot);
|
||||
if (m_inSens && foundp) {
|
||||
if (AstClocking* const clockingp = VN_CAST(foundp->nodep(), Clocking)) {
|
||||
foundp = getCreateClockingEventSymEnt(clockingp);
|
||||
}
|
||||
}
|
||||
AstVar* const varp
|
||||
= foundp ? foundToVarp(foundp, nodep, nodep->access()) : nullptr;
|
||||
nodep->varp(varp);
|
||||
@@ -3061,11 +3158,14 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
// AstVarXRef's even though they are in the same module detect
|
||||
// this and convert to normal VarRefs
|
||||
if (!m_statep->forPrearray() && !m_statep->forScopeCreation()) {
|
||||
if (VN_IS(nodep->dtypep(), IfaceRefDType)) {
|
||||
AstVarRef* const newrefp
|
||||
= new AstVarRef{nodep->fileline(), nodep->varp(), nodep->access()};
|
||||
nodep->replaceWith(newrefp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
if (const AstIfaceRefDType* const ifaceDtp
|
||||
= VN_CAST(nodep->dtypep(), IfaceRefDType)) {
|
||||
if (!ifaceDtp->isVirtual()) {
|
||||
AstVarRef* const newrefp
|
||||
= new AstVarRef{nodep->fileline(), nodep->varp(), nodep->access()};
|
||||
nodep->replaceWith(newrefp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -3111,10 +3211,12 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
void visit(AstMethodCall* nodep) override {
|
||||
// Created here so should already be resolved.
|
||||
VL_RESTORER(m_ds);
|
||||
VL_RESTORER(m_pinSymp);
|
||||
VL_RESTORER(m_fromSymp);
|
||||
VL_RESTORER(m_randMethodCallp);
|
||||
{
|
||||
m_ds.init(m_curSymp);
|
||||
if (nodep->name() == "randomize" && VN_IS(nodep->pinsp(), With)) {
|
||||
if (nodep->name() == "randomize" && nodep->pinsp()) {
|
||||
m_randMethodCallp = nodep;
|
||||
const AstNodeDType* fromDtp = nodep->fromp()->dtypep();
|
||||
if (!fromDtp) {
|
||||
if (const AstNodeVarRef* const varRefp = VN_CAST(nodep->fromp(), NodeVarRef)) {
|
||||
@@ -3131,9 +3233,17 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
fromDtp = varRefp->varp()->dtypeSkipRefp()->subDTypep();
|
||||
}
|
||||
}
|
||||
if (!fromDtp)
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: 'randomize() with' on complex expressions");
|
||||
if (!fromDtp) {
|
||||
if (VN_IS(nodep->pinsp(), With)) {
|
||||
nodep->v3warn(
|
||||
E_UNSUPPORTED,
|
||||
"Unsupported: 'randomize() with' on complex expressions");
|
||||
} else {
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Inline random variable control with "
|
||||
"'randomize()' called on complex expressions");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_statep->forPrimary() && isParamedClassRefDType(fromDtp)) {
|
||||
m_ds.m_unresolvedClass = true;
|
||||
@@ -3144,7 +3254,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
nodep->v3error("'randomize() with' on a non-class-instance "
|
||||
<< fromDtp->prettyNameQ());
|
||||
else
|
||||
m_pinSymp = m_statep->getNodeSym(classDtp->classp());
|
||||
m_fromSymp = m_statep->getNodeSym(classDtp->classp());
|
||||
}
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
@@ -3178,6 +3288,8 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
VL_RESTORER(m_fromSymp);
|
||||
|
||||
bool staticAccess = false;
|
||||
if (m_ds.m_unresolvedClass) {
|
||||
// Unable to link before V3Param
|
||||
@@ -3197,12 +3309,12 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
staticAccess = true;
|
||||
AstClassOrPackageRef* const cpackagerefp
|
||||
= VN_AS(m_ds.m_dotp->lhsp(), ClassOrPackageRef);
|
||||
if (cpackagerefp->name() == "local") {
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: " << AstNode::prettyNameQ(cpackagerefp->name()));
|
||||
}
|
||||
UASSERT_OBJ(cpackagerefp->classOrPackagep(), m_ds.m_dotp->lhsp(), "Bad package link");
|
||||
nodep->classOrPackagep(cpackagerefp->classOrPackagep());
|
||||
if (cpackagerefp->name() == "local::") {
|
||||
m_fromSymp = nullptr;
|
||||
} else {
|
||||
nodep->classOrPackagep(cpackagerefp->classOrPackagep());
|
||||
}
|
||||
// Class/package :: HERE function() . method_called_on_function_return_value()
|
||||
m_ds.m_dotPos = DP_MEMBER;
|
||||
m_ds.m_dotp = nullptr;
|
||||
@@ -3271,6 +3383,24 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
dotSymp = m_statep->findDotted(nodep->fileline(), dotSymp, nodep->dotted(), baddot,
|
||||
okSymp); // Maybe nullptr
|
||||
}
|
||||
if (m_fromSymp) {
|
||||
VSymEnt* const foundp = m_fromSymp->findIdFlat(nodep->name());
|
||||
if (foundp && m_inWith) {
|
||||
UINFO(9, " randomize-with fromSym " << foundp->nodep() << endl);
|
||||
AstNodeExpr* argsp = nullptr;
|
||||
if (nodep->pinsp()) {
|
||||
iterateAndNextNull(nodep->pinsp());
|
||||
argsp = nodep->pinsp()->unlinkFrBackWithNext();
|
||||
}
|
||||
if (m_ds.m_dotPos != DP_NONE) m_ds.m_dotPos = DP_MEMBER;
|
||||
AstNode* const newp = new AstMethodCall{
|
||||
nodep->fileline(), new AstLambdaArgRef{nodep->fileline(), "item", false},
|
||||
VFlagChildDType{}, nodep->name(), argsp};
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
VSymEnt* const foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot);
|
||||
AstNodeFTask* const taskp
|
||||
= foundp ? VN_CAST(foundp->nodep(), NodeFTask) : nullptr; // Maybe nullptr
|
||||
@@ -3309,8 +3439,8 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
} else if (VN_IS(nodep, New) && m_statep->forPrearray()) {
|
||||
// Resolved in V3Width
|
||||
} else if (nodep->name() == "randomize" || nodep->name() == "srandom"
|
||||
|| nodep->name() == "get_randstate"
|
||||
|| nodep->name() == "set_randstate") {
|
||||
|| nodep->name() == "get_randstate" || nodep->name() == "set_randstate"
|
||||
|| nodep->name() == "rand_mode" || nodep->name() == "constraint_mode") {
|
||||
if (AstClass* const classp = VN_CAST(m_modp, Class)) {
|
||||
nodep->classOrPackagep(classp);
|
||||
} else {
|
||||
@@ -3344,8 +3474,13 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
return;
|
||||
} else {
|
||||
VSpellCheck speller;
|
||||
V3Parse::candidatePli(&speller);
|
||||
const string suggest = speller.bestCandidateMsg(nodep->prettyName());
|
||||
nodep->v3error(
|
||||
"Unsupported or unknown PLI call: " << nodep->prettyNameQ());
|
||||
"Unsupported or unknown PLI call: "
|
||||
<< nodep->prettyNameQ() << '\n'
|
||||
<< (suggest.empty() ? "" : nodep->warnMore() + suggest));
|
||||
}
|
||||
} else {
|
||||
const string suggest = m_statep->suggestSymFallback(
|
||||
@@ -3504,9 +3639,10 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
UINFO(5, " " << nodep << endl);
|
||||
checkNoDot(nodep);
|
||||
VL_RESTORER(m_curSymp);
|
||||
VL_RESTORER(m_inWith);
|
||||
{
|
||||
m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep);
|
||||
if (m_pinSymp) m_curSymp->importFromClass(m_statep->symsp(), m_pinSymp);
|
||||
m_inWith = true;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_ds.m_dotSymp = VL_RESTORER_PREV(m_curSymp);
|
||||
|
||||
+1
-1
@@ -246,7 +246,7 @@ class LinkIncVisitor final : public VNVisitor {
|
||||
|
||||
// Prepare a temporary variable
|
||||
FileLine* const fl = nodep->fileline();
|
||||
const string name = string{"__Vincrement"} + cvtToStr(++m_modIncrementsNum);
|
||||
const string name = "__Vincrement"s + cvtToStr(++m_modIncrementsNum);
|
||||
AstVar* const varp = new AstVar{
|
||||
fl, VVarType::BLOCKTEMP, name, VFlagChildDType{},
|
||||
new AstRefDType{fl, AstRefDType::FlagTypeOfExpr{}, readp->cloneTree(true)}};
|
||||
|
||||
+1
-1
@@ -201,7 +201,7 @@ class LinkJumpVisitor final : public VNVisitor {
|
||||
// REPEAT(count,body) -> loop=count,WHILE(loop>0) { body, loop-- }
|
||||
// Note var can be signed or unsigned based on original number.
|
||||
AstNodeExpr* const countp = nodep->countp()->unlinkFrBackWithNext();
|
||||
const string name = string{"__Vrepeat"} + cvtToStr(m_modRepeatNum++);
|
||||
const string name = "__Vrepeat"s + cvtToStr(m_modRepeatNum++);
|
||||
// Spec says value is integral, if negative is ignored
|
||||
AstVar* const varp
|
||||
= new AstVar{nodep->fileline(), VVarType::BLOCKTEMP, name, nodep->findSigned32DType()};
|
||||
|
||||
+57
-3
@@ -36,12 +36,16 @@ class LinkLValueVisitor final : public VNVisitor {
|
||||
bool m_setContinuously = false; // Set that var has some continuous assignment
|
||||
bool m_setStrengthSpecified = false; // Set that var has assignment with strength specified.
|
||||
bool m_setForcedByCode = false; // Set that var is the target of an AstAssignForce/AstRelease
|
||||
bool m_setIfRand = false; // Update VarRefs if var declared as rand
|
||||
bool m_inInitialStatic = false; // Set if inside AstInitialStatic
|
||||
bool m_inFunc = false; // Set if inside AstNodeFTask
|
||||
VAccess m_setRefLvalue; // Set VarRefs to lvalues for pin assignments
|
||||
|
||||
// VISITs
|
||||
// Result handing
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
// VarRef: LValue its reference
|
||||
if (m_setIfRand && !(nodep->varp() && nodep->varp()->isRand())) return;
|
||||
if (m_setRefLvalue != VAccess::NOCHANGE) nodep->access(m_setRefLvalue);
|
||||
if (nodep->varp() && nodep->access().isWriteOrRW()) {
|
||||
if (m_setContinuously) {
|
||||
@@ -50,15 +54,19 @@ class LinkLValueVisitor final : public VNVisitor {
|
||||
// so it is needed to check only if m_setContinuously is true
|
||||
if (m_setStrengthSpecified) nodep->varp()->hasStrengthAssignment(true);
|
||||
}
|
||||
if (const AstClockingItem* itemp = VN_CAST(nodep->varp()->backp(), ClockingItem)) {
|
||||
if (const AstClockingItem* const itemp
|
||||
= VN_CAST(nodep->varp()->backp(), ClockingItem)) {
|
||||
UINFO(5, "ClkOut " << nodep << endl);
|
||||
if (itemp->outputp()) nodep->varp(itemp->outputp()->varp());
|
||||
}
|
||||
if (m_setForcedByCode) {
|
||||
nodep->varp()->setForcedByCode();
|
||||
} else if (!nodep->varp()->isFuncLocal() && nodep->varp()->isReadOnly()) {
|
||||
nodep->v3warn(ASSIGNIN,
|
||||
"Assigning to input/const variable: " << nodep->prettyNameQ());
|
||||
// This is allowed with IEEE 1800-2009 module input with default value.
|
||||
// the checking now happens in V3Width::visit(AstNodeVarRef*)
|
||||
// If you were to check here, it would fail on module inputs with default value,
|
||||
// because Inputs are isReadOnly()=true, and we don't yet have visibility into
|
||||
// it being an Initial style procedure.
|
||||
}
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
@@ -95,6 +103,35 @@ class LinkLValueVisitor final : public VNVisitor {
|
||||
m_setStrengthSpecified = false;
|
||||
iterateAndNextNull(nodep->rhsp());
|
||||
}
|
||||
|
||||
if (m_inInitialStatic && m_inFunc) {
|
||||
const bool rhsHasIO = nodep->rhsp()->exists([](const AstNodeVarRef* const refp) {
|
||||
// Exclude module I/O referenced from a function/task.
|
||||
return refp->varp() && refp->varp()->isIO()
|
||||
&& refp->varp()->lifetime() != VLifetime::NONE;
|
||||
});
|
||||
if (rhsHasIO) {
|
||||
nodep->rhsp()->v3warn(E_UNSUPPORTED,
|
||||
"Static variable initializer\n"
|
||||
<< nodep->rhsp()->warnMore()
|
||||
<< "is dependent on function/task I/O variable");
|
||||
} else {
|
||||
const bool rhsHasAutomatic
|
||||
= nodep->rhsp()->exists([](const AstNodeVarRef* const refp) {
|
||||
return refp->varp() && refp->varp()->lifetime() == VLifetime::AUTOMATIC;
|
||||
});
|
||||
if (rhsHasAutomatic) {
|
||||
nodep->rhsp()->v3error("Static variable initializer\n"
|
||||
<< nodep->rhsp()->warnMore()
|
||||
<< "is dependent on automatic variable");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void visit(AstInitialStatic* nodep) override {
|
||||
VL_RESTORER(m_inInitialStatic);
|
||||
m_inInitialStatic = true;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstRelease* nodep) override {
|
||||
VL_RESTORER(m_setRefLvalue);
|
||||
@@ -285,6 +322,13 @@ class LinkLValueVisitor final : public VNVisitor {
|
||||
void visit(AstMemberSel* nodep) override {
|
||||
if (m_setRefLvalue != VAccess::NOCHANGE) {
|
||||
nodep->access(m_setRefLvalue);
|
||||
if (nodep->varp() && nodep->access().isWriteOrRW()) {
|
||||
if (const AstClockingItem* const itemp
|
||||
= VN_CAST(nodep->varp()->backp(), ClockingItem)) {
|
||||
UINFO(5, "ClkOut " << nodep << endl);
|
||||
if (itemp->outputp()) nodep->varp(itemp->outputp()->varp());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// It is the only place where the access is set to member select nodes.
|
||||
// If it doesn't have to be set to WRITE, it means that it is READ.
|
||||
@@ -313,6 +357,16 @@ class LinkLValueVisitor final : public VNVisitor {
|
||||
}
|
||||
}
|
||||
}
|
||||
void visit(AstConstraint* nodep) override {
|
||||
VL_RESTORER(m_setIfRand);
|
||||
m_setIfRand = true;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNodeFTask* nodep) override {
|
||||
VL_RESTORER(m_inFunc);
|
||||
m_inFunc = true;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
|
||||
+6
-3
@@ -371,9 +371,12 @@ class LinkParseVisitor final : public VNVisitor {
|
||||
FileLine* const fl = nodep->valuep()->fileline();
|
||||
if (nodep->isParam() || (m_ftaskp && nodep->isNonOutput())) {
|
||||
// 1. Parameters and function inputs: It's a default to use if not overridden
|
||||
} else if (!m_ftaskp && !VN_IS(m_modp, Class) && nodep->isNonOutput()) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Default value on module input: "
|
||||
<< nodep->prettyNameQ());
|
||||
} else if (!m_ftaskp && !VN_IS(m_modp, Class) && nodep->isNonOutput()
|
||||
&& !nodep->isInput()) {
|
||||
// Module inout/ref/constref: const default to use
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Default value on module inout/ref/constref: "
|
||||
<< nodep->prettyNameQ());
|
||||
nodep->valuep()->unlinkFrBack()->deleteTree();
|
||||
} // 2. Under modules/class, it's an initial value to be loaded at time 0 via an
|
||||
// AstInitial
|
||||
|
||||
@@ -65,6 +65,11 @@ class NameVisitor final : public VNVisitorConst {
|
||||
nodep->name(newname);
|
||||
nodep->editCountInc();
|
||||
} else if (VN_IS(nodep, CFunc) && VN_AS(nodep, CFunc)->isConstructor()) {
|
||||
} else if (v3Global.opt.emitAccessors() && VN_IS(nodep, Var)
|
||||
&& VN_AS(nodep, Var)->isSigPublic()) {
|
||||
const string newname = "__Vm_sig_" + nodep->name();
|
||||
nodep->name(newname);
|
||||
nodep->editCountInc();
|
||||
} else {
|
||||
renameKeywordCheck(nodep);
|
||||
}
|
||||
|
||||
+13
-19
@@ -570,7 +570,7 @@ string V3Number::ascii(bool prefixed, bool cleanVerilog) const VL_MT_STABLE {
|
||||
}
|
||||
if (isNull()) {
|
||||
if (VL_UNCOVERABLE(!isEqZero())) {
|
||||
out << "-%E-null-not-zero";
|
||||
out << "-%E-null-not-zero"; // LCOV_EXCL_LINE
|
||||
} else {
|
||||
out << " [null]";
|
||||
}
|
||||
@@ -1202,16 +1202,6 @@ V3Number& V3Number::opBitsZ(const V3Number& lhs) { // 0/1->1, X/Z->0
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
V3Number& V3Number::opBitsNonZ(const V3Number& lhs) { // 0/1->1, X/Z->0
|
||||
// op i, L(lhs) bit return
|
||||
NUM_ASSERT_OP_ARGS1(lhs);
|
||||
NUM_ASSERT_LOGIC_ARGS1(lhs);
|
||||
setZero();
|
||||
for (int bit = 0; bit < width(); bit++) {
|
||||
if (!lhs.bitIsZ(bit)) setBit(bit, 1);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Operators - Simple per-bit logical ops
|
||||
@@ -1706,11 +1696,13 @@ V3Number& V3Number::opWildEq(const V3Number& lhs, const V3Number& rhs) {
|
||||
NUM_ASSERT_LOGIC_ARGS2(lhs, rhs);
|
||||
char outc = 1;
|
||||
for (int bit = 0; bit < std::max(lhs.width(), rhs.width()); bit++) {
|
||||
if (!rhs.bitIsXZ(bit) && lhs.bitIs(bit) != rhs.bitIs(bit)) {
|
||||
outc = 0;
|
||||
goto last;
|
||||
if (!rhs.bitIsXZ(bit)) {
|
||||
if (lhs.bitIs(bit) != rhs.bitIs(bit)) {
|
||||
outc = 0;
|
||||
goto last;
|
||||
}
|
||||
if (lhs.bitIsXZ(bit)) outc = 'x';
|
||||
}
|
||||
if (lhs.bitIsXZ(bit)) outc = 'x';
|
||||
}
|
||||
last:
|
||||
return setSingleBits(outc);
|
||||
@@ -1721,11 +1713,13 @@ V3Number& V3Number::opWildNeq(const V3Number& lhs, const V3Number& rhs) {
|
||||
NUM_ASSERT_LOGIC_ARGS2(lhs, rhs);
|
||||
char outc = 0;
|
||||
for (int bit = 0; bit < std::max(lhs.width(), rhs.width()); bit++) {
|
||||
if (!rhs.bitIsXZ(bit) && lhs.bitIs(bit) != rhs.bitIs(bit)) {
|
||||
outc = 1;
|
||||
goto last;
|
||||
if (!rhs.bitIsXZ(bit)) {
|
||||
if (lhs.bitIs(bit) != rhs.bitIs(bit)) {
|
||||
outc = 1;
|
||||
goto last;
|
||||
}
|
||||
if (lhs.bitIsXZ(bit)) outc = 'x';
|
||||
}
|
||||
if (lhs.bitIsXZ(bit)) outc = 'x';
|
||||
}
|
||||
last:
|
||||
return setSingleBits(outc);
|
||||
|
||||
@@ -670,7 +670,6 @@ public:
|
||||
V3Number& opBitsOne(const V3Number& lhs); // 1->1, 0/X/Z->0
|
||||
V3Number& opBitsXZ(const V3Number& lhs); // 0/1->0, X/Z->1
|
||||
V3Number& opBitsZ(const V3Number& lhs); // Z->1, 0/1/X->0
|
||||
V3Number& opBitsNonZ(const V3Number& lhs); // Z->0, 0/1/X->1
|
||||
//
|
||||
V3Number& opAssign(const V3Number& lhs);
|
||||
V3Number& opAssignNonXZ(const V3Number& lhs, bool ignoreXZ = true);
|
||||
|
||||
+28
-11
@@ -361,6 +361,9 @@ void V3Options::checkParameters() {
|
||||
|
||||
void V3Options::addCppFile(const string& filename) { m_cppFiles.insert(filename); }
|
||||
void V3Options::addCFlags(const string& filename) { m_cFlags.push_back(filename); }
|
||||
void V3Options::addCompilerIncludes(const string& filename) {
|
||||
m_compilerIncludes.insert(filename);
|
||||
}
|
||||
void V3Options::addLdLibs(const string& filename) { m_ldLibs.push_back(filename); }
|
||||
void V3Options::addMakeFlags(const string& filename) { m_makeFlags.push_back(filename); }
|
||||
void V3Options::addFuture(const string& flag) { m_futures.insert(flag); }
|
||||
@@ -550,7 +553,7 @@ string V3Options::filePath(FileLine* fl, const string& modname, const string& la
|
||||
// Find a filename to read the specified module name,
|
||||
// using the incdir and libext's.
|
||||
// Return "" if not found.
|
||||
const string filename = V3Os::filenameCleanup(modname);
|
||||
const string filename = V3Os::filenameCleanup(VName::dehash(modname));
|
||||
if (!V3Os::filenameIsRel(filename)) {
|
||||
// filename is an absolute path, so can find getStdPackagePath()
|
||||
string exists = filePathCheckOneDir(filename, "");
|
||||
@@ -572,7 +575,7 @@ string V3Options::filePath(FileLine* fl, const string& modname, const string& la
|
||||
|
||||
// Warn and return not found
|
||||
if (errmsg != "") {
|
||||
fl->v3error(errmsg + filename);
|
||||
fl->v3error(errmsg + "'"s + filename + "'"s);
|
||||
filePathLookedMsg(fl, filename);
|
||||
}
|
||||
return "";
|
||||
@@ -582,8 +585,8 @@ void V3Options::filePathLookedMsg(FileLine* fl, const string& modname) {
|
||||
static bool shown_notfound_msg = false;
|
||||
if (modname.find("__Vhsh") != string::npos) {
|
||||
std::cerr << V3Error::warnMoreStandalone()
|
||||
<< "... Unsupported: Name is longer than 127 characters;"
|
||||
<< " automatic file lookup not supported.\n";
|
||||
<< "... Note: Name is longer than 127 characters; automatic"
|
||||
<< " file lookup may have failed due to OS filename length limits.\n";
|
||||
std::cerr << V3Error::warnMoreStandalone()
|
||||
<< "... Suggest putting filename with this module/package"
|
||||
<< " onto command line instead.\n";
|
||||
@@ -634,6 +637,8 @@ string V3Options::getenvBuiltins(const string& var) {
|
||||
return getenvMAKE();
|
||||
} else if (var == "PERL") {
|
||||
return getenvPERL();
|
||||
} else if (var == "PYTHON3") {
|
||||
return getenvPYTHON3();
|
||||
} else if (var == "SYSTEMC") {
|
||||
return getenvSYSTEMC();
|
||||
} else if (var == "SYSTEMC_ARCH") {
|
||||
@@ -663,6 +668,10 @@ string V3Options::getenvPERL() { //
|
||||
return V3Os::filenameCleanup(V3Os::getenvStr("PERL", "perl"));
|
||||
}
|
||||
|
||||
string V3Options::getenvPYTHON3() { //
|
||||
return V3Os::filenameCleanup(V3Os::getenvStr("PYTHON3", "python3"));
|
||||
}
|
||||
|
||||
string V3Options::getenvSYSTEMC() {
|
||||
string var = V3Os::getenvStr("SYSTEMC", "");
|
||||
// Treat compiled-in DEFENV string literals as C-strings to enable
|
||||
@@ -698,9 +707,9 @@ string V3Options::getenvSYSTEMC_ARCH() {
|
||||
uname(&uts);
|
||||
const string sysname = VString::downcase(uts.sysname); // aka 'uname -s'
|
||||
if (VL_UNCOVERABLE(VString::wildmatch(sysname.c_str(), "*solaris*"))) {
|
||||
var = "gccsparcOS5";
|
||||
var = "gccsparcOS5"; // LCOV_EXCL_LINE
|
||||
} else if (VL_UNCOVERABLE(VString::wildmatch(sysname.c_str(), "*cygwin*"))) {
|
||||
var = "cygwin";
|
||||
var = "cygwin"; // LCOV_EXCL_LINE
|
||||
} else {
|
||||
var = "linux";
|
||||
}
|
||||
@@ -855,8 +864,11 @@ void V3Options::notify() VL_MT_DISABLED {
|
||||
cmdfl->v3error(
|
||||
"--hierarchical must not be set with --hierarchical-child or --hierarchical-block");
|
||||
}
|
||||
if (m_hierChild && m_hierBlocks.empty()) {
|
||||
cmdfl->v3error("--hierarchical-block must be set when --hierarchical-child is set");
|
||||
if (m_hierChild) {
|
||||
if (m_hierBlocks.empty()) {
|
||||
cmdfl->v3error("--hierarchical-block must be set when --hierarchical-child is set");
|
||||
}
|
||||
m_main = false;
|
||||
}
|
||||
|
||||
if (protectIds()) {
|
||||
@@ -1022,10 +1034,9 @@ void V3Options::parseOpts(FileLine* fl, int argc, char** argv) VL_MT_DISABLED {
|
||||
}
|
||||
|
||||
// Default prefix to the filename
|
||||
if (prefix() == "" && topModule() != "")
|
||||
m_prefix = string{"V"} + AstNode::encodeName(topModule());
|
||||
if (prefix() == "" && topModule() != "") m_prefix = "V"s + AstNode::encodeName(topModule());
|
||||
if (prefix() == "" && vFilesList.size() >= 1)
|
||||
m_prefix = string{"V"} + AstNode::encodeName(V3Os::filenameNonExt(*(vFilesList.begin())));
|
||||
m_prefix = "V"s + AstNode::encodeName(V3Os::filenameNonDirExt(*(vFilesList.begin())));
|
||||
if (modPrefix() == "") m_modPrefix = prefix();
|
||||
|
||||
// Find files in makedir
|
||||
@@ -1186,6 +1197,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
|
||||
<< fl->warnMore() << "... Suggest 'clang', 'gcc', or 'msvc'");
|
||||
}
|
||||
});
|
||||
DECL_OPTION("-compiler-include", CbVal, callStrSetter(&V3Options::addCompilerIncludes));
|
||||
DECL_OPTION("-coverage", CbOnOff, [this](bool flag) { coverage(flag); });
|
||||
DECL_OPTION("-converge-limit", Set, &m_convergeLimit);
|
||||
DECL_OPTION("-coverage-line", OnOff, &m_coverageLine);
|
||||
@@ -1236,6 +1248,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
|
||||
if (flag) m_std = false;
|
||||
m_preprocOnly = flag;
|
||||
});
|
||||
DECL_OPTION("-emit-accessors", OnOff, &m_emitAccessors);
|
||||
DECL_OPTION("-error-limit", CbVal, static_cast<void (*)(int)>(&V3Error::errorLimit));
|
||||
DECL_OPTION("-exe", OnOff, &m_exe);
|
||||
DECL_OPTION("-expand-limit", CbVal,
|
||||
@@ -1316,6 +1329,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
|
||||
m_hierBlocks.emplace(opt.mangledName(), opt);
|
||||
});
|
||||
DECL_OPTION("-hierarchical-child", Set, &m_hierChild);
|
||||
DECL_OPTION("-hierarchical-params-file", CbVal,
|
||||
[this](const char* optp) { m_hierParamsFile = optp; });
|
||||
|
||||
DECL_OPTION("-I", CbPartialMatch,
|
||||
[this, &optdir](const char* optp) { addIncDirUser(parseFileArg(optdir, optp)); });
|
||||
@@ -1406,6 +1421,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
|
||||
m_pinsScUint = flag;
|
||||
if (!m_pinsScBigUint) m_pinsBv = 65;
|
||||
});
|
||||
DECL_OPTION("-pins-sc-uint-bool", CbOnOff, [this](bool flag) { m_pinsScUintBool = flag; });
|
||||
DECL_OPTION("-pins-sc-biguint", CbOnOff, [this](bool flag) {
|
||||
m_pinsScBigUint = flag;
|
||||
m_pinsBv = 513;
|
||||
@@ -1950,6 +1966,7 @@ void V3Options::showVersion(bool verbose) {
|
||||
cout << "Environment:\n";
|
||||
cout << " MAKE = " << V3Os::getenvStr("MAKE", "") << "\n";
|
||||
cout << " PERL = " << V3Os::getenvStr("PERL", "") << "\n";
|
||||
cout << " PYTHON3 = " << V3Os::getenvStr("PYTHON3", "") << "\n";
|
||||
cout << " SYSTEMC = " << V3Os::getenvStr("SYSTEMC", "") << "\n";
|
||||
cout << " SYSTEMC_ARCH = " << V3Os::getenvStr("SYSTEMC_ARCH", "") << "\n";
|
||||
cout << " SYSTEMC_INCLUDE = " << V3Os::getenvStr("SYSTEMC_INCLUDE", "") << "\n";
|
||||
|
||||
@@ -203,6 +203,7 @@ private:
|
||||
V3StringList m_cFlags; // argument: user CFLAGS
|
||||
V3StringList m_ldLibs; // argument: user LDFLAGS
|
||||
V3StringList m_makeFlags; // argument: user MAKEFLAGS
|
||||
V3StringSet m_compilerIncludes; // argument: user --compiler-include
|
||||
V3StringSet m_futures; // argument: -Wfuture- list
|
||||
V3StringSet m_future0s; // argument: -future list
|
||||
V3StringSet m_future1s; // argument: -future1 list
|
||||
@@ -249,6 +250,7 @@ private:
|
||||
bool m_decoration = true; // main switch: --decoration
|
||||
bool m_decorationNodes = false; // main switch: --decoration=nodes
|
||||
bool m_dpiHdrOnly = false; // main switch: --dpi-hdr-only
|
||||
bool m_emitAccessors = false; // main switch: --emit-accessors
|
||||
bool m_exe = false; // main switch: --exe
|
||||
bool m_flatten = false; // main switch: --flatten
|
||||
bool m_hierarchical = false; // main switch: --hierarchical
|
||||
@@ -260,6 +262,7 @@ private:
|
||||
bool m_pedantic = false; // main switch: --Wpedantic
|
||||
bool m_pinsInoutEnables = false;// main switch: --pins-inout-enables
|
||||
bool m_pinsScUint = false; // main switch: --pins-sc-uint
|
||||
bool m_pinsScUintBool = false; // main switch: --pins-sc-uint-bool
|
||||
bool m_pinsScBigUint = false; // main switch: --pins-sc-biguint
|
||||
bool m_pinsUint8 = false; // main switch: --pins-uint8
|
||||
bool m_ppComments = false; // main switch: --pp-comments
|
||||
@@ -343,6 +346,7 @@ private:
|
||||
string m_buildDepBin; // main switch: --build-dep-bin {filename}
|
||||
string m_exeName; // main switch: -o {name}
|
||||
string m_flags; // main switch: -f {name}
|
||||
string m_hierParamsFile; // main switch: --hierarchical-params-file
|
||||
string m_l2Name; // main switch: --l2name; "" for top-module's name
|
||||
string m_libCreate; // main switch: --lib-create {lib_name}
|
||||
string m_mainTopName; // main switch: --main-top-name
|
||||
@@ -434,6 +438,7 @@ public:
|
||||
// METHODS
|
||||
void addCppFile(const string& filename);
|
||||
void addCFlags(const string& filename);
|
||||
void addCompilerIncludes(const string& filename);
|
||||
void addLdLibs(const string& filename);
|
||||
void addMakeFlags(const string& filename);
|
||||
void addLibraryFile(const string& filename);
|
||||
@@ -496,6 +501,7 @@ public:
|
||||
bool dumpTreeDot() const {
|
||||
return m_dumpLevel.count("tree-dot") && m_dumpLevel.at("tree-dot");
|
||||
}
|
||||
bool emitAccessors() const { return m_emitAccessors; }
|
||||
bool exe() const { return m_exe; }
|
||||
bool flatten() const { return m_flatten; }
|
||||
bool gmake() const { return m_gmake; }
|
||||
@@ -514,6 +520,7 @@ public:
|
||||
bool pedantic() const { return m_pedantic; }
|
||||
bool pinsInoutEnables() const { return m_pinsInoutEnables; }
|
||||
bool pinsScUint() const { return m_pinsScUint; }
|
||||
bool pinsScUintBool() const { return m_pinsScUintBool; }
|
||||
bool pinsScBigUint() const { return m_pinsScBigUint; }
|
||||
bool pinsUint8() const { return m_pinsUint8; }
|
||||
bool ppComments() const { return m_ppComments; }
|
||||
@@ -594,6 +601,7 @@ public:
|
||||
int compLimitParens() const { return m_compLimitParens; }
|
||||
|
||||
string exeName() const { return m_exeName != "" ? m_exeName : prefix(); }
|
||||
string hierParamFile() const { return m_hierParamsFile; }
|
||||
string l2Name() const { return m_l2Name; }
|
||||
string libCreate() const { return m_libCreate; }
|
||||
string libCreateName(bool shared) {
|
||||
@@ -626,6 +634,7 @@ public:
|
||||
|
||||
const V3StringSet& cppFiles() const { return m_cppFiles; }
|
||||
const V3StringList& cFlags() const { return m_cFlags; }
|
||||
const V3StringSet& compilerIncludes() const { return m_compilerIncludes; }
|
||||
const V3StringList& ldLibs() const { return m_ldLibs; }
|
||||
const V3StringList& makeFlags() const { return m_makeFlags; }
|
||||
const V3StringSet& libraryFiles() const { return m_libraryFiles; }
|
||||
@@ -713,6 +722,7 @@ public:
|
||||
static string getenvMAKE();
|
||||
static string getenvMAKEFLAGS();
|
||||
static string getenvPERL();
|
||||
static string getenvPYTHON3();
|
||||
static string getenvSYSTEMC();
|
||||
static string getenvSYSTEMC_ARCH();
|
||||
static string getenvSYSTEMC_INCLUDE();
|
||||
|
||||
@@ -58,6 +58,15 @@ class V3OrderProcessDomains final {
|
||||
|
||||
// METHODS
|
||||
|
||||
// Dump a domain for debugging
|
||||
std::string debugDomain(AstSenTree* domainp) {
|
||||
return cvtToHex(domainp)
|
||||
+ (domainp == m_deleteDomainp ? " [DEL]"
|
||||
: domainp->hasCombo() ? " [COMB]"
|
||||
: domainp->isMulti() ? " [MULT]"
|
||||
: "");
|
||||
}
|
||||
|
||||
// Make a domain that merges the two domains
|
||||
AstSenTree* combineDomains(AstSenTree* ap, AstSenTree* bp) {
|
||||
if (ap == bp) return ap;
|
||||
@@ -93,6 +102,7 @@ class V3OrderProcessDomains final {
|
||||
// For logic, start with the explicit hybrid sensitivities
|
||||
OrderLogicVertex* const lvtxp = vtxp->cast<OrderLogicVertex>();
|
||||
if (lvtxp) domainp = lvtxp->hybridp();
|
||||
if (domainp) UINFO(6, " hybr d=" << debugDomain(domainp) << " " << vtxp << endl);
|
||||
|
||||
// For each incoming edge, examine the source vertex
|
||||
for (V3GraphEdge& edge : vtxp->inEdges()) {
|
||||
@@ -104,6 +114,7 @@ class V3OrderProcessDomains final {
|
||||
|
||||
AstSenTree* fromDomainp = fromVtxp->domainp();
|
||||
|
||||
UINFO(6, " from d=" << debugDomain(fromDomainp) << " " << fromVtxp << endl);
|
||||
UASSERT(fromDomainp == m_deleteDomainp || !fromDomainp->hasCombo(),
|
||||
"There should be no need for combinational domains");
|
||||
|
||||
@@ -113,6 +124,8 @@ class V3OrderProcessDomains final {
|
||||
externalDomainps.clear();
|
||||
m_externalDomains(vscp, externalDomainps);
|
||||
for (AstSenTree* const externalDomainp : externalDomainps) {
|
||||
UINFO(6, " xtrn d=" << debugDomain(externalDomainp) << " " << fromVtxp
|
||||
<< " because of " << vscp << endl);
|
||||
UASSERT_OBJ(!externalDomainp->hasCombo(), vscp,
|
||||
"There should be no need for combinational domains");
|
||||
fromDomainp = combineDomains(fromDomainp, externalDomainp);
|
||||
@@ -140,12 +153,7 @@ class V3OrderProcessDomains final {
|
||||
// Set the domain of the vertex
|
||||
vtxp->domainp(domainp);
|
||||
|
||||
UINFO(5, " done d=" << cvtToHex(domainp)
|
||||
<< (domainp == m_deleteDomainp ? " [DEL]"
|
||||
: domainp->hasCombo() ? " [COMB]"
|
||||
: domainp->isMulti() ? " [MULT]"
|
||||
: "")
|
||||
<< " " << vtxp << endl);
|
||||
UINFO(5, " done d=" << debugDomain(domainp) << " " << vtxp << endl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
std::vector<AstActive*> V3Order::createSerial(OrderGraph& graph, const std::string& tag,
|
||||
const TrigToSenMap& trigToSen, bool slow) {
|
||||
|
||||
UINFO(2, " Constructing serial code for '" + tag + "'");
|
||||
UINFO(2, " Constructing serial code for '" + tag + "'\n");
|
||||
|
||||
// Build the move graph
|
||||
OrderMoveDomScope::clear();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user