mirror of
https://github.com/verilator/verilator.git
synced 2026-10-05 01:23:37 +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 | ||
|
|
cd693ce02b | ||
|
|
2641bc98aa | ||
|
|
ad2862ce3f | ||
|
|
4695967185 | ||
|
|
f96e99542c | ||
|
|
7de193c2e9 | ||
|
|
442c9bc316 | ||
|
|
3203019408 | ||
|
|
bc853e260b | ||
|
|
d6e5703298 | ||
|
|
a9e50327fd | ||
|
|
6f0a36318e | ||
|
|
221fecad0b | ||
|
|
fd1e4d9e45 | ||
|
|
920ed5ca3d | ||
|
|
2537431273 | ||
|
|
13114f2efe | ||
|
|
e9f59e44a5 | ||
|
|
d4c3e35f97 | ||
|
|
02dd33b60e | ||
|
|
2bc883f3b3 | ||
|
|
29db25b70e | ||
|
|
d6bc0c712a | ||
|
|
962efa3691 | ||
|
|
4babba16d6 | ||
|
|
5363d89870 | ||
|
|
44acb08ea9 | ||
|
|
6584b4d426 | ||
|
|
9ff06c1664 | ||
|
|
1537c9badc | ||
|
|
708f875b31 | ||
|
|
0d3cb86a44 | ||
|
|
5c2b9d55b2 | ||
|
|
1dbf1be3e6 | ||
|
|
3f886f7c61 | ||
|
|
7c9fa8647c | ||
|
|
dbf68a99e8 | ||
|
|
53c2e416f4 | ||
|
|
e834b5be2d | ||
|
|
bd7689f59a | ||
|
|
3496eb80a5 | ||
|
|
839c3bf444 | ||
|
|
d4b3583307 | ||
|
|
ee130cb20d | ||
|
|
552a146f9c | ||
|
|
913679f261 | ||
|
|
d9078df650 | ||
|
|
f84592af49 | ||
|
|
5f85c89425 | ||
|
|
94f789b677 | ||
|
|
739be2f782 | ||
|
|
25b9a16bc7 | ||
|
|
9a8e68928d | ||
|
|
19cccd170e | ||
|
|
d99c8f5d44 | ||
|
|
cf111d2e1f | ||
|
|
45eb5b8a5c | ||
|
|
ce5cad17a8 | ||
|
|
ed01befc25 | ||
|
|
69a2bfee68 | ||
|
|
1cc37ad6ce | ||
|
|
2a9f29912c | ||
|
|
298e0f24d1 | ||
|
|
80b08b71aa | ||
|
|
64ba569f0d | ||
|
|
3cb4033c97 | ||
|
|
6509bac59e | ||
|
|
ec45a77d93 | ||
|
|
8044833c74 | ||
|
|
3f89bdcfac | ||
|
|
c99364b81a | ||
|
|
d841a791e6 | ||
|
|
b5b937e2f2 | ||
|
|
4a41f69293 | ||
|
|
8fd038f88e | ||
|
|
4982f63da2 | ||
|
|
71bc60fb91 | ||
|
|
0da7ecf753 | ||
|
|
25ca1af8b3 | ||
|
|
5d54fa8e6f | ||
|
|
e8c5b9faad | ||
|
|
8e0301c287 | ||
|
|
3f625fc359 | ||
|
|
72b96d5069 | ||
|
|
d1b45b8d32 | ||
|
|
5601056ed0 | ||
|
|
8ed269c77f | ||
|
|
27b7e70218 | ||
|
|
97680656d3 | ||
|
|
4202ec2ab2 | ||
|
|
4f3a816fb0 | ||
|
|
25fd8ef5c0 | ||
|
|
a9664b8d16 | ||
|
|
26a5729514 | ||
|
|
5b839699ac | ||
|
|
1315aa31ed | ||
|
|
5d8da6b4ac | ||
|
|
aac2563a07 | ||
|
|
8e44487354 | ||
|
|
1012c054e6 | ||
|
|
a8b5738b44 | ||
|
|
72845167e6 | ||
|
|
7ca2d6470a | ||
|
|
33e999e01a |
+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
|
||||
|
||||
@@ -18,3 +18,5 @@ What 'verilator --version' are you using? Did you try it with the git master ve
|
||||
What OS and distribution are you using?
|
||||
|
||||
May we assist you in trying to fix this in Verilator yourself?
|
||||
|
||||
(Please avoid attaching screenshots that show text - you can convert images to text using e.g. https://ocr.space)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# See https://citation-file-format.github.io/
|
||||
cff-version: 1.2.0
|
||||
title: Verilator
|
||||
message: >-
|
||||
If you use this software, please cite it using the
|
||||
metadata from this file.
|
||||
type: software
|
||||
authors:
|
||||
- given-names: Wilson
|
||||
family-names: Snyder
|
||||
email: [email protected]
|
||||
affiliation: Veripool
|
||||
- given-names: Paul
|
||||
family-names: Wasson
|
||||
- given-names: Duane
|
||||
family-names: Galbi
|
||||
- name: 'et al'
|
||||
repository-code: 'https://github.com/verilator/verilator'
|
||||
url: 'https://verilator.org'
|
||||
abstract: >-
|
||||
The Verilator package converts Verilog and SystemVerilog hardware
|
||||
description language (HDL) designs into a fast C++ or SystemC model
|
||||
that, after compiling, can be executed. Verilator is not a
|
||||
traditional simulator but a compiler.
|
||||
license:
|
||||
- LGPL-3.0-only
|
||||
- Artistic-2.0
|
||||
+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.024
|
||||
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,158 @@ 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
|
||||
==========================
|
||||
|
||||
**Major:**
|
||||
|
||||
* Support constrained randomization with external solvers (#4947). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Support `$psprintf` system function (#4314) (#5169). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Support 2D dynamic array initialization (#4700) (#5122). [Valentin Atepalikhin]
|
||||
* Support __en/__out signals on top level inout ports (#4812) (#4856). [Paul Wright]
|
||||
* Support empty queue as dynarray default value (#5055). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Support vpiInertialDelay (#5087). [Todd Strader]
|
||||
* Support NBAs to arrays inside loops (#5092). [Geza Lore]
|
||||
* Support parsing and otherwise ignoring inline constraints (#5126). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Support `inout` clocking items (#5160). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Support StructSel in unpacked array assignments (#5176). [Geza Lore]
|
||||
* Add error on zero width select (#5028).
|
||||
* Add CITATION.cff (#5057) (#5058). [Gijs Burghoorn]
|
||||
* Add VPI eval needed tracking (#5065). [Todd Strader]
|
||||
* Add `--localize-max-size` option and optimization (#5072).
|
||||
* Add parameterless assert control system tasks (#5010). [Bartłomiej Chmiel]
|
||||
* 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.]
|
||||
* Fix missing flex include path variable (#4970) (#4971). [Christopher Taylor]
|
||||
* Fix missing parameters with comma to be errors (#4979) (#5012). [Paul Swirhun]
|
||||
* Fix 'experimental/coroutine' file not found on MacOS (#5030) (#5031) (#5151). [Paul Bowen-Huggett]
|
||||
* Fix bound queue printing (#5032). [Aleksander Kiryk, Antmicro Ltd.]
|
||||
* Fix consecutive zero-delays (#5038). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Fix attempted to destroy locked thread pool error (#5040). [Bartłomiej Chmiel, Antmicro Ltd.]
|
||||
* Fix `$system` with string argument (#5042).
|
||||
* Fix width extension on delays (#5043).
|
||||
* Fix `$typename` on array.min and others (#5049). [Gökçe Aydos]
|
||||
* Fix `make $(info)` which cannot be silenced (#5059). [Gökçe Aydos]
|
||||
* Fix CMake builds to export VERILATOR_ROOT (#5063). [Michael Bikovitsky]
|
||||
* Fix false ASSIGNIN on functions with explicit port map (#5069).
|
||||
* Fix 4-state value support for $readmem (#5070) (#5078). [Ethan Sifferman]
|
||||
* Fix DFG assertion with SystemC (#5076). [Geza Lore]
|
||||
* Fix `$typename` string to be more standard (#5082) (#5083). [Andrew Nolte]
|
||||
* Fix missed optimization in V3Delayed (#5089). [Geza Lore]
|
||||
* Fix macro expansion in strings per 1800-2023 (#5094). [Geza Lore]
|
||||
* Fix width extension of unpacked array select (#5095). [Varun Koyyalagunta]
|
||||
* Fix MacOS missing <type_traits> header (#5096) (#5097). [Vito Gamberini]
|
||||
* Fix assertion failure in V3Gate (#5101). [Yutetsu TAKATSUKASA]
|
||||
* Fix aliases for forced port signals (#5105). [Geza Lore]
|
||||
* Fix tracing interface functions (#5108). [Alex Solomatnikov]
|
||||
* Fix method calls parsing in constraints (#5110). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Fix vpiInertialDelay for memories (#5113). [Todd Strader]
|
||||
* Fix hierarchical compilation with nested -F (#5114) (#5124). [Alex Solomatnikov]
|
||||
* Fix references to ports in forks (#5123). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Fix output C++ type error on change detect of I/O arrays (#5125) (#5131). [Pawel Jewstafjew]
|
||||
* Fix x-valued parameters with `--x-assign unique` (#5129). [Ethan Sifferman]
|
||||
* Fix overflow of string on VPI reads (#5145) (#5146). [Kaleb Barrett]
|
||||
* Fix VerilatedVpiPutHolder class (#5156). [Kaleb Barrett]
|
||||
* Fix extending out-of-range select (#5159) (#5164). [Geza Lore]
|
||||
* Fix radix in width warnings (#5166). [Geza Lore]
|
||||
* 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
|
||||
==========================
|
||||
|
||||
@@ -180,6 +332,7 @@ Verilator 5.020 2024-01-01
|
||||
* Fix dynamic NBA conditions (#4773). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Fix `V3Fork` stage to run only if `--timing` is set (#4778). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Fix max multiply width and add runtime assertions if too small. (#4781)
|
||||
* Fix select value too wide (#5148) (#5153). [Dercury]
|
||||
|
||||
|
||||
Verilator 5.018 2023-10-30
|
||||
@@ -552,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]
|
||||
@@ -1443,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
|
||||
|
||||
@@ -91,6 +91,7 @@ datarootdir = @datarootdir@
|
||||
CFG_WITH_CCWARN = @CFG_WITH_CCWARN@
|
||||
CFG_WITH_DEFENV = @CFG_WITH_DEFENV@
|
||||
CFG_WITH_LONGTESTS = @CFG_WITH_LONGTESTS@
|
||||
CFG_WITH_SOLVER = @CFG_WITH_SOLVER@
|
||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||
|
||||
#### End of system configuration section. ####
|
||||
@@ -446,6 +447,7 @@ PY_PROGRAMS = \
|
||||
nodist/fuzzer/actual_fail \
|
||||
nodist/fuzzer/generate_dictionary \
|
||||
nodist/install_test \
|
||||
nodist/log_changes \
|
||||
|
||||
PY_FILES = \
|
||||
$(PY_PROGRAMS) \
|
||||
|
||||
+15
-14
@@ -1,21 +1,23 @@
|
||||
.. Github doesn't render images unless absolute URL
|
||||
.. Do not know of a conditional tag, "only: github" nor "github display" works
|
||||
|
||||
.. image:: https://img.shields.io/badge/Website-Verilator.org-181717.svg
|
||||
|badge1| |badge2| |badge3| |badge4| |badge5| |badge6| |badge7| |badge8|
|
||||
|
||||
.. |badge1| image:: https://img.shields.io/badge/Website-Verilator.org-181717.svg
|
||||
:target: https://verilator.org
|
||||
.. image:: https://img.shields.io/badge/License-LGPL%20v3-blue.svg
|
||||
.. |badge2| image:: https://img.shields.io/badge/License-LGPL%20v3-blue.svg
|
||||
:target: https://www.gnu.org/licenses/lgpl-3.0
|
||||
.. image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg
|
||||
.. |badge3| image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg
|
||||
:target: https://opensource.org/licenses/Artistic-2.0
|
||||
.. image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages
|
||||
.. |badge4| image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages
|
||||
:target: https://repology.org/project/verilator/versions
|
||||
.. image:: https://img.shields.io/docker/pulls/verilator/verilator
|
||||
.. |badge5| image:: https://img.shields.io/docker/pulls/verilator/verilator
|
||||
:target: https://hub.docker.com/r/verilator/verilator
|
||||
.. image:: https://api.codacy.com/project/badge/Grade/fa78caa433c84a4ab9049c43e9debc6f
|
||||
.. |badge6| image:: https://api.codacy.com/project/badge/Grade/fa78caa433c84a4ab9049c43e9debc6f
|
||||
:target: https://www.codacy.com/gh/verilator/verilator
|
||||
.. image:: https://codecov.io/gh/verilator/verilator/branch/master/graph/badge.svg
|
||||
.. |badge7| image:: https://codecov.io/gh/verilator/verilator/branch/master/graph/badge.svg
|
||||
:target: https://codecov.io/gh/verilator/verilator
|
||||
.. image:: https://github.com/verilator/verilator/workflows/build/badge.svg
|
||||
.. |badge8| image:: https://github.com/verilator/verilator/workflows/build/badge.svg
|
||||
:target: https://github.com/verilator/verilator/actions?query=workflow%3Abuild
|
||||
|
||||
|
||||
@@ -85,12 +87,11 @@ thread is about 100 times faster than interpreted Verilog simulators such
|
||||
as `Icarus Verilog`_. Another 2-10x speedup might be gained from
|
||||
multithreading (yielding 200-1000x total over interpreted simulators).
|
||||
|
||||
Verilator has typically similar or better performance versus
|
||||
closed-source Verilog simulators (e.g., Carbon Design Systems Carbonator,
|
||||
Modelsim/Questa, Cadence Incisive/NC-Verilog, Synopsys VCS, VTOC, and
|
||||
Pragmatic CVer/CVC). But, Verilator is open-sourced, so you can spend on
|
||||
computes rather than licenses. Thus, Verilator gives you the best
|
||||
simulation cycles/dollar.
|
||||
Verilator has typically similar or better performance versus closed-source
|
||||
Verilog simulators (e.g., Aldec Riviera-Pro, Cadence Incisive/NC-Verilog,
|
||||
Mentor ModelSim/Questa, Synopsys VCS, VTOC, and Pragmatic CVer/CVC). But,
|
||||
Verilator is open-sourced, so you can spend on computes rather than
|
||||
licenses. Thus, Verilator gives you the best simulation cycles/dollar.
|
||||
|
||||
|
||||
Installation & Documentation
|
||||
|
||||
+8
-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
|
||||
@@ -387,6 +390,7 @@ detailed descriptions of these arguments.
|
||||
--lib-create <name> Create a DPI library
|
||||
+libext+<ext>+[ext]... Extensions for finding modules
|
||||
--lint-only Lint, but do not make output
|
||||
--localize-max-size <value> Tune localize optimization variable size
|
||||
--make <build-tool> Generate scripts for specified build tool
|
||||
-MAKEFLAGS <flags> Arguments to pass to make during --build
|
||||
--main Generate C++ main() file
|
||||
@@ -406,8 +410,10 @@ detailed descriptions of these arguments.
|
||||
--output-split-ctrace <statements> Split tracing functions
|
||||
-P Disable line numbers and blanks with -E
|
||||
--pins-bv <bits> Specify types for top-level ports
|
||||
--pins-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
|
||||
@@ -457,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
|
||||
|
||||
@@ -154,9 +154,9 @@ verilator_coverage - Verilator coverage analyzer
|
||||
|
||||
verilator_coverage --annotate <obj>
|
||||
|
||||
verilator_coverage -write merged.dat -read <datafiles>...
|
||||
verilator_coverage -write merged.dat <datafiles>...
|
||||
|
||||
verilator_coverage -write-info merged.info -read <datafiles>...
|
||||
verilator_coverage -write-info merged.info <datafiles>...
|
||||
|
||||
Verilator_coverage processes Verilated model-generated coverage reports.
|
||||
|
||||
|
||||
+4
-4
@@ -92,8 +92,8 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
||||
sudo apt-get update ||
|
||||
sudo apt-get update
|
||||
# libfl-dev needed for internal coverage's test runs
|
||||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq ||
|
||||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq
|
||||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq z3 ||
|
||||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq z3
|
||||
# Required for test_regress/t/t_dist_attributes.pl
|
||||
if [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
|
||||
sudo apt-get install python3-clang mold ||
|
||||
@@ -106,10 +106,10 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
||||
elif [ "$CI_OS_NAME" = "osx" ]; then
|
||||
brew update
|
||||
# brew cask install gtkwave # fst2vcd hangs at launch, so don't bother
|
||||
brew install ccache perl jq
|
||||
brew install ccache perl jq z3
|
||||
elif [ "$CI_OS_NAME" = "freebsd" ]; then
|
||||
# fst2vcd fails with "Could not open '<input file>', exiting."
|
||||
sudo pkg install -y ccache gmake perl5 python3 jq
|
||||
sudo pkg install -y ccache gmake perl5 python3 jq z3
|
||||
else
|
||||
fatal "Unknown os: '$CI_OS_NAME'"
|
||||
fi
|
||||
|
||||
+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
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
# DESCRIPTION: Verilator: CI Windows Power Shell - Compile Verilator
|
||||
#
|
||||
# Copyright 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
|
||||
################################################################################
|
||||
|
||||
Set-PSDebug -Trace 1
|
||||
|
||||
if (-Not (Test-Path $PWD/../.ccache/win_bison.exe)) {
|
||||
git clone --depth 1 https://github.com/lexxmark/winflexbison
|
||||
cd winflexbison
|
||||
@@ -8,6 +20,7 @@ if (-Not (Test-Path $PWD/../.ccache/win_bison.exe)) {
|
||||
cmake --install . --prefix $PWD/../../../.ccache
|
||||
cd ../..
|
||||
}
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. --install-prefix $PWD/../install
|
||||
|
||||
+17
-1
@@ -1,3 +1,16 @@
|
||||
# DESCRIPTION: Verilator: CI Windows Power Shell - Verilate a test
|
||||
#
|
||||
# Copyright 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
|
||||
################################################################################
|
||||
|
||||
|
||||
Set-PSDebug -Trace 1
|
||||
|
||||
cd install
|
||||
$Env:VERILATOR_ROOT=$PWD
|
||||
cd examples/cmake_tracing_c
|
||||
@@ -5,6 +18,9 @@ mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
cmake --build . --config Release -j 3
|
||||
Release/example.exe
|
||||
|
||||
# TODO put this back in, see issue# 5163
|
||||
# Release/example.exe
|
||||
|
||||
cd ..
|
||||
Remove-Item -path build -recurse
|
||||
|
||||
Executable
BIN
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -43,6 +43,7 @@ RUN apt-get update \
|
||||
perl \
|
||||
python3 \
|
||||
wget \
|
||||
z3 \
|
||||
zlib1g \
|
||||
zlib1g-dev \
|
||||
&& apt-get clean \
|
||||
|
||||
@@ -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.:
|
||||
|
||||
::
|
||||
|
||||
+66
-27
@@ -5,12 +5,12 @@
|
||||
# General Public License Version 3 or the Perl Artistic License Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
# When releasing, also update header of Changes file, and CmakeLists.txt,
|
||||
# When releasing, also update header of Changes file, and CMakeLists.txt,
|
||||
# and commit using "devel release" or "Version bump" message
|
||||
# Then 'make maintainer-dist'
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[5.024 2024-04-05],
|
||||
AC_INIT([Verilator],[5.028 2024-08-21],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
|
||||
@@ -134,6 +134,28 @@ AC_ARG_ENABLE([longtests],
|
||||
AC_SUBST(CFG_WITH_LONGTESTS)
|
||||
AC_MSG_RESULT($CFG_WITH_LONGTESTS)
|
||||
|
||||
AC_CHECK_PROG(HAVE_Z3,z3,yes)
|
||||
AC_CHECK_PROG(HAVE_CVC5,cvc5,yes)
|
||||
AC_CHECK_PROG(HAVE_CVC4,cvc4,yes)
|
||||
|
||||
# Special Substitutions - CFG_WITH_SOLVER
|
||||
AC_MSG_CHECKING(for SMT solver)
|
||||
AC_ARG_WITH([solver],
|
||||
[AS_HELP_STRING([--with-solver='z3 --in'],
|
||||
[set default SMT solver for constrained randomization])],
|
||||
[CFG_WITH_SOLVER="${withval}"],
|
||||
[CFG_WITH_SOLVER=no
|
||||
if test "x$HAVE_Z3" = "xyes"; then
|
||||
CFG_WITH_SOLVER="z3 --in"
|
||||
elif test "x$HAVE_CVC5" = "xyes"; then
|
||||
CFG_WITH_SOLVER="cvc5 --incremental"
|
||||
elif test "x$HAVE_CVC4" = "xyes"; then
|
||||
CFG_WITH_SOLVER="cvc4 --lang=smt2 --incremental"
|
||||
fi]
|
||||
)
|
||||
AC_SUBST(CFG_WITH_SOLVER)
|
||||
AC_MSG_RESULT($CFG_WITH_SOLVER)
|
||||
|
||||
# Compiler flags (ensure they are not empty to avoid configure defaults)
|
||||
CFLAGS="$CFLAGS "
|
||||
CPPFLAGS="$CPPFLAGS "
|
||||
@@ -266,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
|
||||
@@ -351,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
|
||||
|
||||
@@ -9,20 +9,25 @@ Adrien Le Masle
|
||||
Ahmed El-Mahmoudy
|
||||
Aleksander Kiryk
|
||||
Alex Chadwick
|
||||
Alex Solomatnikov
|
||||
Àlex Torregrosa
|
||||
Aliaksei Chapyzhenka
|
||||
Ameya Vikram Singh
|
||||
Andrea Calabrese
|
||||
Andreas Kuster
|
||||
Andrei Kostovski
|
||||
Andrew Miloradovsky
|
||||
Andrew Nolte
|
||||
Anthony Donlon
|
||||
Arkadiusz Kozdra
|
||||
Arthur Rosa
|
||||
Aylon Chaim Porat
|
||||
Bartłomiej Chmiel
|
||||
Cameron Kirk
|
||||
Chih-Mao Chen
|
||||
Chris Bachhuber
|
||||
Chris Randall
|
||||
Christopher Taylor
|
||||
Chuxuan Wang
|
||||
Chykon
|
||||
Conor McCullough
|
||||
@@ -33,6 +38,7 @@ David Ledger
|
||||
David Metz
|
||||
David Stanford
|
||||
David Turner
|
||||
Dercury
|
||||
Don Williamson
|
||||
Drew Ranck
|
||||
Drew Taussig
|
||||
@@ -51,6 +57,7 @@ G-A. Kamendje
|
||||
Garrett Smith
|
||||
Geza Lore
|
||||
Gianfranco Costamagna
|
||||
Gijs Burghoorn
|
||||
Glen Gibb
|
||||
Gökçe Aydos
|
||||
Graham Rushton
|
||||
@@ -70,6 +77,7 @@ Iru Cai
|
||||
Ivan Vnučec
|
||||
Iztok Jeras
|
||||
Jake Merdich
|
||||
James Bailey
|
||||
James Hanlon
|
||||
James Hutchinson
|
||||
James Pallister
|
||||
@@ -112,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
|
||||
@@ -127,6 +138,7 @@ Marshal Qiao
|
||||
Martin Schmidt
|
||||
Martin Stadler
|
||||
Matthew Ballance
|
||||
Michael Bikovitsky
|
||||
Michael Killough
|
||||
Michaël Lefebvre
|
||||
Michal Czyz
|
||||
@@ -141,13 +153,16 @@ Nathan Myers
|
||||
Nolan Poe
|
||||
Oleh Maksymenko
|
||||
Patrick Stewart
|
||||
Paul Bowen-Huggett
|
||||
Paul Swirhun
|
||||
Paul Wright
|
||||
Pawel Jewstafjew
|
||||
Pawel Sagan
|
||||
Pengcheng Xu
|
||||
Peter Debacker
|
||||
Peter Horvath
|
||||
Peter Monsson
|
||||
Philip Axer
|
||||
Philipp Wagner
|
||||
Pierre-Henri Horrein
|
||||
Pieter Kapsenberg
|
||||
@@ -157,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
|
||||
@@ -185,6 +203,8 @@ Tudor Timi
|
||||
Tymoteusz Blazejczyk
|
||||
Udi Finkelstein
|
||||
Unai Martinez-Corral
|
||||
Udaya Raj Subedi
|
||||
Valentin Atepalikhin
|
||||
Varun Koyyalagunta
|
||||
Vassilis Papaefstathiou
|
||||
Veripool API Bot
|
||||
@@ -194,6 +214,7 @@ William D. Jones
|
||||
Wilson Snyder
|
||||
Xi Zhang
|
||||
Yan Xu
|
||||
Yangyu Chen
|
||||
Yinan Xu
|
||||
Yoda Lee
|
||||
Yossi Nivin
|
||||
@@ -205,3 +226,4 @@ Zhanglei Wang
|
||||
Zhou Shen
|
||||
Zixi Li
|
||||
أحمد المحمودي
|
||||
404allen404
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.. comment: generated by t_lint_multidriven_bad
|
||||
.. code-block::
|
||||
|
||||
%Warning-MULTIDRIVEN: example.v:1:22 Signal has multiple driving blocks with different clocking: 'out2'
|
||||
%Warning-MULTIDRIVEN: example.v:1:22 Signal has multiple driving blocks with different clocking: 't.mem'
|
||||
example.v:1:7 ... Location of first driving block
|
||||
example.v:1:7 ... Location of other driving block
|
||||
|
||||
@@ -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)
|
||||
@@ -407,6 +407,16 @@ only a couple of instructions.
|
||||
For signal callbacks to work the main loop of the program must call
|
||||
:code:`VerilatedVpi::callValueCbs()`.
|
||||
|
||||
Verilator also tracks when the model state has been modified via the VPI with
|
||||
an :code:`evalNeeded` flag. This flag can be checked with :code:`VerilatedVpi::evalNeeded()`
|
||||
and it can be cleared with :code:`VerilatedVpi::clearEvalNeeded()`. Used together
|
||||
it is possible to skip :code:`eval()` calls if no model state has been changed
|
||||
since the last :code:`eval()`.
|
||||
|
||||
Any data written via :code:`vpi_put_value` with :code:`vpiInertialDelay` will
|
||||
be deferred for later. These delayed values can be flushed to the model with
|
||||
:code:`VerilatedVpi::doInertialPuts()`.
|
||||
|
||||
|
||||
.. _VPI Example:
|
||||
|
||||
|
||||
@@ -118,6 +118,12 @@ associated programs.
|
||||
|
||||
See :ref:`Installation` for more details.
|
||||
|
||||
.. option:: VERILATOR_SOLVER
|
||||
|
||||
If set, the command to run as a constrained randomization backend, such
|
||||
as :command:`cvc4 --lang=smt2 --incremental`. If not specified, it will use
|
||||
the one supplied or found during configure, or :command:`z3 --in` if empty.
|
||||
|
||||
.. option:: VERILATOR_VALGRIND
|
||||
|
||||
If set, the command to run when using the :vlopt:`--valgrind` option, such as
|
||||
|
||||
@@ -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`.
|
||||
@@ -817,6 +837,11 @@ Summary:
|
||||
If the design is not to be completely Verilated, see also the
|
||||
:vlopt:`--bbox-sys` and :vlopt:`--bbox-unsup` options.
|
||||
|
||||
.. option:: --localize-max-size <value>
|
||||
|
||||
Rarely needed. Set the maximum variable size in bytes for it to be
|
||||
subject to localizing-to-stack optimization. Defaults to 1024.
|
||||
|
||||
.. option:: --make <build-tool>
|
||||
|
||||
Generates a script for the specified build tool.
|
||||
@@ -985,6 +1010,13 @@ Summary:
|
||||
:option:`/*verilator&32;sc_bv*/` metacomment to select specific ports to
|
||||
be sc_bv.
|
||||
|
||||
.. option:: --pins-inout-enables
|
||||
|
||||
Specifies that the __en and __out outputs will always be created for
|
||||
inouts in the top-level module. The __en variable has a one in a bit
|
||||
position to indicate the corresponding bit of the __out variable has
|
||||
a value being driven from within the Verilated model.
|
||||
|
||||
.. option:: --pins-sc-uint
|
||||
|
||||
Specifies SystemC inputs/outputs greater than 2 bits wide should use
|
||||
@@ -992,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
|
||||
@@ -1732,9 +1768,9 @@ Summary:
|
||||
|
||||
.. note::
|
||||
|
||||
This option applies only to values explicitly written as X
|
||||
in modules (not classes) in the Verilog source code. Initial values
|
||||
of clocks are set to 0 unless `--x-initial-edge` is
|
||||
This option applies only to values explicitly written as X in modules
|
||||
(not classes, nor parameters) in the Verilog source code. Initial
|
||||
values of clocks are set to 0 unless `--x-initial-edge` is
|
||||
specified. Initial values of all other state holding variables are
|
||||
controlled with `--x-initial`.
|
||||
|
||||
@@ -1967,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
|
||||
|
||||
@@ -37,9 +37,9 @@ verilator_coverage Example Usage
|
||||
|
||||
verilator_coverage --annotate <obj>
|
||||
|
||||
verilator_coverage -write merged.dat -read <datafiles>...
|
||||
verilator_coverage -write merged.dat <datafiles>...
|
||||
|
||||
verilator_coverage -write-info merged.info -read <datafiles>...
|
||||
verilator_coverage -write-info merged.info <datafiles>...
|
||||
|
||||
|
||||
verilator_coverage Arguments
|
||||
|
||||
+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:
|
||||
|
||||
|
||||
+14
-2
@@ -152,6 +152,17 @@ To make use of Verilator FST tracing you will want `GTKwave
|
||||
required at Verilator build time.
|
||||
|
||||
|
||||
Install Z3
|
||||
^^^^^^^^^^
|
||||
|
||||
In order to use constrained randomization the `Z3 Theorem Prover
|
||||
<https://github.com/z3prover/z3#readme>`__ must be installed, however this is
|
||||
not required at Verilator build time. There are other compatible SMT solvers,
|
||||
like CVC5/CVC4, but they are not guaranteed to work. Since different solvers are
|
||||
faster for different scenarios, the solver to use at run-time can be specified
|
||||
by the environment variable :option:`VERILATOR_SOLVER`.
|
||||
|
||||
|
||||
.. _Obtain Sources:
|
||||
|
||||
Obtain Sources
|
||||
@@ -193,8 +204,9 @@ Eventual Installation Options
|
||||
Before configuring the build, you must decide how you're going to
|
||||
eventually install Verilator onto your system. Verilator will be compiling
|
||||
the current value of the environment variables :option:`VERILATOR_ROOT`,
|
||||
:option:`SYSTEMC_INCLUDE`, and :option:`SYSTEMC_LIBDIR` as defaults into
|
||||
the executable, so they must be correct before configuring.
|
||||
:option:`VERILATOR_SOLVER`, :option:`SYSTEMC_INCLUDE`, and
|
||||
:option:`SYSTEMC_LIBDIR` as defaults into the executable, so they must be
|
||||
correct before configuring.
|
||||
|
||||
These are the installation options:
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
+88
-32
@@ -210,22 +210,35 @@ List Of Warnings
|
||||
|
||||
.. option:: BLKLOOPINIT
|
||||
|
||||
.. TODO better example
|
||||
|
||||
This indicates that the initialization of an array needs to use
|
||||
non-delayed assignments. This is done in the interest of speed; if
|
||||
delayed assignments were used, the simulator would have to copy large
|
||||
arrays every cycle. (In smaller loops, loop unrolling allows the
|
||||
delayed assignment to work, though it's a bit slower than a non-delayed
|
||||
assignment.) Here's an example
|
||||
Indicates certain constructs where non-blocking assignments to unpacked
|
||||
arrays (memories) are not supported inside loops. These typically appear in
|
||||
initialization/reset code:
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
always @(posedge clk)
|
||||
if (~reset_l)
|
||||
for (i=0; i<`ARRAY_SIZE; i++)
|
||||
array[i] = 0; // Non-delayed for verilator
|
||||
array[i] <= 0; // Non-blocking assignment inside loop
|
||||
else
|
||||
array[address] <= data;
|
||||
|
||||
While this is supported in typical synthesizeable code (including the
|
||||
example above), some complicated cases are not supported. Namely:
|
||||
|
||||
1. If the above loop is inside a suspendable process or fork statement.
|
||||
|
||||
2. If the variable is also the target of a '<=' non-blocking assignment
|
||||
in a suspendable process or fork statement (in addition to a synthesizable
|
||||
loop).
|
||||
|
||||
3. If the element type of the array is a compound type.
|
||||
|
||||
4. In versions before 5.026, any delayed assignment to an array.
|
||||
|
||||
It might slightly improve run-time performance if you change the
|
||||
non-blocking assignment inside the loop into a blocking assignment
|
||||
(that is: use '=' instead of '<='), if possible.
|
||||
|
||||
This message is only seen on large or complicated loops because
|
||||
Verilator generally unrolls small loops. You may want to try increasing
|
||||
@@ -406,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.
|
||||
@@ -1204,6 +1217,14 @@ List Of Warnings
|
||||
:vlopt:`--no-timing` option.
|
||||
|
||||
|
||||
.. option:: NONSTD
|
||||
|
||||
Warns when a non-standard language feature is used that has a standard
|
||||
equivalent, which might behave differently in corner cases. For example
|
||||
:code:`$psprintf` system function is replaced by its standard equivalent
|
||||
:code:`$sformatf`.
|
||||
|
||||
|
||||
.. option:: NULLPORT
|
||||
|
||||
Warns that a null port was detected in the module definition port
|
||||
@@ -1243,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
|
||||
@@ -1252,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".
|
||||
@@ -1283,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
|
||||
@@ -1325,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
|
||||
@@ -1354,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
|
||||
@@ -1876,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
|
||||
@@ -1927,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
|
||||
@@ -2057,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.
|
||||
|
||||
@@ -2068,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
|
||||
|
||||
@@ -2104,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
|
||||
|
||||
@@ -2115,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
|
||||
|
||||
@@ -2172,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
|
||||
|
||||
@@ -2193,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}};
|
||||
|
||||
@@ -1031,6 +1031,91 @@ the evaluation process records a bitmask of variables that might have
|
||||
changed; if clear, checking those signals for changes may be skipped.
|
||||
|
||||
|
||||
Constrained randomization
|
||||
-------------------------
|
||||
|
||||
Because general constrained randomization is a co-NP-hard problem, not all
|
||||
cases are implemented in Verilator, and an external specialized SMT solver is
|
||||
used for any non-obvious ones.
|
||||
|
||||
The ``randomize()`` method spawns an SMT solver in a sub-process. Then the
|
||||
solver gets a setup query, then the definition of variables, then all the
|
||||
constraints (SMT assertions) about the variables. Since the solver has no
|
||||
information about the class' PRNG state, if the problem is satisfiable,
|
||||
the solution space is further constrained by adding extra random constraints,
|
||||
and querying the values satisfying the problem statement.
|
||||
The constraint is currently constructed as fixing a simple xor of randomly
|
||||
chosen bits of the variables being randomized.
|
||||
|
||||
The runtime classes used for handling the randomization are defined in
|
||||
``verilated_random.h`` and ``verilated_random.cpp``.
|
||||
|
||||
|
||||
``VlSubprocess``
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Subprocess handle, responsible for keeping track of the resources like child
|
||||
PID, read and write file descriptors, and presenting them as a C++ iostream.
|
||||
|
||||
|
||||
``VlRandomizer``
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Randomizer class, responsible for keeping track of variables and constraints,
|
||||
and communicating with the solver subprocess.
|
||||
|
||||
The solver gets the constraints in `SMT-LIB2
|
||||
<https://smtlib.cs.uiowa.edu/>`__ textual format in the following syntax:
|
||||
|
||||
::
|
||||
|
||||
(set-info :smt-lib-version 2.0)
|
||||
(set-option :produce-models true)
|
||||
(set-logic QF_BV)
|
||||
|
||||
(declare-fun v () (_ BitVec 16))
|
||||
(declare-fun w () (_ BitVec 64))
|
||||
(declare-fun x () (_ BitVec 48))
|
||||
(declare-fun z () (_ BitVec 24))
|
||||
(declare-fun t () (_ BitVec 23))
|
||||
(assert (or (= v #x0003) (= v #x0008)))
|
||||
(assert (= w #x0000000000000009))
|
||||
(assert (or (or (= x #x000000000001) (= x #x000000000002)) (or (= x #x000000000004) (= x #x000000000009))))
|
||||
(assert (bvult ((_ zero_extend 8) z) #x00000015))
|
||||
(assert (bvugt ((_ zero_extend 8) z) #x0000000d))
|
||||
|
||||
(check-sat)
|
||||
|
||||
The solver responds with either ``sat`` or ``unsat``. Then the initial solution
|
||||
is queried with:
|
||||
|
||||
::
|
||||
|
||||
(get-value (v w x z t ))
|
||||
|
||||
The solver then responds with e.g.:
|
||||
|
||||
::
|
||||
|
||||
((v #x0008)
|
||||
(w #x0000000000000005)
|
||||
(x #x000000000002)
|
||||
(z #x000010)
|
||||
(t #b00000000000000000000000))
|
||||
|
||||
And then a follow-up query (or a series thereof) is asked, and the solver gets
|
||||
reset, so that it can be reused by subsequent randomization attempts:
|
||||
|
||||
::
|
||||
|
||||
(assert (= (bvxor (bvxor <...> (bvxor ((_ extract 21 21) z) ((_ extract 39 39) x)) ((_ extract 5 5) w)) <...> ((_ extract 10 10) w)) #b0))
|
||||
(check-sat)
|
||||
(get-value)
|
||||
...
|
||||
(reset)
|
||||
|
||||
|
||||
|
||||
Coding Conventions
|
||||
==================
|
||||
|
||||
@@ -2125,6 +2210,10 @@ VERILATOR_ROOT
|
||||
Standard path to Verilator distribution root; see primary Verilator
|
||||
documentation.
|
||||
|
||||
VERILATOR_SOLVER
|
||||
SMT solver command for constrained randomization; see primary Verilator
|
||||
documentation.
|
||||
|
||||
VERILATOR_TESTS_SITE
|
||||
Used with ``--site``, a colon-separated list of directories with tests to
|
||||
be added to testlist.
|
||||
|
||||
@@ -40,6 +40,7 @@ Brej
|
||||
Briquet
|
||||
Brownlee
|
||||
Buckenmaier
|
||||
Burghoorn
|
||||
Bybell
|
||||
CLang
|
||||
CMake
|
||||
@@ -131,6 +132,7 @@ Gernot
|
||||
Gerst
|
||||
Gielda
|
||||
Gigerl
|
||||
Gijs
|
||||
Gileadi
|
||||
Giri
|
||||
Gisselquist
|
||||
@@ -164,6 +166,7 @@ Homebrew
|
||||
Hornung
|
||||
Hossell
|
||||
Hsu
|
||||
Huggett
|
||||
Hutt
|
||||
Hyperthreading
|
||||
Ibrahim
|
||||
@@ -376,6 +379,7 @@ Srini
|
||||
Srinivasan
|
||||
Stamness
|
||||
Stephane
|
||||
Sterin
|
||||
Stoddard
|
||||
Stroebel
|
||||
Strouble
|
||||
@@ -458,6 +462,7 @@ Wojciech
|
||||
Wolfel
|
||||
Wouters
|
||||
Wpedantic
|
||||
Wunused
|
||||
Wwarn
|
||||
XSim
|
||||
Xcelium
|
||||
@@ -494,6 +499,7 @@ architected
|
||||
args
|
||||
arrarys
|
||||
assertOn
|
||||
assertcontrol
|
||||
astgen
|
||||
async
|
||||
atClone
|
||||
@@ -586,6 +592,7 @@ defname
|
||||
defparam
|
||||
demangling
|
||||
dep
|
||||
deparametrized
|
||||
der
|
||||
dereference
|
||||
desassign
|
||||
@@ -698,6 +705,7 @@ genvar
|
||||
genvars
|
||||
getenv
|
||||
getline
|
||||
getter
|
||||
ggdb
|
||||
glibc
|
||||
gmake
|
||||
@@ -711,6 +719,7 @@ hh
|
||||
hier
|
||||
hierCMakeArgs
|
||||
hierMkArgs
|
||||
hierParameters
|
||||
hierVer
|
||||
hx
|
||||
hyperthreading
|
||||
@@ -842,6 +851,8 @@ parallelized
|
||||
param
|
||||
parameterization
|
||||
parameterized
|
||||
parameterized
|
||||
parameterless
|
||||
params
|
||||
parens
|
||||
pawel
|
||||
@@ -885,7 +896,9 @@ profcfunc
|
||||
profiler
|
||||
prototyptes
|
||||
ps
|
||||
psprintf
|
||||
pthread
|
||||
pthreads
|
||||
ptr
|
||||
pulldown
|
||||
pulldowns
|
||||
@@ -968,6 +981,9 @@ submakes
|
||||
submodule
|
||||
submodules
|
||||
substring
|
||||
superclass
|
||||
suspendable
|
||||
suspendables
|
||||
sv
|
||||
svBitVal
|
||||
svBitVecVal
|
||||
@@ -978,6 +994,7 @@ svLogicVal
|
||||
svdpi
|
||||
svgGetTimeUnit
|
||||
swrite
|
||||
synthesizeable
|
||||
sys
|
||||
systemc
|
||||
tenghtt
|
||||
@@ -996,6 +1013,7 @@ toolchain
|
||||
topcell
|
||||
toplevel
|
||||
toupper
|
||||
traceCapable
|
||||
traceEverOn
|
||||
tran
|
||||
treei
|
||||
@@ -1060,6 +1078,7 @@ vluint
|
||||
vpi
|
||||
vpiConstType
|
||||
vpiDefName
|
||||
vpiInertialDelay
|
||||
vpiInstance
|
||||
vpiLeftRange
|
||||
vpiModule
|
||||
@@ -1093,3 +1112,4 @@ zdave
|
||||
Øyvind
|
||||
Алексеевич
|
||||
Исаак
|
||||
|
||||
|
||||
@@ -47,12 +47,12 @@ int main(int argc, char** argv) {
|
||||
|
||||
// Simulate until $finish
|
||||
while (!contextp->gotFinish()) {
|
||||
contextp->timeInc(1);
|
||||
top->clk = ~top->clk & 0x1;
|
||||
top->eval();
|
||||
#if VM_TRACE
|
||||
if (tfp) tfp->dump(contextp->time());
|
||||
#endif
|
||||
contextp->timeInc(1);
|
||||
}
|
||||
|
||||
// Final model cleanup
|
||||
|
||||
@@ -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
|
||||
@@ -347,8 +348,9 @@ return(NULL);
|
||||
|
||||
static void *fstMmap2(size_t __len, int __fd, fst_off_t __off)
|
||||
{
|
||||
DWORD64 len64 = __len; /* Must be 64-bit for shift below */
|
||||
HANDLE handle = CreateFileMapping((HANDLE)_get_osfhandle(__fd), NULL,
|
||||
PAGE_READWRITE, (DWORD)(__len >> 32),
|
||||
PAGE_READWRITE, (DWORD)(len64 >> 32),
|
||||
(DWORD)__len, NULL);
|
||||
if (!handle) { return NULL; }
|
||||
|
||||
@@ -4632,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));
|
||||
@@ -6184,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;
|
||||
@@ -6276,7 +6285,9 @@ for(;;)
|
||||
}
|
||||
|
||||
blkpos += seclen;
|
||||
#ifdef FST_DEBUG
|
||||
secnum++;
|
||||
#endif
|
||||
}
|
||||
|
||||
xc->rvat_beg_tim = beg_tim;
|
||||
|
||||
+137
-54
@@ -82,6 +82,12 @@
|
||||
|
||||
#include "verilated_trace.h"
|
||||
|
||||
#ifdef VM_SOLVER_DEFAULT
|
||||
#define VL_SOLVER_DEFAULT VM_SOLVER_DEFAULT
|
||||
#else
|
||||
#define VL_SOLVER_DEFAULT "z3 --in"
|
||||
#endif
|
||||
|
||||
// Max characters in static char string for VL_VALUE_STRING
|
||||
constexpr unsigned VL_VALUE_STRING_MAX_WIDTH = 8192;
|
||||
|
||||
@@ -406,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;
|
||||
@@ -415,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);
|
||||
@@ -1171,8 +1187,8 @@ static char* _vl_vsss_read_bin(FILE* fp, int& floc, const WDataInP fromp, const
|
||||
static void _vl_vsss_setbit(WDataOutP iowp, int obits, int lsb, int nbits, IData ld) VL_MT_SAFE {
|
||||
for (; nbits && lsb < obits; nbits--, lsb++, ld >>= 1) VL_ASSIGNBIT_WI(lsb, iowp, ld & 1);
|
||||
}
|
||||
static void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp,
|
||||
size_t posstart, size_t posend) VL_MT_SAFE {
|
||||
void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp, size_t posstart,
|
||||
size_t posend) VL_MT_SAFE {
|
||||
// Read in base "2^^baseLog2" digits from strp[posstart..posend-1] into owp of size obits.
|
||||
VL_ZERO_W(obits, owp);
|
||||
int lsb = 0;
|
||||
@@ -1749,7 +1765,10 @@ IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE {
|
||||
IData VL_SYSTEM_IW(int lhswords, const WDataInP lhsp) VL_MT_SAFE {
|
||||
char filenamez[VL_VALUE_STRING_MAX_CHARS + 1];
|
||||
_vl_vint_to_string(lhswords * VL_EDATASIZE, filenamez, lhsp);
|
||||
const int code = std::system(filenamez); // Yes, std::system() is threadsafe
|
||||
return VL_SYSTEM_IN(filenamez);
|
||||
}
|
||||
IData VL_SYSTEM_IN(const std::string& lhs) VL_MT_SAFE {
|
||||
const int code = std::system(lhs.c_str()); // Yes, std::system() is threadsafe
|
||||
return code >> 8; // Want exit status
|
||||
}
|
||||
|
||||
@@ -2037,22 +2056,26 @@ bool VlReadMem::get(QData& addrr, std::string& valuer) {
|
||||
if (VL_UNLIKELY(!m_fp)) return false;
|
||||
valuer = "";
|
||||
// Prep for reading
|
||||
bool indata = false;
|
||||
bool ignore_to_eol = false;
|
||||
bool ignore_to_cmt = false;
|
||||
bool reading_addr = false;
|
||||
int lastc = ' ';
|
||||
bool inData = false;
|
||||
bool ignoreToEol = false;
|
||||
bool ignoreToComment = false;
|
||||
bool readingAddress = false;
|
||||
int lastCh = ' ';
|
||||
// Read the data
|
||||
// We process a character at a time, as then we don't need to deal
|
||||
// with changing buffer sizes dynamically, etc.
|
||||
while (true) {
|
||||
int c = std::fgetc(m_fp);
|
||||
if (VL_UNLIKELY(c == EOF)) break;
|
||||
const bool chIs4StateBin
|
||||
= c == '0' || c == '1' || c == 'x' || c == 'X' || c == 'z' || c == 'Z';
|
||||
const bool chIs2StateHex = std::isxdigit(c);
|
||||
const bool chIs4StateHex = std::isxdigit(c) || chIs4StateBin;
|
||||
// printf("%d: Got '%c' Addr%lx IN%d IgE%d IgC%d\n",
|
||||
// m_linenum, c, m_addr, indata, ignore_to_eol, ignore_to_cmt);
|
||||
// m_linenum, c, m_addr, inData, ignoreToEol, ignoreToComment);
|
||||
// See if previous data value has completed, and if so return
|
||||
if (c == '_') continue; // Ignore _ e.g. inside a number
|
||||
if (indata && !std::isxdigit(c) && c != 'x' && c != 'X') {
|
||||
if (inData && !chIs4StateHex) {
|
||||
// printf("Got data @%lx = %s\n", m_addr, valuer.c_str());
|
||||
ungetc(c, m_fp);
|
||||
addrr = m_addr;
|
||||
@@ -2062,50 +2085,46 @@ bool VlReadMem::get(QData& addrr, std::string& valuer) {
|
||||
// Parse line
|
||||
if (c == '\n') {
|
||||
++m_linenum;
|
||||
ignore_to_eol = false;
|
||||
reading_addr = false;
|
||||
ignoreToEol = false;
|
||||
readingAddress = false;
|
||||
} else if (c == '\t' || c == ' ' || c == '\r' || c == '\f') {
|
||||
reading_addr = false;
|
||||
readingAddress = false;
|
||||
}
|
||||
// Skip // comments and detect /* comments
|
||||
else if (ignore_to_cmt && lastc == '*' && c == '/') {
|
||||
ignore_to_cmt = false;
|
||||
reading_addr = false;
|
||||
} else if (!ignore_to_eol && !ignore_to_cmt) {
|
||||
if (lastc == '/' && c == '*') {
|
||||
ignore_to_cmt = true;
|
||||
} else if (lastc == '/' && c == '/') {
|
||||
ignore_to_eol = true;
|
||||
else if (ignoreToComment && lastCh == '*' && c == '/') {
|
||||
ignoreToComment = false;
|
||||
readingAddress = false;
|
||||
} else if (!ignoreToEol && !ignoreToComment) {
|
||||
if (lastCh == '/' && c == '*') {
|
||||
ignoreToComment = true;
|
||||
} else if (lastCh == '/' && c == '/') {
|
||||
ignoreToEol = true;
|
||||
} else if (c == '/') { // Part of /* or //
|
||||
} else if (c == '#') {
|
||||
ignore_to_eol = true;
|
||||
ignoreToEol = true;
|
||||
} else if (c == '@') {
|
||||
reading_addr = true;
|
||||
readingAddress = true;
|
||||
m_anyAddr = true;
|
||||
m_addr = 0;
|
||||
}
|
||||
// Check for hex or binary digits as file format requests
|
||||
else if (std::isxdigit(c) || (!reading_addr && (c == 'x' || c == 'X'))) {
|
||||
} else if (readingAddress && chIs2StateHex) {
|
||||
c = std::tolower(c);
|
||||
const int value
|
||||
= (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c - 'a' + 10)) : (c - '0'));
|
||||
if (reading_addr) {
|
||||
// Decode @ addresses
|
||||
m_addr = (m_addr << 4) + value;
|
||||
} else {
|
||||
indata = true;
|
||||
valuer += static_cast<char>(c);
|
||||
// printf(" Value width=%d @%x = %c\n", width, m_addr, c);
|
||||
if (VL_UNLIKELY(value > 1 && !m_hex)) {
|
||||
VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
|
||||
"$readmemb (binary) file contains hex characters");
|
||||
}
|
||||
const int addressValue = (c >= 'a') ? (c - 'a' + 10) : (c - '0');
|
||||
m_addr = (m_addr << 4) + addressValue;
|
||||
} else if (readingAddress && chIs4StateHex) {
|
||||
VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
|
||||
"$readmem address contains 4-state characters");
|
||||
} else if (chIs4StateHex) {
|
||||
inData = true;
|
||||
valuer += static_cast<char>(c);
|
||||
if (VL_UNLIKELY(!m_hex && !chIs4StateBin)) {
|
||||
VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
|
||||
"$readmemb (binary) file contains hex characters");
|
||||
}
|
||||
} else {
|
||||
VL_FATAL_MT(m_filename.c_str(), m_linenum, "", "$readmem file syntax error");
|
||||
}
|
||||
}
|
||||
lastc = c;
|
||||
lastCh = c;
|
||||
}
|
||||
|
||||
if (VL_UNLIKELY(m_end != ~0ULL && m_addr <= m_end && !m_anyAddr)) {
|
||||
@@ -2114,7 +2133,7 @@ bool VlReadMem::get(QData& addrr, std::string& valuer) {
|
||||
}
|
||||
|
||||
addrr = m_addr;
|
||||
return indata; // EOF
|
||||
return inData; // EOF
|
||||
}
|
||||
void VlReadMem::setData(void* valuep, const std::string& rhs) {
|
||||
const QData shift = m_hex ? 4ULL : 1ULL;
|
||||
@@ -2122,8 +2141,9 @@ void VlReadMem::setData(void* valuep, const std::string& rhs) {
|
||||
// Shift value in
|
||||
for (const auto& i : rhs) {
|
||||
const char c = std::tolower(i);
|
||||
const int value
|
||||
= (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c - 'a' + 10)) : (c - '0'));
|
||||
const int value = (c == 'x' || c == 'z') ? VL_RAND_RESET_I(m_hex ? 4 : 1)
|
||||
: (c >= 'a') ? (c - 'a' + 10)
|
||||
: (c - '0');
|
||||
if (m_bits <= 8) {
|
||||
CData* const datap = reinterpret_cast<CData*>(valuep);
|
||||
if (!innum) *datap = 0;
|
||||
@@ -2457,6 +2477,7 @@ VerilatedContext::VerilatedContext()
|
||||
m_ns.m_coverageFilename = "coverage.dat";
|
||||
m_ns.m_profExecFilename = "profile_exec.dat";
|
||||
m_ns.m_profVltFilename = "profile.vlt";
|
||||
m_ns.m_solverProgram = VlOs::getenvStr("VERILATOR_SOLVER", VL_SOLVER_DEFAULT);
|
||||
m_fdps.resize(31);
|
||||
std::fill(m_fdps.begin(), m_fdps.end(), static_cast<FILE*>(nullptr));
|
||||
m_fdFreeMct.resize(30);
|
||||
@@ -2483,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};
|
||||
@@ -2567,6 +2626,14 @@ std::string VerilatedContext::profVltFilename() const VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
return m_ns.m_profVltFilename;
|
||||
}
|
||||
void VerilatedContext::solverProgram(const std::string& flag) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_ns.m_solverProgram = flag;
|
||||
}
|
||||
std::string VerilatedContext::solverProgram() const VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
return m_ns.m_solverProgram;
|
||||
}
|
||||
void VerilatedContext::quiet(bool flag) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_quiet = flag;
|
||||
@@ -2645,8 +2712,16 @@ void VerilatedContext::addModel(VerilatedModel* modelp) {
|
||||
m_ns.m_cpuTimeStart.start();
|
||||
m_ns.m_wallTimeStart.start();
|
||||
}
|
||||
threadPoolp(); // Ensure thread pool is created, so m_threads cannot change any more
|
||||
|
||||
// We look for time passing, as opposed to post-eval(), as embedded
|
||||
// models might get added inside initial blocks.
|
||||
if (VL_UNLIKELY(time()))
|
||||
VL_FATAL_MT(
|
||||
"", 0, "",
|
||||
"Adding model when time is non-zero. ... Suggest check time(), or for restarting"
|
||||
" model use a new VerilatedContext");
|
||||
|
||||
threadPoolp(); // Ensure thread pool is created, so m_threads cannot change any more
|
||||
m_threadsInModels += modelp->threads();
|
||||
if (VL_UNLIKELY(modelp->threads() > m_threads)) {
|
||||
std::ostringstream msg;
|
||||
@@ -2882,7 +2957,7 @@ void VerilatedContext::statsPrintSummary() VL_MT_UNSAFE {
|
||||
VL_PRINTF("- Verilator: %s at %s; walltime %0.3f s; speed %s/s\n", endwhy.c_str(),
|
||||
simtime.c_str(), walltime, simtimePerf.c_str());
|
||||
const double modelMB = VlOs::memUsageBytes() / 1024.0 / 1024.0;
|
||||
VL_PRINTF("- Verilator: cpu %0.3f s on %d threads; alloced %0.0f MB\n", cputime,
|
||||
VL_PRINTF("- Verilator: cpu %0.3f s on %u threads; alloced %0.0f MB\n", cputime,
|
||||
threadsInModels(), modelMB);
|
||||
}
|
||||
|
||||
@@ -2933,7 +3008,6 @@ void VerilatedContext::trace(VerilatedTraceBaseC* tfp, int levels, int options)
|
||||
VL_FATAL_MT("", 0, "",
|
||||
"Testbench C call to 'VerilatedContext::trace()' must not be called"
|
||||
" after 'VerilatedTrace*::open()'\n");
|
||||
return;
|
||||
}
|
||||
{
|
||||
// Legacy usage may call {modela}->trace(...) then {modelb}->trace(...)
|
||||
@@ -3126,12 +3200,21 @@ void Verilated::stackCheck(QData needSize) VL_MT_UNSAFE {
|
||||
if (haveSize == RLIM_INFINITY) haveSize = 0;
|
||||
}
|
||||
// VL_PRINTF_MT("-Info: stackCheck(%" PRIu64 ") have %" PRIu64 "\n", needSize, haveSize);
|
||||
// Check for 1.5x need, but suggest 2x so small model increase won't cause warning
|
||||
// if the user follows the suggestions
|
||||
if (VL_UNLIKELY(haveSize && needSize && haveSize < (needSize + needSize / 2))) {
|
||||
VL_PRINTF_MT("%%Warning: System has stack size %" PRIu64 " kb"
|
||||
" which may be too small; suggest 'ulimit -s %" PRIu64 "' or larger\n",
|
||||
haveSize / 1024, (needSize * 2) / 1024);
|
||||
// Check and request for 1.5x need. This is automated so the user doesn't need to do anything.
|
||||
QData requestSize = needSize + needSize / 2;
|
||||
if (VL_UNLIKELY(haveSize && needSize && haveSize < requestSize)) {
|
||||
// Try to increase the stack limit to the requested size
|
||||
rlim.rlim_cur = requestSize;
|
||||
if (
|
||||
#ifdef _VL_TEST_RLIMIT_FAIL
|
||||
true ||
|
||||
#endif
|
||||
setrlimit(RLIMIT_STACK, &rlim)) {
|
||||
VL_PRINTF_MT("%%Warning: System has stack size %" PRIu64 " kb"
|
||||
" which may be too small; failed to request more"
|
||||
" using 'ulimit -s %" PRIu64 "'\n",
|
||||
haveSize / 1024, requestSize);
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)needSize; // Unused argument
|
||||
|
||||
+92
-40
@@ -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
|
||||
@@ -396,6 +435,7 @@ protected:
|
||||
std::string m_coverageFilename; // +coverage+file filename
|
||||
std::string m_profExecFilename; // +prof+exec+file filename
|
||||
std::string m_profVltFilename; // +prof+vlt filename
|
||||
std::string m_solverProgram; // SMT solver program
|
||||
VlOs::DeltaCpuTime m_cpuTimeStart{false}; // CPU time, starts when create first model
|
||||
VlOs::DeltaWallTime m_wallTimeStart{false}; // Wall time, starts when create first model
|
||||
std::vector<traceBaseModelCb_t> m_traceBaseModelCbs; // Callbacks to traceRegisterModel
|
||||
@@ -447,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.
|
||||
@@ -469,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);
|
||||
@@ -606,23 +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
|
||||
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;
|
||||
@@ -799,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);
|
||||
@@ -953,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) \
|
||||
@@ -225,7 +226,6 @@ endif
|
||||
# 1. Extract object files from .a
|
||||
# 2. Create a new archive from extracted .o and given .o
|
||||
%.a: | %.verilator_deplist.tmp
|
||||
$(info Archive $(AR) -rcs $@ $^)
|
||||
$(foreach L, $(filter-out %.a,$^), $(shell echo $L >>[email protected]_deplist.tmp))
|
||||
@if test $(words $(filter %.a,$^)) -eq 0; then \
|
||||
$(RM) -f $@; \
|
||||
|
||||
@@ -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
|
||||
|
||||
+348
-33
@@ -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;
|
||||
|
||||
@@ -115,6 +123,9 @@ extern void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp,
|
||||
extern WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP const lwp, WDataInP const rwp,
|
||||
bool is_modulus) VL_MT_SAFE;
|
||||
|
||||
extern void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp,
|
||||
size_t posstart, size_t posend) VL_MT_SAFE;
|
||||
|
||||
extern IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE;
|
||||
|
||||
extern void VL_FFLUSH_I(IData fdi) VL_MT_SAFE;
|
||||
@@ -152,6 +163,7 @@ extern std::string VL_STACKTRACE_N() VL_MT_SAFE;
|
||||
extern IData VL_SYSTEM_IW(int lhswords, WDataInP const lhsp) VL_MT_SAFE;
|
||||
extern IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE;
|
||||
inline IData VL_SYSTEM_II(IData lhs) VL_MT_SAFE { return VL_SYSTEM_IQ(lhs); }
|
||||
extern IData VL_SYSTEM_IN(const std::string& lhs) VL_MT_SAFE;
|
||||
|
||||
extern IData VL_TESTPLUSARGS_I(const std::string& format) VL_MT_SAFE;
|
||||
extern const char* vl_mc_scan_plusargs(const char* prefixp) VL_MT_SAFE; // PLIish
|
||||
@@ -215,6 +227,10 @@ static inline QData VL_CVT_Q_D(double lhs) VL_PURE {
|
||||
return u.q;
|
||||
}
|
||||
// clang-format on
|
||||
// Return string from DPI char*
|
||||
static inline std::string VL_CVT_N_CSTR(const char* lhsp) VL_PURE {
|
||||
return lhsp ? std::string{lhsp} : ""s;
|
||||
}
|
||||
|
||||
// Return double from lhs (numeric) unsigned
|
||||
double VL_ITOR_D_W(int lbits, WDataInP const lwp) VL_PURE;
|
||||
@@ -238,7 +254,7 @@ static inline double VL_ISTOR_D_Q(int lbits, QData lhs) VL_MT_SAFE {
|
||||
VL_SET_WQ(lwp, lhs);
|
||||
return VL_ISTOR_D_W(lbits, lwp);
|
||||
}
|
||||
// Return QData from double (numeric)
|
||||
// Return IData truncated from double (numeric)
|
||||
static inline IData VL_RTOI_I_D(double lhs) VL_PURE { return static_cast<int32_t>(VL_TRUNC(lhs)); }
|
||||
|
||||
// Sign extend such that if MSB set, we get ffff_ffff, else 0s
|
||||
@@ -504,19 +520,18 @@ static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE {
|
||||
{ (svar).write(rd); }
|
||||
#define VL_ASSIGN_SBQ(obits, svar, rd) \
|
||||
{ (svar).write(rd); }
|
||||
#define VL_SC_BITS_PER_DIGIT 30 // This comes from sc_nbdefs.h BITS_PER_DIGIT
|
||||
#define VL_ASSIGN_SBW(obits, svar, rwp) \
|
||||
{ \
|
||||
sc_dt::sc_biguint<(obits)> _butemp; \
|
||||
int32_t lsb = 0; \
|
||||
uint32_t* chunkp = _butemp.get_raw(); \
|
||||
while (lsb + VL_SC_BITS_PER_DIGIT < (obits)) { \
|
||||
while (lsb + BITS_PER_DIGIT < (obits)) { \
|
||||
static_assert(std::is_same<IData, EData>::value, "IData and EData mismatch"); \
|
||||
const uint32_t data = VL_SEL_IWII(lsb + VL_SC_BITS_PER_DIGIT + 1, (rwp).data(), lsb, \
|
||||
VL_SC_BITS_PER_DIGIT); \
|
||||
*chunkp = data & VL_MASK_E(VL_SC_BITS_PER_DIGIT); \
|
||||
const uint32_t data \
|
||||
= VL_SEL_IWII(lsb + BITS_PER_DIGIT + 1, (rwp).data(), lsb, BITS_PER_DIGIT); \
|
||||
*chunkp = data & VL_MASK_E(BITS_PER_DIGIT); \
|
||||
++chunkp; \
|
||||
lsb += VL_SC_BITS_PER_DIGIT; \
|
||||
lsb += BITS_PER_DIGIT; \
|
||||
} \
|
||||
if (lsb < (obits)) { \
|
||||
const uint32_t msb_data = VL_SEL_IWII((obits) + 1, (rwp).data(), lsb, (obits)-lsb); \
|
||||
@@ -1570,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.
|
||||
@@ -2052,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 {
|
||||
|
||||
@@ -0,0 +1,430 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
// Copyright 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
|
||||
//
|
||||
//=========================================================================
|
||||
///
|
||||
/// \file
|
||||
/// \brief Verilated randomization implementation code
|
||||
///
|
||||
/// This file must be compiled and linked against all Verilated objects
|
||||
/// that use randomization features.
|
||||
///
|
||||
/// See the internals documentation docs/internals.rst for details.
|
||||
///
|
||||
//=========================================================================
|
||||
|
||||
#include "verilated_random.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <streambuf>
|
||||
|
||||
#define _VL_SOLVER_HASH_LEN 1
|
||||
#define _VL_SOLVER_HASH_LEN_TOTAL 4
|
||||
|
||||
// clang-format off
|
||||
#if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
|
||||
# define _VL_SOLVER_PIPE // Allow pipe SMT solving. Needs fork()
|
||||
#endif
|
||||
|
||||
#ifdef _VL_SOLVER_PIPE
|
||||
# include <sys/wait.h>
|
||||
# include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) || defined(__MINGW32__)
|
||||
# include <io.h> // open, read, write, close
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
class Process final : private std::streambuf, public std::iostream {
|
||||
static constexpr int BUFFER_SIZE = 4096;
|
||||
const char* const* m_cmd = nullptr; // fork() process argv
|
||||
#ifdef _VL_SOLVER_PIPE
|
||||
pid_t m_pid = 0; // fork() process id
|
||||
#else
|
||||
int m_pid = 0; // fork() process id - always zero as disabled
|
||||
#endif
|
||||
bool m_pidExited = true; // If subprocess has exited and can be opened
|
||||
int m_pidStatus = 0; // fork() process exit status, valid if m_pidExited
|
||||
int m_writeFd = -1; // File descriptor TO subprocess
|
||||
int m_readFd = -1; // File descriptor FROM subprocess
|
||||
char m_readBuf[BUFFER_SIZE];
|
||||
char m_writeBuf[BUFFER_SIZE];
|
||||
|
||||
public:
|
||||
typedef std::streambuf::traits_type traits_type;
|
||||
|
||||
protected:
|
||||
int overflow(int c = traits_type::eof()) override {
|
||||
char c2 = static_cast<char>(c);
|
||||
if (pbase() == pptr()) return 0;
|
||||
size_t size = pptr() - pbase();
|
||||
ssize_t n = ::write(m_writeFd, pbase(), size);
|
||||
if (n == -1) perror("write");
|
||||
if (n <= 0) {
|
||||
wait_report();
|
||||
return traits_type::eof();
|
||||
}
|
||||
if (n == size)
|
||||
setp(m_writeBuf, m_writeBuf + sizeof(m_writeBuf));
|
||||
else
|
||||
setp(m_writeBuf + n, m_writeBuf + sizeof(m_writeBuf));
|
||||
if (c != traits_type::eof()) sputc(c2);
|
||||
return 0;
|
||||
}
|
||||
int underflow() override {
|
||||
sync();
|
||||
ssize_t n = ::read(m_readFd, m_readBuf, sizeof(m_readBuf));
|
||||
if (n == -1) perror("read");
|
||||
if (n <= 0) {
|
||||
wait_report();
|
||||
return traits_type::eof();
|
||||
}
|
||||
setg(m_readBuf, m_readBuf, m_readBuf + n);
|
||||
return traits_type::to_int_type(m_readBuf[0]);
|
||||
}
|
||||
int sync() override {
|
||||
overflow();
|
||||
return 0;
|
||||
}
|
||||
|
||||
public:
|
||||
explicit Process(const char* const* const cmd = nullptr)
|
||||
: std::streambuf{}
|
||||
, std::iostream{this}
|
||||
, m_cmd{cmd} {
|
||||
open(cmd);
|
||||
}
|
||||
|
||||
void wait_report() {
|
||||
if (m_pidExited) return;
|
||||
#ifdef _VL_SOLVER_PIPE
|
||||
if (waitpid(m_pid, &m_pidStatus, 0) != m_pid) return;
|
||||
if (m_pidStatus) {
|
||||
std::stringstream msg;
|
||||
msg << "Subprocess command `" << m_cmd[0];
|
||||
for (const char* const* arg = m_cmd + 1; *arg; arg++) msg << ' ' << *arg;
|
||||
msg << "' failed: ";
|
||||
if (WIFSIGNALED(m_pidStatus))
|
||||
msg << strsignal(WTERMSIG(m_pidStatus))
|
||||
<< (WCOREDUMP(m_pidStatus) ? " (core dumped)" : "");
|
||||
else if (WIFEXITED(m_pidStatus))
|
||||
msg << "exit status " << WEXITSTATUS(m_pidStatus);
|
||||
const std::string str = msg.str();
|
||||
VL_WARN_MT("", 0, "Process", str.c_str());
|
||||
}
|
||||
#endif
|
||||
m_pidExited = true;
|
||||
m_pid = 0;
|
||||
closeFds();
|
||||
}
|
||||
|
||||
void closeFds() {
|
||||
if (m_writeFd != -1) {
|
||||
close(m_writeFd);
|
||||
m_writeFd = -1;
|
||||
}
|
||||
if (m_readFd != -1) {
|
||||
close(m_readFd);
|
||||
m_readFd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool open(const char* const* const cmd) {
|
||||
setp(std::begin(m_writeBuf), std::end(m_writeBuf));
|
||||
setg(m_readBuf, m_readBuf, m_readBuf);
|
||||
#ifdef _VL_SOLVER_PIPE
|
||||
if (!cmd || !cmd[0]) return false;
|
||||
m_cmd = cmd;
|
||||
int fd_stdin[2]; // Can't use std::array
|
||||
int fd_stdout[2]; // Can't use std::array
|
||||
constexpr int P_RD = 0;
|
||||
constexpr int P_WR = 1;
|
||||
|
||||
if (pipe(fd_stdin) != 0) {
|
||||
perror("Process::open: pipe");
|
||||
return false;
|
||||
}
|
||||
if (pipe(fd_stdout) != 0) {
|
||||
perror("Process::open: pipe");
|
||||
close(fd_stdin[P_RD]);
|
||||
close(fd_stdin[P_WR]);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fd_stdin[P_RD] <= 2 || fd_stdin[P_WR] <= 2 || fd_stdout[P_RD] <= 2
|
||||
|| fd_stdout[P_WR] <= 2) {
|
||||
// We'd have to rearrange all of the FD usages in this case.
|
||||
// Too unlikely; verilator isn't a daemon.
|
||||
fprintf(stderr, "stdin/stdout closed before pipe opened\n");
|
||||
close(fd_stdin[P_RD]);
|
||||
close(fd_stdin[P_WR]);
|
||||
close(fd_stdout[P_RD]);
|
||||
close(fd_stdout[P_WR]);
|
||||
return false;
|
||||
}
|
||||
|
||||
const pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
perror("Process::open: fork");
|
||||
close(fd_stdin[P_RD]);
|
||||
close(fd_stdin[P_WR]);
|
||||
close(fd_stdout[P_RD]);
|
||||
close(fd_stdout[P_WR]);
|
||||
return false;
|
||||
}
|
||||
if (pid == 0) {
|
||||
// Child
|
||||
close(fd_stdin[P_WR]);
|
||||
dup2(fd_stdin[P_RD], STDIN_FILENO);
|
||||
close(fd_stdout[P_RD]);
|
||||
dup2(fd_stdout[P_WR], STDOUT_FILENO);
|
||||
execvp(cmd[0], const_cast<char* const*>(cmd));
|
||||
std::stringstream msg;
|
||||
msg << "Process::open: execvp(" << cmd[0] << ")";
|
||||
const std::string str = msg.str();
|
||||
perror(str.c_str());
|
||||
_exit(127);
|
||||
}
|
||||
// Parent
|
||||
m_pid = pid;
|
||||
m_pidExited = false;
|
||||
m_pidStatus = 0;
|
||||
m_readFd = fd_stdout[P_RD];
|
||||
m_writeFd = fd_stdin[P_WR];
|
||||
|
||||
close(fd_stdin[P_RD]);
|
||||
close(fd_stdout[P_WR]);
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
static Process& getSolver() {
|
||||
static Process s_solver;
|
||||
static bool s_done = false;
|
||||
if (s_done) return s_solver;
|
||||
s_done = true;
|
||||
|
||||
static std::vector<const char*> s_argv;
|
||||
static std::string s_program = Verilated::threadContextp()->solverProgram();
|
||||
s_argv.emplace_back(&s_program[0]);
|
||||
for (char* arg = &s_program[0]; *arg; arg++) {
|
||||
if (*arg == ' ') {
|
||||
*arg = '\0';
|
||||
s_argv.emplace_back(arg + 1);
|
||||
}
|
||||
}
|
||||
s_argv.emplace_back(nullptr);
|
||||
|
||||
const char* const* const cmd = &s_argv[0];
|
||||
s_solver.open(cmd);
|
||||
s_solver << "(set-logic QF_BV)\n";
|
||||
s_solver << "(check-sat)\n";
|
||||
s_solver << "(reset)\n";
|
||||
std::string s;
|
||||
getline(s_solver, s);
|
||||
if (s == "sat") return s_solver;
|
||||
|
||||
std::stringstream msg;
|
||||
msg << "Unable to communicate with SAT solver, please check its installation or specify a "
|
||||
"different one in VERILATOR_SOLVER environment variable.\n";
|
||||
msg << " ... Tried: $";
|
||||
for (const char* const* arg = cmd; *arg; arg++) msg << ' ' << *arg;
|
||||
msg << '\n';
|
||||
const std::string str = msg.str();
|
||||
VL_WARN_MT("", 0, "randomize", str.c_str());
|
||||
|
||||
while (getline(s_solver, s)) {}
|
||||
return s_solver;
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// VlRandomizer:: Methods
|
||||
|
||||
void VlRandomVar::emit(std::ostream& s) const { s << m_name; }
|
||||
void VlRandomConst::emit(std::ostream& s) const {
|
||||
s << "#b";
|
||||
for (int i = 0; i < m_width; i++) s << (VL_BITISSET_Q(m_val, m_width - i - 1) ? '1' : '0');
|
||||
}
|
||||
void VlRandomBinOp::emit(std::ostream& s) const {
|
||||
s << '(' << m_op << ' ';
|
||||
m_lhs->emit(s);
|
||||
s << ' ';
|
||||
m_rhs->emit(s);
|
||||
s << ')';
|
||||
}
|
||||
void VlRandomExtract::emit(std::ostream& s) const {
|
||||
s << "((_ extract " << m_idx << ' ' << m_idx << ") ";
|
||||
m_expr->emit(s);
|
||||
s << ')';
|
||||
}
|
||||
bool VlRandomVar::set(std::string&& val) const {
|
||||
VlWide<VL_WQ_WORDS_E> qowp;
|
||||
VL_SET_WQ(qowp, 0ULL);
|
||||
WDataOutP owp = qowp;
|
||||
int obits = width();
|
||||
if (obits > VL_QUADSIZE) owp = reinterpret_cast<WDataOutP>(datap());
|
||||
int i;
|
||||
for (i = 0; val[i] && val[i] != '#'; i++) {}
|
||||
if (val[i++] != '#') return false;
|
||||
switch (val[i++]) {
|
||||
case 'b': _vl_vsss_based(owp, obits, 1, &val[i], 0, val.size() - i); break;
|
||||
case 'o': _vl_vsss_based(owp, obits, 3, &val[i], 0, val.size() - i); break;
|
||||
case 'h': // FALLTHRU
|
||||
case 'x': _vl_vsss_based(owp, obits, 4, &val[i], 0, val.size() - i); break;
|
||||
default:
|
||||
VL_WARN_MT(__FILE__, __LINE__, "randomize",
|
||||
"Internal: Unable to parse solver's randomized number");
|
||||
return false;
|
||||
}
|
||||
if (obits <= VL_BYTESIZE) {
|
||||
CData* const p = static_cast<CData*>(datap());
|
||||
*p = VL_CLEAN_II(obits, obits, owp[0]);
|
||||
} else if (obits <= VL_SHORTSIZE) {
|
||||
SData* const p = static_cast<SData*>(datap());
|
||||
*p = VL_CLEAN_II(obits, obits, owp[0]);
|
||||
} else if (obits <= VL_IDATASIZE) {
|
||||
IData* const p = static_cast<IData*>(datap());
|
||||
*p = VL_CLEAN_II(obits, obits, owp[0]);
|
||||
} else if (obits <= VL_QUADSIZE) {
|
||||
QData* const p = static_cast<QData*>(datap());
|
||||
*p = VL_CLEAN_QQ(obits, obits, VL_SET_QW(owp));
|
||||
} else {
|
||||
_vl_clean_inplace_w(obits, owp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<const VlRandomExpr> VlRandomizer::randomConstraint(VlRNG& rngr, int bits) {
|
||||
unsigned long long hash = VL_RANDOM_RNG_I(rngr) & ((1 << bits) - 1);
|
||||
std::shared_ptr<const VlRandomExpr> concat = nullptr;
|
||||
std::vector<std::shared_ptr<const VlRandomExpr>> varbits;
|
||||
for (const auto& var : m_vars) {
|
||||
for (int i = 0; i < var.second->width(); i++)
|
||||
varbits.emplace_back(std::make_shared<const VlRandomExtract>(var.second, i));
|
||||
}
|
||||
for (int i = 0; i < bits; i++) {
|
||||
std::shared_ptr<const VlRandomExpr> bit = nullptr;
|
||||
for (unsigned j = 0; j * 2 < varbits.size(); j++) {
|
||||
unsigned idx = j + VL_RANDOM_RNG_I(rngr) % (varbits.size() - j);
|
||||
auto sel = varbits[idx];
|
||||
std::swap(varbits[idx], varbits[j]);
|
||||
bit = bit == nullptr ? sel : std::make_shared<const VlRandomBinOp>("bvxor", bit, sel);
|
||||
}
|
||||
concat = concat == nullptr ? bit
|
||||
: std::make_shared<const VlRandomBinOp>("concat", concat, bit);
|
||||
}
|
||||
return std::make_shared<const VlRandomBinOp>(
|
||||
"=", concat, std::make_shared<const VlRandomConst>(hash, bits));
|
||||
}
|
||||
|
||||
bool VlRandomizer::next(VlRNG& rngr) {
|
||||
if (m_vars.empty()) return true;
|
||||
std::iostream& f = getSolver();
|
||||
if (!f) return false;
|
||||
|
||||
f << "(set-option :produce-models true)\n";
|
||||
f << "(set-logic QF_BV)\n";
|
||||
for (const auto& var : m_vars) {
|
||||
f << "(declare-fun " << var.second->name() << " () (_ BitVec " << var.second->width()
|
||||
<< "))\n";
|
||||
}
|
||||
for (const std::string& constraint : m_constraints) { f << "(assert " << constraint << ")\n"; }
|
||||
f << "(check-sat)\n";
|
||||
|
||||
bool sat = parseSolution(f);
|
||||
if (!sat) {
|
||||
f << "(reset)\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _VL_SOLVER_HASH_LEN_TOTAL && sat; i++) {
|
||||
f << "(assert ";
|
||||
randomConstraint(rngr, _VL_SOLVER_HASH_LEN)->emit(f);
|
||||
f << ")\n";
|
||||
f << "\n(check-sat)\n";
|
||||
sat = parseSolution(f);
|
||||
}
|
||||
|
||||
f << "(reset)\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VlRandomizer::parseSolution(std::iostream& f) {
|
||||
std::string sat;
|
||||
do { std::getline(f, sat); } while (sat == "");
|
||||
|
||||
if (sat == "unsat") return false;
|
||||
if (sat != "sat") {
|
||||
std::stringstream msg;
|
||||
msg << "Internal: Solver error: " << sat;
|
||||
const std::string str = msg.str();
|
||||
VL_WARN_MT(__FILE__, __LINE__, "randomize", str.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
f << "(get-value (";
|
||||
for (const auto& var : m_vars) f << var.second->name() << ' ';
|
||||
f << "))\n";
|
||||
|
||||
// Quasi-parse S-expression of the form ((x #xVALUE) (y #bVALUE) (z #xVALUE))
|
||||
char c;
|
||||
f >> c;
|
||||
if (c != '(') {
|
||||
VL_WARN_MT(__FILE__, __LINE__, "randomize",
|
||||
"Internal: Unable to parse solver's response: invalid S-expression");
|
||||
return false;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
f >> c;
|
||||
if (c == ')') break;
|
||||
if (c != '(') {
|
||||
VL_WARN_MT(__FILE__, __LINE__, "randomize",
|
||||
"Internal: Unable to parse solver's response: invalid S-expression");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string name, value;
|
||||
f >> name;
|
||||
std::getline(f, value, ')');
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
varr.set(std::move(value));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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) {
|
||||
VL_PRINTF("Variable (%d): %s\n", var.second->width(), var.second->name());
|
||||
}
|
||||
for (const std::string& c : m_constraints) VL_PRINTF("Constraint: %s\n", c.c_str());
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,128 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
// Copyright 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
|
||||
//
|
||||
//*************************************************************************
|
||||
///
|
||||
/// \file
|
||||
/// \brief Verilated randomization header
|
||||
///
|
||||
/// This file is included automatically by Verilator in some of the C++ files
|
||||
/// it generates if randomization features are used.
|
||||
///
|
||||
/// This file is not part of the Verilated public-facing API.
|
||||
/// It is only for internal use.
|
||||
///
|
||||
/// See the internals documentation docs/internals.rst for details.
|
||||
///
|
||||
//*************************************************************************
|
||||
#ifndef VERILATOR_VERILATED_RANDOM_H_
|
||||
#define VERILATOR_VERILATED_RANDOM_H_
|
||||
|
||||
#include "verilated.h"
|
||||
|
||||
//=============================================================================
|
||||
// VlRandomExpr and subclasses represent expressions for the constraint solver.
|
||||
|
||||
class VlRandomExpr VL_NOT_FINAL {
|
||||
public:
|
||||
virtual void emit(std::ostream& s) const = 0;
|
||||
};
|
||||
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, std::uint32_t randModeIdx)
|
||||
: m_name{name}
|
||||
, m_datap{datap}
|
||||
, 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;
|
||||
};
|
||||
|
||||
class VlRandomConst final : public VlRandomExpr {
|
||||
const QData m_val; // Constant value
|
||||
const int m_width; // Constant width in bits
|
||||
|
||||
public:
|
||||
VlRandomConst(QData val, int width)
|
||||
: m_val{val}
|
||||
, m_width{width} {
|
||||
assert(width <= sizeof(m_val) * 8);
|
||||
}
|
||||
void emit(std::ostream& s) const override;
|
||||
};
|
||||
|
||||
class VlRandomExtract final : public VlRandomExpr {
|
||||
const std::shared_ptr<const VlRandomExpr> m_expr; // Sub-expression
|
||||
const unsigned m_idx; // Extracted index
|
||||
|
||||
public:
|
||||
VlRandomExtract(std::shared_ptr<const VlRandomExpr> expr, unsigned idx)
|
||||
: m_expr{expr}
|
||||
, m_idx{idx} {}
|
||||
void emit(std::ostream& s) const override;
|
||||
};
|
||||
|
||||
class VlRandomBinOp final : public VlRandomExpr {
|
||||
const char* const m_op; // Binary operation identifier
|
||||
const std::shared_ptr<const VlRandomExpr> m_lhs, m_rhs; // Sub-expressions
|
||||
|
||||
public:
|
||||
VlRandomBinOp(const char* op, std::shared_ptr<const VlRandomExpr> lhs,
|
||||
std::shared_ptr<const VlRandomExpr> rhs)
|
||||
: m_op{op}
|
||||
, m_lhs{lhs}
|
||||
, m_rhs{rhs} {}
|
||||
void emit(std::ostream& s) const override;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlRandomizer is the object holding constraints and variable references.
|
||||
|
||||
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
|
||||
const VlQueue<CData>* m_randmode; // rand_mode state;
|
||||
|
||||
// PRIVATE METHODS
|
||||
std::shared_ptr<const VlRandomExpr> randomConstraint(VlRNG& rngr, int bits);
|
||||
bool parseSolution(std::iostream& file);
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
// Finds the next solution satisfying the constraints
|
||||
bool next(VlRNG& rngr);
|
||||
template <typename T>
|
||||
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, 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
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
@@ -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
|
||||
|
||||
@@ -67,9 +67,16 @@ void VlDelayScheduler::resume() {
|
||||
}
|
||||
|
||||
if (!m_zeroDelayed.empty()) {
|
||||
for (auto&& handle : m_zeroDelayed) handle.resume();
|
||||
m_zeroDelayed.clear();
|
||||
// First, we need to move the coroutines out of the queue, as a resumed coroutine can
|
||||
// suspend on #0 again, adding itself to the queue, which can result in reallocating the
|
||||
// queue mid-iteration.
|
||||
// We swap with the m_zeroDlyResumed field to keep the allocated buffer.
|
||||
m_zeroDlyResumed.swap(m_zeroDelayed);
|
||||
for (auto&& handle : m_zeroDlyResumed) handle.resume();
|
||||
m_zeroDlyResumed.clear();
|
||||
resumed = true;
|
||||
// We are now in the Active region, so any coroutines added to m_zeroDelayed in the
|
||||
// meantime will have to wait until the next Inactive region.
|
||||
}
|
||||
|
||||
if (!resumed) {
|
||||
|
||||
+18
-11
@@ -32,14 +32,18 @@
|
||||
// clang-format off
|
||||
// Some preprocessor magic to support both Clang and GCC coroutines with both libc++ and libstdc++
|
||||
#if defined _LIBCPP_VERSION // libc++
|
||||
# if __clang_major__ > 13 // Clang > 13 warns that coroutine types in std::experimental are deprecated
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-experimental-coroutine"
|
||||
# if defined(__has_include) && !__has_include(<coroutine>) && __has_include(<experimental/coroutine>)
|
||||
# if __clang_major__ > 13 // Clang > 13 warns that coroutine types in std::experimental are deprecated
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-experimental-coroutine"
|
||||
# endif
|
||||
# include <experimental/coroutine>
|
||||
namespace std {
|
||||
using namespace experimental; // Bring std::experimental into the std namespace
|
||||
}
|
||||
# else
|
||||
# include <coroutine>
|
||||
# endif
|
||||
# include <experimental/coroutine>
|
||||
namespace std {
|
||||
using namespace experimental; // Bring std::experimental into the std namespace
|
||||
}
|
||||
#else
|
||||
# if defined __clang__ && defined __GLIBCXX__ && !defined __cpp_impl_coroutine
|
||||
# define __cpp_impl_coroutine 1 // Clang doesn't define this, but it's needed for libstdc++
|
||||
@@ -165,6 +169,9 @@ class VlDelayScheduler final {
|
||||
VerilatedContext& m_context;
|
||||
VlDelayedCoroutineQueue m_queue; // Coroutines to be restored at a certain simulation time
|
||||
std::vector<VlCoroutineHandle> m_zeroDelayed; // Coroutines waiting for #0
|
||||
std::vector<VlCoroutineHandle> m_zeroDlyResumed; // Coroutines that waited for #0 and are
|
||||
// to be resumed. Kept as a field to avoid
|
||||
// reallocation.
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -193,9 +200,9 @@ public:
|
||||
VlProcessRef process; // Data of the suspended process, null if not needed
|
||||
VlDelayedCoroutineQueue& queue;
|
||||
std::vector<VlCoroutineHandle>& queueZeroDelay;
|
||||
uint64_t delay;
|
||||
VlDelayPhase phase;
|
||||
VlFileLineDebug fileline;
|
||||
const uint64_t delay;
|
||||
const VlDelayPhase phase;
|
||||
const VlFileLineDebug fileline;
|
||||
|
||||
bool await_ready() const { return false; } // Always suspend
|
||||
void await_suspend(std::coroutine_handle<> coro) {
|
||||
@@ -208,7 +215,7 @@ public:
|
||||
void await_resume() const {}
|
||||
};
|
||||
|
||||
VlDelayPhase phase = (delay == 0) ? VlDelayPhase::INACTIVE : VlDelayPhase::ACTIVE;
|
||||
const VlDelayPhase phase = (delay == 0) ? VlDelayPhase::INACTIVE : VlDelayPhase::ACTIVE;
|
||||
#ifdef VL_DEBUG
|
||||
if (phase == VlDelayPhase::INACTIVE) {
|
||||
VL_WARN_MT(filename, lineno, VL_UNKNOWN,
|
||||
|
||||
@@ -30,8 +30,9 @@
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <type_traits>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <deque>
|
||||
@@ -294,7 +295,7 @@ private:
|
||||
uint64_t m_timeLastDump = 0; // Last time we did a dump
|
||||
bool m_didSomeDump = false; // Did at least one dump (i.e.: m_timeLastDump is valid)
|
||||
VerilatedContext* m_contextp = nullptr; // The context used by the traced models
|
||||
std::unordered_set<const VerilatedModel*> m_models; // The collection of models being traced
|
||||
std::set<const VerilatedModel*> m_models; // The collection of models being traced
|
||||
|
||||
void addCallbackRecord(std::vector<CallbackRecord>& cbVec, CallbackRecord&& cbRec)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
|
||||
@@ -633,12 +633,13 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addModel(VerilatedModel* modelp)
|
||||
|
||||
// Validate
|
||||
if (!newModel) { // LCOV_EXCL_START
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"The same model has already been added to this trace file");
|
||||
VL_FATAL_MT(
|
||||
__FILE__, __LINE__, "",
|
||||
"The same model has already been added to this trace file or VerilatedContext");
|
||||
}
|
||||
if (VL_UNCOVERABLE(m_contextp && contextp != m_contextp)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"A trace file instance can only handle models from the same context");
|
||||
"A trace file instance can only handle models from the same VerilatedContext");
|
||||
}
|
||||
if (VL_UNCOVERABLE(m_didSomeDump)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
|
||||
+240
-36
@@ -36,7 +36,6 @@
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
//=========================================================================
|
||||
@@ -415,8 +414,20 @@ public:
|
||||
|
||||
static int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_PURE;
|
||||
|
||||
template <std::size_t T_Words>
|
||||
struct VlWide;
|
||||
|
||||
// Type trait to check if a type is VlWide
|
||||
template <typename>
|
||||
struct VlIsVlWide : public std::false_type {};
|
||||
|
||||
template <std::size_t T_Words>
|
||||
struct VlIsVlWide<VlWide<T_Words>> : public std::true_type {};
|
||||
|
||||
template <std::size_t T_Words>
|
||||
struct VlWide final {
|
||||
static constexpr size_t Words = T_Words;
|
||||
|
||||
// MEMBERS
|
||||
// This should be the only data member, otherwise generated static initializers need updating
|
||||
EData m_storage[T_Words]; // Contents of the packed array
|
||||
@@ -501,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;
|
||||
@@ -740,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{};
|
||||
@@ -749,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>{};
|
||||
@@ -758,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{};
|
||||
@@ -767,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>{};
|
||||
@@ -777,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 {
|
||||
@@ -786,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 {
|
||||
@@ -800,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 {
|
||||
@@ -891,8 +903,8 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template <class T_Value>
|
||||
std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
|
||||
template <class T_Value, size_t T_MaxSize>
|
||||
std::string VL_TO_STRING(const VlQueue<T_Value, T_MaxSize>& obj) {
|
||||
return obj.to_string();
|
||||
}
|
||||
|
||||
@@ -1081,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 {
|
||||
@@ -1090,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 {
|
||||
@@ -1098,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 {
|
||||
@@ -1106,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
|
||||
@@ -1117,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 {
|
||||
@@ -1127,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>();
|
||||
@@ -1136,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 {
|
||||
@@ -1146,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 {
|
||||
@@ -1309,7 +1321,11 @@ public:
|
||||
// Similar to 'neq' above, *this = that used for change detection
|
||||
void assign(const VlUnpacked<T_Value, T_Depth>& that) { *this = that; }
|
||||
bool operator==(const VlUnpacked<T_Value, T_Depth>& that) const { return !neq(that); }
|
||||
bool operator!=(const VlUnpacked<T_Value, T_Depth>& that) { return neq(that); }
|
||||
bool operator!=(const VlUnpacked<T_Value, T_Depth>& that) const { return neq(that); }
|
||||
// interface to C style arrays (used in ports), see issue #5125
|
||||
bool neq(const T_Value that[T_Depth]) const { return neq(*this, that); }
|
||||
void assign(const T_Value that[T_Depth]) { std::copy_n(that, T_Depth, m_storage); }
|
||||
void operator=(const T_Value that[T_Depth]) { assign(that); }
|
||||
|
||||
// inside (set membership operator)
|
||||
bool inside(const T_Value& value) const {
|
||||
@@ -1386,7 +1402,7 @@ public:
|
||||
VlQueue<T_Key> unique_index(Func with_func) const {
|
||||
VlQueue<T_Key> out;
|
||||
IData index = 0;
|
||||
std::unordered_set<T_Value> saw;
|
||||
std::set<T_Value> saw;
|
||||
for (const auto& i : m_storage) {
|
||||
const auto i_mapped = with_func(index, i);
|
||||
auto it = saw.find(i_mapped);
|
||||
@@ -1423,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>{};
|
||||
@@ -1432,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>{};
|
||||
@@ -1440,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>{};
|
||||
}
|
||||
@@ -1455,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 {
|
||||
@@ -1463,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 {
|
||||
@@ -1475,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)
|
||||
@@ -1499,6 +1515,15 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T_Val, std::size_t T_Dep>
|
||||
static bool neq(const VlUnpacked<T_Val, T_Dep>& a, const T_Val b[T_Dep]) {
|
||||
for (size_t i = 0; i < T_Dep; ++i) {
|
||||
// Recursive 'neq', in case T_Val is also a VlUnpacked<_, _>
|
||||
if (neq(a.m_storage[i], b[i])) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T_Other> //
|
||||
static bool neq(const T_Other& a, const T_Other& b) {
|
||||
// Base case (T_Other is not VlUnpacked<_, _>), fall back on !=
|
||||
@@ -1511,6 +1536,181 @@ std::string VL_TO_STRING(const VlUnpacked<T_Value, T_Depth>& obj) {
|
||||
return obj.to_string();
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Helper to apply the given indices to a target expression
|
||||
|
||||
template <size_t Curr, size_t Rank, typename T_Target>
|
||||
struct VlApplyIndices final {
|
||||
VL_ATTR_ALWINLINE
|
||||
static auto& apply(T_Target& target, const size_t* indicesp) {
|
||||
return VlApplyIndices<Curr + 1, Rank, decltype(target[indicesp[Curr]])>::apply(
|
||||
target[indicesp[Curr]], indicesp);
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t Rank, typename T_Target>
|
||||
struct VlApplyIndices<Rank, Rank, T_Target> final {
|
||||
VL_ATTR_ALWINLINE
|
||||
static T_Target& apply(T_Target& target, const size_t*) { return target; }
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// Commit queue for NBAs - currently only for unpacked arrays
|
||||
//
|
||||
// This data-structure is used to handle non-blocking assignments
|
||||
// that might execute a variable number of times in a single
|
||||
// evaluation. It has 2 operations:
|
||||
// - 'enqueue' will add an update to the queue
|
||||
// - 'commit' will apply all enqueued updates to the target variable,
|
||||
// in the order they were enqueued. This ensures the last NBA
|
||||
// takes effect as it is expected.
|
||||
// There are 2 specializations of this class below:
|
||||
// - A version when a partial element update is not required,
|
||||
// e.g, to handle:
|
||||
// logic [31:0] array[N];
|
||||
// for (int i = 0 ; i < N ; ++i) array[i] <= x;
|
||||
// Here 'enqueue' takes the RHS ('x'), and the array indices ('i')
|
||||
// as arguments.
|
||||
// - A different version when a partial element update is required,
|
||||
// e.g. for:
|
||||
// logic [31:0] array[N];
|
||||
// for (int i = 0 ; i < N ; ++i) array[i][3:1] <= y;
|
||||
// Here 'enqueue' takes one additional argument, which is a bitmask
|
||||
// derived from the bit selects (_[3:1]), which masks the bits that
|
||||
// need to be updated, and additionally the RHS is widened to a full
|
||||
// element size, with the bits inserted into the masked region.
|
||||
template <typename T_Target, // Type of the variable this commit queue updates
|
||||
bool Partial, // Whether partial element updates are necessary
|
||||
// The following we could figure out from 'T_Target using type traits, but passing
|
||||
// explicitly to avoid template expansion, as Verilator already knows them
|
||||
typename T_Element, // Non-array leaf element type of T_Target array
|
||||
std::size_t T_Rank // Rank of T_Target (i.e.: how many dimensions it has)
|
||||
>
|
||||
class VlNBACommitQueue;
|
||||
|
||||
// Specialization for whole element updates only
|
||||
template <typename T_Target, typename T_Element, std::size_t T_Rank>
|
||||
class VlNBACommitQueue<T_Target, /* Partial: */ false, T_Element, T_Rank> final {
|
||||
// TYPES
|
||||
struct Entry final {
|
||||
T_Element value;
|
||||
size_t indices[T_Rank];
|
||||
};
|
||||
|
||||
// STATE
|
||||
std::vector<Entry> m_pending; // Pending updates, in program order
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
VlNBACommitQueue() = default;
|
||||
VL_UNCOPYABLE(VlNBACommitQueue);
|
||||
|
||||
// METHODS
|
||||
template <typename... Args>
|
||||
void enqueue(const T_Element& value, Args... indices) {
|
||||
m_pending.emplace_back(Entry{value, {indices...}});
|
||||
}
|
||||
|
||||
// Note: T_Commit might be different from T_Target. Specifically, when the signal is a
|
||||
// top-level IO port, T_Commit will be a native C array, while T_Target, will be a VlUnpacked
|
||||
template <typename T_Commit>
|
||||
void commit(T_Commit& target) {
|
||||
if (m_pending.empty()) return;
|
||||
for (const Entry& entry : m_pending) {
|
||||
VlApplyIndices<0, T_Rank, T_Commit>::apply(target, entry.indices) = entry.value;
|
||||
}
|
||||
m_pending.clear();
|
||||
}
|
||||
};
|
||||
|
||||
// With partial element updates
|
||||
template <typename T_Target, typename T_Element, std::size_t T_Rank>
|
||||
class VlNBACommitQueue<T_Target, /* Partial: */ true, T_Element, T_Rank> final {
|
||||
// TYPES
|
||||
struct Entry final {
|
||||
T_Element value;
|
||||
T_Element mask;
|
||||
size_t indices[T_Rank];
|
||||
};
|
||||
|
||||
// STATE
|
||||
std::vector<Entry> m_pending; // Pending updates, in program order
|
||||
|
||||
// STATIC METHODS
|
||||
|
||||
// Binary & | ~ for elements to use for masking in partial updates. Sorry for the templates.
|
||||
template <typename T>
|
||||
VL_ATTR_ALWINLINE static typename std::enable_if<!VlIsVlWide<T>::value, T>::type
|
||||
bAnd(const T& a, const T& b) {
|
||||
return a & b;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
VL_ATTR_ALWINLINE static typename std::enable_if<VlIsVlWide<T>::value, T>::type
|
||||
bAnd(const T& a, const T& b) {
|
||||
T result;
|
||||
for (size_t i = 0; i < T::Words; ++i) {
|
||||
result.m_storage[i] = a.m_storage[i] & b.m_storage[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
VL_ATTR_ALWINLINE static typename std::enable_if<!VlIsVlWide<T>::value, T>::type
|
||||
bOr(const T& a, const T& b) {
|
||||
return a | b;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
VL_ATTR_ALWINLINE static typename std::enable_if<VlIsVlWide<T>::value, T>::type //
|
||||
bOr(const T& a, const T& b) {
|
||||
T result;
|
||||
for (size_t i = 0; i < T::Words; ++i) {
|
||||
result.m_storage[i] = a.m_storage[i] | b.m_storage[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
VL_ATTR_ALWINLINE static typename std::enable_if<!VlIsVlWide<T>::value, T>::type
|
||||
bNot(const T& a) {
|
||||
return ~a;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
VL_ATTR_ALWINLINE static typename std::enable_if<VlIsVlWide<T>::value, T>::type
|
||||
bNot(const T& a) {
|
||||
T result;
|
||||
for (size_t i = 0; i < T::Words; ++i) result.m_storage[i] = ~a.m_storage[i];
|
||||
return result;
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
VlNBACommitQueue() = default;
|
||||
VL_UNCOPYABLE(VlNBACommitQueue);
|
||||
|
||||
// METHODS
|
||||
template <typename... Args>
|
||||
void enqueue(const T_Element& value, const T_Element& mask, Args... indices) {
|
||||
m_pending.emplace_back(Entry{value, mask, {indices...}});
|
||||
}
|
||||
|
||||
// Note: T_Commit might be different from T_Target. Specifically, when the signal is a
|
||||
// top-level IO port, T_Commit will be a native C array, while T_Target, will be a VlUnpacked
|
||||
template <typename T_Commit>
|
||||
void commit(T_Commit& target) {
|
||||
if (m_pending.empty()) return;
|
||||
for (const Entry& entry : m_pending) { //
|
||||
auto& ref = VlApplyIndices<0, T_Rank, T_Commit>::apply(target, entry.indices);
|
||||
// Maybe inefficient, but it works for now ...
|
||||
const auto oldValue = ref;
|
||||
ref = bOr(bAnd(entry.value, entry.mask), bAnd(oldValue, bNot(entry.mask)));
|
||||
}
|
||||
m_pending.clear();
|
||||
}
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// Object that VlDeleter is capable of deleting
|
||||
|
||||
@@ -1587,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
|
||||
|
||||
+222
-81
@@ -31,11 +31,14 @@
|
||||
#include "verilated.h"
|
||||
#include "verilated_imp.h"
|
||||
|
||||
#include "vltstd/vpi_user.h"
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -630,6 +633,146 @@ public:
|
||||
void invalidate() { m_id = 0; }
|
||||
};
|
||||
|
||||
class VerilatedVpiPutHolder final {
|
||||
VerilatedVpioVar m_var;
|
||||
s_vpi_value m_value;
|
||||
union Storage {
|
||||
char init = 0; // to ensure trivial constructor
|
||||
std::string str;
|
||||
std::vector<s_vpi_vecval> vec;
|
||||
~Storage() noexcept {/* handled by VerilatedVpiPutHolder */};
|
||||
} m_storage{};
|
||||
|
||||
public:
|
||||
VerilatedVpiPutHolder(const VerilatedVpioVar* vop, p_vpi_value valuep)
|
||||
: m_var{vop} {
|
||||
m_value.format = valuep->format;
|
||||
switch (valuep->format) {
|
||||
case vpiBinStrVal: // FALLTHRU
|
||||
case vpiOctStrVal: // FALLTHRU
|
||||
case vpiDecStrVal: // FALLTHRU
|
||||
case vpiHexStrVal: // FALLTHRU
|
||||
case vpiStringVal: {
|
||||
new (&m_storage.str) std::string{valuep->value.str};
|
||||
m_value.value.str = const_cast<char*>(m_storage.str.c_str());
|
||||
break;
|
||||
}
|
||||
case vpiScalarVal: {
|
||||
m_value.value.scalar = valuep->value.scalar;
|
||||
break;
|
||||
}
|
||||
case vpiIntVal: {
|
||||
m_value.value.integer = valuep->value.integer;
|
||||
break;
|
||||
}
|
||||
case vpiRealVal: {
|
||||
m_value.value.real = valuep->value.real;
|
||||
break;
|
||||
}
|
||||
case vpiVectorVal: {
|
||||
size_t words = 0;
|
||||
switch (vop->varp()->vltype()) {
|
||||
case VLVT_UINT8:
|
||||
case VLVT_UINT16:
|
||||
case VLVT_UINT32: {
|
||||
words = 1;
|
||||
break;
|
||||
}
|
||||
case VLVT_UINT64: {
|
||||
words = 2;
|
||||
break;
|
||||
}
|
||||
case VLVT_WDATA: {
|
||||
words = VL_WORDS_I(vop->varp()->packed().elements());
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
new (&m_storage.vec)
|
||||
std::vector<s_vpi_vecval>{valuep->value.vector, &valuep->value.vector[words]};
|
||||
m_value.value.vector = m_storage.vec.data();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerilatedVpiPutHolder(VerilatedVpiPutHolder const& o)
|
||||
: m_var{o.m_var}
|
||||
, m_value{o.m_value} {
|
||||
switch (m_value.format) {
|
||||
case vpiBinStrVal: // FALLTHRU
|
||||
case vpiOctStrVal: // FALLTHRU
|
||||
case vpiDecStrVal: // FALLTHRU
|
||||
case vpiHexStrVal: // FALLTHRU
|
||||
case vpiStringVal: {
|
||||
new (&m_storage.str) std::string{o.m_storage.str};
|
||||
break;
|
||||
}
|
||||
case vpiVectorVal: {
|
||||
new (&m_storage.vec) std::vector<s_vpi_vecval>{o.m_storage.vec};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerilatedVpiPutHolder(VerilatedVpiPutHolder&& o) noexcept
|
||||
: m_var{std::move(o.m_var)}
|
||||
, m_value{std::move(o.m_value)} {
|
||||
switch (m_value.format) {
|
||||
case vpiBinStrVal: // FALLTHRU
|
||||
case vpiOctStrVal: // FALLTHRU
|
||||
case vpiDecStrVal: // FALLTHRU
|
||||
case vpiHexStrVal: // FALLTHRU
|
||||
case vpiStringVal: {
|
||||
new (&m_storage.str) std::string{std::move(o.m_storage.str)};
|
||||
break;
|
||||
}
|
||||
case vpiVectorVal: {
|
||||
new (&m_storage.vec) std::vector<s_vpi_vecval>{std::move(o.m_storage.vec)};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~VerilatedVpiPutHolder() noexcept {
|
||||
switch (m_value.format) {
|
||||
case vpiBinStrVal: // FALLTHRU
|
||||
case vpiOctStrVal: // FALLTHRU
|
||||
case vpiDecStrVal: // FALLTHRU
|
||||
case vpiHexStrVal: // FALLTHRU
|
||||
case vpiStringVal: m_storage.str.~basic_string(); break;
|
||||
case vpiVectorVal: m_storage.vec.~vector(); break;
|
||||
}
|
||||
}
|
||||
|
||||
VerilatedVpioVar* varp() { return &m_var; }
|
||||
p_vpi_value valuep() { return &m_value; }
|
||||
|
||||
static bool canInertialDelay(p_vpi_value valuep) {
|
||||
switch (valuep->format) {
|
||||
case vpiBinStrVal: // FALLTHRU
|
||||
case vpiOctStrVal: // FALLTHRU
|
||||
case vpiDecStrVal: // FALLTHRU
|
||||
case vpiHexStrVal: // FALLTHRU
|
||||
case vpiStringVal: {
|
||||
if (VL_UNLIKELY(!valuep->value.str)) return false;
|
||||
break;
|
||||
}
|
||||
case vpiScalarVal: // FALLTHRU
|
||||
case vpiIntVal: // FALLTHRU
|
||||
case vpiRealVal: break;
|
||||
case vpiVectorVal: {
|
||||
if (VL_UNLIKELY(!valuep->value.vector)) return false;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct VerilatedVpiTimedCbsCmp final {
|
||||
// Ordering sets keyed by time, then callback unique id
|
||||
bool operator()(const std::pair<QData, uint64_t>& a,
|
||||
@@ -652,9 +795,11 @@ class VerilatedVpiImp final {
|
||||
std::array<VpioCbList, CB_ENUM_MAX_VALUE> m_cbCurrentLists;
|
||||
VpioFutureCbs m_futureCbs; // Time based callbacks for future timestamps
|
||||
VpioFutureCbs m_nextCbs; // cbNextSimTime callbacks
|
||||
std::list<VerilatedVpiPutHolder> m_inertialPuts; // Pending vpi puts due to vpiInertialDelay
|
||||
VerilatedVpiError* m_errorInfop = nullptr; // Container for vpi error info
|
||||
VerilatedAssertOneThread m_assertOne; // Assert only called from single thread
|
||||
uint64_t m_nextCallbackId = 1; // Id to identify callback
|
||||
bool m_evalNeeded = false; // Model has had signals updated via vpi_put_value()
|
||||
|
||||
static VerilatedVpiImp& s() { // Singleton
|
||||
static VerilatedVpiImp s_s;
|
||||
@@ -776,7 +921,7 @@ public:
|
||||
assertOneCheck();
|
||||
VpioCbList& cbObjList = s().m_cbCurrentLists[cbValueChange];
|
||||
bool called = false;
|
||||
std::unordered_set<VerilatedVpioVar*> update; // set of objects to update after callbacks
|
||||
std::set<VerilatedVpioVar*> update; // set of objects to update after callbacks
|
||||
if (cbObjList.empty()) return called;
|
||||
const auto last = std::prev(cbObjList.end()); // prevent looping over newly added elements
|
||||
for (auto it = cbObjList.begin(); true;) {
|
||||
@@ -813,6 +958,17 @@ public:
|
||||
}
|
||||
static void dumpCbs() VL_MT_UNSAFE_ONE;
|
||||
static VerilatedVpiError* error_info() VL_MT_UNSAFE_ONE; // getter for vpi error info
|
||||
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);
|
||||
}
|
||||
static void doInertialPuts() {
|
||||
for (auto& it : s().m_inertialPuts) {
|
||||
vpi_put_value(it.varp()->castVpiHandle(), it.valuep(), nullptr, vpiNoDelay);
|
||||
}
|
||||
s().m_inertialPuts.clear();
|
||||
}
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
@@ -915,6 +1071,11 @@ PLI_INT32 VerilatedVpioReasonCb::dovpi_remove_cb() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void VerilatedVpi::clearEvalNeeded() VL_MT_UNSAFE_ONE { VerilatedVpiImp::evalNeeded(false); }
|
||||
bool VerilatedVpi::evalNeeded() VL_MT_UNSAFE_ONE { return VerilatedVpiImp::evalNeeded(); }
|
||||
|
||||
void VerilatedVpi::doInertialPuts() VL_MT_UNSAFE_ONE { VerilatedVpiImp::doInertialPuts(); }
|
||||
|
||||
//======================================================================
|
||||
// VerilatedVpiImp implementation
|
||||
|
||||
@@ -2194,13 +2355,28 @@ bool vl_check_format(const VerilatedVar* varp, const p_vpi_value valuep, const c
|
||||
return status;
|
||||
}
|
||||
|
||||
static void vl_strprintf(std::string& buffer, char const* fmt, ...) {
|
||||
va_list args, args_copy;
|
||||
va_start(args, fmt);
|
||||
buffer.clear();
|
||||
// Make copy of args since we may need to call VL_VSNPRINTF more than once
|
||||
va_copy(args_copy, args);
|
||||
// Try VL_VSNPRINTF in existing buffer
|
||||
const int result
|
||||
= VL_VSNPRINTF(const_cast<char*>(buffer.data()), buffer.capacity(), fmt, args_copy);
|
||||
va_end(args_copy);
|
||||
const int required = result + 1; // Returned size doesn't include NUL terminator
|
||||
// If there wasn't enough space, reallocate and try again
|
||||
if (buffer.capacity() < required) {
|
||||
buffer.reserve(required * 2);
|
||||
VL_VSNPRINTF(const_cast<char*>(buffer.data()), buffer.capacity(), fmt, args);
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
const char* fullname) {
|
||||
if (!vl_check_format(varp, valuep, fullname, true)) return;
|
||||
// Maximum required size is for binary string, one byte per bit plus null termination
|
||||
static thread_local char t_outStr[VL_VALUE_STRING_MAX_WORDS * VL_EDATASIZE + 1];
|
||||
// cppcheck-suppress variableScope
|
||||
static const thread_local int t_outStrSz = sizeof(t_outStr) - 1;
|
||||
// string data type is dynamic and may vary in size during simulation
|
||||
static thread_local std::string t_outDynamicStr;
|
||||
// We used to presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal
|
||||
@@ -2244,43 +2420,21 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
return;
|
||||
}
|
||||
} else if (valuep->format == vpiBinStrVal) {
|
||||
valuep->value.str = t_outStr;
|
||||
int bits = varp->packed().elements();
|
||||
t_outDynamicStr.resize(bits);
|
||||
const CData* datap = (reinterpret_cast<CData*>(varDatap));
|
||||
int i;
|
||||
if (bits > t_outStrSz) {
|
||||
// limit maximum size of output to size of buffer to prevent overrun.
|
||||
VL_VPI_WARNING_(
|
||||
__FILE__, __LINE__,
|
||||
"%s: Truncating string value of %s for %s"
|
||||
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
|
||||
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
|
||||
VL_VALUE_STRING_MAX_WORDS, bits);
|
||||
bits = t_outStrSz;
|
||||
}
|
||||
for (i = 0; i < bits; ++i) {
|
||||
for (size_t i = 0; i < bits; ++i) {
|
||||
const char val = (datap[i >> 3] >> (i & 7)) & 1;
|
||||
t_outStr[bits - i - 1] = val ? '1' : '0';
|
||||
t_outDynamicStr[bits - i - 1] = val ? '1' : '0';
|
||||
}
|
||||
t_outStr[i] = '\0';
|
||||
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
|
||||
return;
|
||||
} else if (valuep->format == vpiOctStrVal) {
|
||||
valuep->value.str = t_outStr;
|
||||
int chars = (varp->packed().elements() + 2) / 3;
|
||||
t_outDynamicStr.resize(chars);
|
||||
const int bytes = VL_BYTES_I(varp->packed().elements());
|
||||
const CData* datap = (reinterpret_cast<CData*>(varDatap));
|
||||
int i;
|
||||
if (chars > t_outStrSz) {
|
||||
// limit maximum size of output to size of buffer to prevent overrun.
|
||||
VL_VPI_WARNING_(
|
||||
__FILE__, __LINE__,
|
||||
"%s: Truncating string value of %s for %s"
|
||||
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
|
||||
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
|
||||
VL_VALUE_STRING_MAX_WORDS, chars);
|
||||
chars = t_outStrSz;
|
||||
}
|
||||
for (i = 0; i < chars; ++i) {
|
||||
for (size_t i = 0; i < chars; ++i) {
|
||||
const div_t idx = div(i * 3, 8);
|
||||
int val = datap[idx.quot];
|
||||
if ((idx.quot + 1) < bytes) {
|
||||
@@ -2299,46 +2453,31 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
val &= (1 << rem) - 1;
|
||||
}
|
||||
}
|
||||
t_outStr[chars - i - 1] = '0' + (val & 7);
|
||||
t_outDynamicStr[chars - i - 1] = '0' + (val & 7);
|
||||
}
|
||||
t_outStr[i] = '\0';
|
||||
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
|
||||
return;
|
||||
} else if (valuep->format == vpiDecStrVal) {
|
||||
valuep->value.str = t_outStr;
|
||||
// outStrSz does not include nullptr termination so add one
|
||||
if (varp->vltype() == VLVT_UINT8) {
|
||||
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%hhu",
|
||||
static_cast<unsigned char>(*(reinterpret_cast<CData*>(varDatap))));
|
||||
return;
|
||||
vl_strprintf(t_outDynamicStr, "%hhu",
|
||||
static_cast<unsigned char>(*(reinterpret_cast<CData*>(varDatap))));
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%hu",
|
||||
static_cast<unsigned short>(*(reinterpret_cast<SData*>(varDatap))));
|
||||
return;
|
||||
vl_strprintf(t_outDynamicStr, "%hu",
|
||||
static_cast<unsigned short>(*(reinterpret_cast<SData*>(varDatap))));
|
||||
} else if (varp->vltype() == VLVT_UINT32) {
|
||||
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%u",
|
||||
static_cast<unsigned int>(*(reinterpret_cast<IData*>(varDatap))));
|
||||
return;
|
||||
vl_strprintf(t_outDynamicStr, "%u",
|
||||
static_cast<unsigned int>(*(reinterpret_cast<IData*>(varDatap))));
|
||||
} else if (varp->vltype() == VLVT_UINT64) {
|
||||
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%llu",
|
||||
static_cast<unsigned long long>(*(reinterpret_cast<QData*>(varDatap))));
|
||||
return;
|
||||
vl_strprintf(t_outDynamicStr, "%llu",
|
||||
static_cast<unsigned long long>(*(reinterpret_cast<QData*>(varDatap))));
|
||||
}
|
||||
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
|
||||
return;
|
||||
} else if (valuep->format == vpiHexStrVal) {
|
||||
valuep->value.str = t_outStr;
|
||||
int chars = (varp->packed().elements() + 3) >> 2;
|
||||
t_outDynamicStr.resize(chars);
|
||||
const CData* datap = (reinterpret_cast<CData*>(varDatap));
|
||||
int i;
|
||||
if (chars > t_outStrSz) {
|
||||
// limit maximum size of output to size of buffer to prevent overrun.
|
||||
VL_VPI_WARNING_(
|
||||
__FILE__, __LINE__,
|
||||
"%s: Truncating string value of %s for %s"
|
||||
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
|
||||
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
|
||||
VL_VALUE_STRING_MAX_WORDS, chars);
|
||||
chars = t_outStrSz;
|
||||
}
|
||||
for (i = 0; i < chars; ++i) {
|
||||
for (size_t i = 0; i < chars; ++i) {
|
||||
char val = (datap[i >> 1] >> ((i & 1) << 2)) & 15;
|
||||
if (i == (chars - 1)) {
|
||||
// most significant char, mask off nonexistent bits when vector
|
||||
@@ -2349,9 +2488,9 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
val &= (1 << rem) - 1;
|
||||
}
|
||||
}
|
||||
t_outStr[chars - i - 1] = "0123456789abcdef"[static_cast<int>(val)];
|
||||
t_outDynamicStr[chars - i - 1] = "0123456789abcdef"[static_cast<int>(val)];
|
||||
}
|
||||
t_outStr[i] = '\0';
|
||||
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
|
||||
return;
|
||||
} else if (valuep->format == vpiStringVal) {
|
||||
if (varp->vltype() == VLVT_STRING) {
|
||||
@@ -2364,26 +2503,15 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
valuep->value.str = t_outStr;
|
||||
int bytes = VL_BYTES_I(varp->packed().elements());
|
||||
t_outDynamicStr.resize(bytes);
|
||||
const CData* datap = (reinterpret_cast<CData*>(varDatap));
|
||||
int i;
|
||||
if (bytes > t_outStrSz) {
|
||||
// limit maximum size of output to size of buffer to prevent overrun.
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__,
|
||||
"%s: Truncating string value of %s for %s"
|
||||
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than "
|
||||
"required (%d)",
|
||||
__func__, VerilatedVpiError::strFromVpiVal(valuep->format),
|
||||
fullname, t_outStrSz, VL_VALUE_STRING_MAX_WORDS, bytes);
|
||||
bytes = t_outStrSz;
|
||||
}
|
||||
for (i = 0; i < bytes; ++i) {
|
||||
for (size_t i = 0; i < bytes; ++i) {
|
||||
const char val = datap[bytes - i - 1];
|
||||
// other simulators replace [leading?] zero chars with spaces, replicate here.
|
||||
t_outStr[i] = val ? val : ' ';
|
||||
t_outDynamicStr[i] = val ? val : ' ';
|
||||
}
|
||||
t_outStr[i] = '\0';
|
||||
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
|
||||
return;
|
||||
}
|
||||
} else if (valuep->format == vpiIntVal) {
|
||||
@@ -2432,7 +2560,7 @@ void vpi_get_value(vpiHandle object, p_vpi_value valuep) {
|
||||
}
|
||||
|
||||
vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_p*/,
|
||||
PLI_INT32 /*flags*/) {
|
||||
PLI_INT32 flags) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_put_value %p %p\n", object, valuep););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
VL_VPI_ERROR_RESET_();
|
||||
@@ -2440,11 +2568,24 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__, "Ignoring vpi_put_value with nullptr value pointer");
|
||||
return nullptr;
|
||||
}
|
||||
PLI_INT32 delay_mode = flags & 0xfff;
|
||||
if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) {
|
||||
if (delay_mode == vpiInertialDelay) {
|
||||
if (!VerilatedVpiPutHolder::canInertialDelay(valuep)) {
|
||||
VL_VPI_WARNING_(
|
||||
__FILE__, __LINE__,
|
||||
"%s: Unsupported p_vpi_value as requested for '%s' with vpiInertialDelay",
|
||||
__func__, vop->fullname());
|
||||
return nullptr;
|
||||
}
|
||||
VerilatedVpiImp::inertialDelay(vop, valuep);
|
||||
return object;
|
||||
}
|
||||
VL_DEBUG_IF_PLI(
|
||||
VL_DBG_MSGF("- vpi: vpi_put_value name=%s fmt=%d vali=%d\n", vop->fullname(),
|
||||
valuep->format, valuep->value.integer);
|
||||
VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n", vop->varp()->datap(), vop->varDatap()););
|
||||
VerilatedVpiImp::evalNeeded(true);
|
||||
|
||||
if (VL_UNLIKELY(!vop->varp()->isPublicRW())) {
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__,
|
||||
|
||||
@@ -54,6 +54,13 @@ public:
|
||||
static QData cbNextDeadline() VL_MT_UNSAFE_ONE;
|
||||
/// Debug dump of callbacks
|
||||
static void dumpCbs() VL_MT_UNSAFE_ONE;
|
||||
/// Checks VPI dirty state (i.e. whether vpi_put_value() has
|
||||
/// been called since the last clearEvalNeeded())
|
||||
static bool evalNeeded() VL_MT_UNSAFE_ONE;
|
||||
/// Clears VPI dirty state (see evalNeeded())
|
||||
static void clearEvalNeeded() VL_MT_UNSAFE_ONE;
|
||||
/// Perform inertially delayed puts
|
||||
static void doInertialPuts() VL_MT_UNSAFE_ONE;
|
||||
|
||||
// Self test, for internal use only
|
||||
static void selfTest() VL_MT_UNSAFE_ONE;
|
||||
|
||||
@@ -614,8 +614,13 @@ static inline double VL_ROUND(double n) {
|
||||
//=========================================================================
|
||||
// Time and performance
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace VlOs {
|
||||
|
||||
/// Get environment variable
|
||||
extern std::string getenvStr(const std::string& envvar,
|
||||
const std::string& defaultValue) VL_MT_SAFE;
|
||||
extern uint64_t memUsageBytes() VL_MT_SAFE; ///< Return memory usage in bytes, or 0 if unknown
|
||||
|
||||
// Internal: Record CPU time, starting point on construction, and current delta from that
|
||||
|
||||
+30
-4
@@ -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
|
||||
}
|
||||
|
||||
@@ -99,5 +99,31 @@ uint64_t memUsageBytes() VL_MT_SAFE {
|
||||
#endif
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// VlOs::getenvStr implementation
|
||||
|
||||
std::string getenvStr(const std::string& envvar, const std::string& defaultValue) VL_MT_SAFE {
|
||||
std::string ret;
|
||||
#if defined(_MSC_VER)
|
||||
// Note: MinGW does not offer _dupenv_s
|
||||
const char* envvalue = nullptr;
|
||||
_dupenv_s((char**)&envvalue, nullptr, envvar.c_str());
|
||||
if (envvalue != nullptr) {
|
||||
const std::string result{envvalue};
|
||||
free((void*)envvalue);
|
||||
ret = result;
|
||||
} else {
|
||||
ret = defaultValue;
|
||||
}
|
||||
#else
|
||||
if (const char* const envvalue = getenv(envvar.c_str())) {
|
||||
ret = envvalue;
|
||||
} else {
|
||||
ret = defaultValue;
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
} //namespace VlOs
|
||||
|
||||
+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
|
||||
@@ -480,6 +482,7 @@ set_target_properties(${verilator} PROPERTIES
|
||||
MSVC_RUNTIME_LIBRARY MultiThreaded$<IF:$<CONFIG:Release>,,DebugDLL>
|
||||
#JOB_POOL_LINK one_job # Linking takes lots of resources
|
||||
INTERPROCEDURAL_OPTIMIZATION_RELEASE $<IF:MINGW,FALSE,TRUE>
|
||||
INCLUDE_DIRECTORIES ${FLEX_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
add_dependencies(${verilator}
|
||||
@@ -487,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
|
||||
|
||||
@@ -56,6 +56,7 @@ pkgdatadir = @pkgdatadir@
|
||||
# Compile options
|
||||
CFG_WITH_CCWARN = @CFG_WITH_CCWARN@
|
||||
CFG_WITH_DEFENV = @CFG_WITH_DEFENV@
|
||||
CFG_WITH_SOLVER = @CFG_WITH_SOLVER@
|
||||
CPPFLAGS += @CPPFLAGS@
|
||||
CFLAGS += @CFLAGS@
|
||||
CXXFLAGS += @CXXFLAGS@
|
||||
@@ -125,6 +126,13 @@ ifeq ($(CFG_WITH_DEFENV),yes)
|
||||
else
|
||||
CPPFLAGS += -DDEFENV_VERILATOR_ROOT=\"$(VERILATOR_ROOT)\"
|
||||
endif
|
||||
ifneq ($(CFG_WITH_SOLVER),no)
|
||||
CPPFLAGS += -DDEFENV_VERILATOR_SOLVER='"$(CFG_WITH_SOLVER)"'
|
||||
else
|
||||
ifneq ($(VERILATOR_SOLVER),)
|
||||
CPPFLAGS += -DDEFENV_VERILATOR_SOLVER='"$(VERILATOR_SOLVER)"'
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
HEADERS = $(wildcard V*.h v*.h)
|
||||
@@ -276,6 +284,7 @@ RAW_OBJS_PCH_ASTNOMT = \
|
||||
V3ProtectLib.o \
|
||||
V3Randomize.o \
|
||||
V3Reloop.o \
|
||||
V3Sampled.o \
|
||||
V3Sched.o \
|
||||
V3SchedAcyclic.o \
|
||||
V3SchedPartition.o \
|
||||
|
||||
+2
-2
@@ -68,7 +68,7 @@ class ActiveTopVisitor final : public VNVisitor {
|
||||
UINFO(4, " ACTIVE " << nodep << endl);
|
||||
// Remove duplicate clocks and such; sensesp() may change!
|
||||
V3Const::constifyExpensiveEdit(nodep);
|
||||
AstSenTree* const sensesp = nodep->sensesp();
|
||||
AstSenTree* sensesp = nodep->sensesp();
|
||||
UASSERT_OBJ(sensesp, nodep, "nullptr");
|
||||
if (sensesp->sensesp() && sensesp->sensesp()->isNever()) {
|
||||
// Never executing. Kill it.
|
||||
@@ -106,7 +106,7 @@ class ActiveTopVisitor final : public VNVisitor {
|
||||
// There may be other references to same sense tree,
|
||||
// we'll be removing all references when we get to them,
|
||||
// but don't dangle our pointer yet!
|
||||
pushDeletep(sensesp);
|
||||
VL_DO_DANGLING(pushDeletep(sensesp), sensesp);
|
||||
}
|
||||
nodep->sensesp(wantp);
|
||||
}
|
||||
|
||||
+206
-51
@@ -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,13 +327,17 @@ 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);
|
||||
pushDeletep(nodep);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
} else {
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -518,6 +598,81 @@ class AssertVisitor final : public VNVisitor {
|
||||
iterateChildren(nodep);
|
||||
newPslAssertion(nodep, nodep->failsp());
|
||||
}
|
||||
void visit(AstAssertCtl* nodep) override {
|
||||
if (VN_IS(m_modp, Class) || VN_IS(m_modp, Iface)) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: assertcontrols in classes or interfaces");
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
}
|
||||
|
||||
iterateChildren(nodep);
|
||||
|
||||
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);
|
||||
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:
|
||||
case VAssertCtlType::UNLOCK:
|
||||
case VAssertCtlType::PASS_ON:
|
||||
case VAssertCtlType::PASS_OFF:
|
||||
case VAssertCtlType::FAIL_ON:
|
||||
case VAssertCtlType::FAIL_OFF:
|
||||
case VAssertCtlType::NONVACUOUS_ON:
|
||||
case VAssertCtlType::VACUOUS_OFF: {
|
||||
nodep->unlinkFrBack();
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported assertcontrol control_type");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
nodep->unlinkFrBack();
|
||||
nodep->v3warn(EC_ERROR, "Bad assertcontrol control_type (IEEE 1800-2023 Table 20-5)");
|
||||
}
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
void visit(AstAssertIntrinsic* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
newPslAssertion(nodep, nodep->failsp());
|
||||
|
||||
+98
-17
@@ -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,8 +157,17 @@ 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
|
||||
AstVar* const varp = nodep->varp();
|
||||
if (!varp) {
|
||||
// Unused item
|
||||
pushDeletep(nodep->unlinkFrBack());
|
||||
return;
|
||||
}
|
||||
FileLine* const flp = nodep->fileline();
|
||||
V3Const::constifyEdit(nodep->skewp());
|
||||
if (!VN_IS(nodep->skewp(), Const)) {
|
||||
@@ -168,23 +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();
|
||||
// Get a ref to the sampled/driven variable
|
||||
AstVar* const varp = nodep->varp()->unlinkFrBack();
|
||||
m_clockingp->addVarsp(varp);
|
||||
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{
|
||||
@@ -221,9 +259,9 @@ 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:
|
||||
// always queue.push(<sampled var>);
|
||||
@@ -235,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()}};
|
||||
@@ -250,7 +288,7 @@ private:
|
||||
} else {
|
||||
nodep->v3fatal("Invalid direction");
|
||||
}
|
||||
pushDeletep(nodep->unlinkFrBack());
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
void visit(AstDelay* nodep) override {
|
||||
// Only cycle delays are relevant in this stage; also only process once
|
||||
@@ -259,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) {
|
||||
@@ -290,6 +329,8 @@ private:
|
||||
const std::string delayName = m_cycleDlyNames.get(nodep);
|
||||
AstVar* const cntVarp = new AstVar{flp, VVarType::BLOCKTEMP, delayName + "__counter",
|
||||
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
|
||||
cntVarp->lifetime(VLifetime::AUTOMATIC);
|
||||
cntVarp->funcLocal(true);
|
||||
AstBegin* const beginp = new AstBegin{flp, delayName + "__block", cntVarp, false, true};
|
||||
beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, valuep});
|
||||
beginp->addStmtsp(new AstWhile{
|
||||
@@ -309,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()) {
|
||||
@@ -321,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)");
|
||||
}
|
||||
}
|
||||
@@ -488,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;
|
||||
}
|
||||
|
||||
|
||||
+209
-37
@@ -253,17 +253,17 @@ public:
|
||||
, m_final{false}
|
||||
, m_initial{false} {}
|
||||
class Extends {};
|
||||
VBaseOverride(Extends)
|
||||
explicit VBaseOverride(Extends)
|
||||
: m_extends{true}
|
||||
, m_final{false}
|
||||
, m_initial{false} {}
|
||||
class Final {};
|
||||
VBaseOverride(Final)
|
||||
explicit VBaseOverride(Final)
|
||||
: m_extends{false}
|
||||
, m_final{true}
|
||||
, m_initial{false} {}
|
||||
class Initial {};
|
||||
VBaseOverride(Initial)
|
||||
explicit VBaseOverride(Initial)
|
||||
: m_extends{false}
|
||||
, m_final{false}
|
||||
, m_initial{true} {}
|
||||
@@ -330,6 +330,7 @@ public:
|
||||
ILLEGAL,
|
||||
COVERAGE_BLOCK_OFF,
|
||||
HIER_BLOCK,
|
||||
HIER_PARAMS,
|
||||
INLINE_MODULE,
|
||||
NO_INLINE_MODULE,
|
||||
NO_INLINE_TASK,
|
||||
@@ -559,6 +560,7 @@ public:
|
||||
DYNAMIC_TRIGGER_SCHEDULER,
|
||||
FORK_SYNC,
|
||||
PROCESS_REFERENCE,
|
||||
RANDOM_GENERATOR,
|
||||
// Unsigned and two state; fundamental types
|
||||
UINT32,
|
||||
UINT64,
|
||||
@@ -592,6 +594,7 @@ public:
|
||||
"VlDynamicTriggerScheduler",
|
||||
"VlFork",
|
||||
"VlProcessRef",
|
||||
"VlRandomizer",
|
||||
"IData",
|
||||
"QData",
|
||||
"LOGIC_IMPLICIT",
|
||||
@@ -599,20 +602,13 @@ public:
|
||||
return names[m_e];
|
||||
}
|
||||
const char* dpiType() const {
|
||||
static const char* const names[] = {"%E-unk", "svBit",
|
||||
"char", "void*",
|
||||
"char", "int",
|
||||
"%E-integer", "svLogic",
|
||||
"long long", "double",
|
||||
"short", "%E-time",
|
||||
"const char*", "%E-untyped",
|
||||
"dpiScope", "const char*",
|
||||
"%E-mtaskstate", "%E-triggervec",
|
||||
"%E-dly-sched", "%E-trig-sched",
|
||||
"%E-dyn-sched", "%E-fork",
|
||||
"%E-proc-ref", "IData",
|
||||
"QData", "%E-logic-implct",
|
||||
" MAX"};
|
||||
static const char* const names[]
|
||||
= {"%E-unk", "svBit", "char", "void*", "char",
|
||||
"int", "%E-integer", "svLogic", "long long", "double",
|
||||
"short", "%E-time", "const char*", "%E-untyped", "dpiScope",
|
||||
"const char*", "%E-mtaskstate", "%E-triggervec", "%E-dly-sched", "%E-trig-sched",
|
||||
"%E-dyn-sched", "%E-fork", "%E-proc-ref", "%E-rand-gen", "IData",
|
||||
"QData", "%E-logic-implct", " MAX"};
|
||||
return names[m_e];
|
||||
}
|
||||
static void selfTest() {
|
||||
@@ -652,6 +648,7 @@ public:
|
||||
case DYNAMIC_TRIGGER_SCHEDULER: return 0; // opaque
|
||||
case FORK_SYNC: return 0; // opaque
|
||||
case PROCESS_REFERENCE: return 0; // opaque
|
||||
case RANDOM_GENERATOR: return 0; // opaque
|
||||
case UINT32: return 32;
|
||||
case UINT64: return 64;
|
||||
default: return 0;
|
||||
@@ -691,7 +688,8 @@ public:
|
||||
return (m_e == EVENT || m_e == STRING || m_e == SCOPEPTR || m_e == CHARPTR
|
||||
|| m_e == MTASKSTATE || m_e == TRIGGERVEC || m_e == DELAY_SCHEDULER
|
||||
|| m_e == TRIGGER_SCHEDULER || m_e == DYNAMIC_TRIGGER_SCHEDULER || m_e == FORK_SYNC
|
||||
|| m_e == PROCESS_REFERENCE || m_e == DOUBLE || m_e == UNTYPED);
|
||||
|| m_e == PROCESS_REFERENCE || m_e == RANDOM_GENERATOR || m_e == DOUBLE
|
||||
|| m_e == UNTYPED);
|
||||
}
|
||||
bool isDouble() const VL_MT_SAFE { return m_e == DOUBLE; }
|
||||
bool isEvent() const { return m_e == EVENT; }
|
||||
@@ -790,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;
|
||||
}
|
||||
@@ -903,7 +902,7 @@ public:
|
||||
TRIWIRE,
|
||||
TRI0,
|
||||
TRI1,
|
||||
PORT, // Temp type used in parser only
|
||||
PORT, // Used in parser and V3Fork to recognize ports
|
||||
BLOCKTEMP,
|
||||
MODULETEMP,
|
||||
STMTTEMP,
|
||||
@@ -1102,6 +1101,156 @@ constexpr bool operator==(VAlwaysKwd::en lhs, const VAlwaysKwd& rhs) { return lh
|
||||
|
||||
// ######################################################################
|
||||
|
||||
class VAssertCtlType final {
|
||||
public:
|
||||
// IEEE 1800-2023 Table 20-5
|
||||
enum en : uint8_t {
|
||||
_TO_BE_EVALUATED = 0,
|
||||
LOCK = 1,
|
||||
UNLOCK = 2,
|
||||
ON = 3,
|
||||
OFF = 4,
|
||||
KILL = 5,
|
||||
PASS_ON = 6,
|
||||
PASS_OFF = 7,
|
||||
FAIL_ON = 8,
|
||||
FAIL_OFF = 9,
|
||||
NONVACUOUS_ON = 10,
|
||||
VACUOUS_OFF = 11
|
||||
};
|
||||
enum en m_e;
|
||||
VAssertCtlType()
|
||||
: m_e{_TO_BE_EVALUATED} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
constexpr VAssertCtlType(en _e)
|
||||
: m_e{_e} {}
|
||||
explicit VAssertCtlType(int _e)
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
constexpr operator en() const { return m_e; }
|
||||
const char* ascii() const {
|
||||
// IEEE 1800-2023 20.11
|
||||
static const char* const names[] = {"",
|
||||
"",
|
||||
"",
|
||||
"$asserton",
|
||||
"$assertoff",
|
||||
"$assertkill",
|
||||
"$assertpasson",
|
||||
"$assertpassoff",
|
||||
"$assertfailon",
|
||||
"$assertfailoff",
|
||||
"$assertnonvacuouson",
|
||||
"$assertvacuousoff"};
|
||||
return names[m_e];
|
||||
}
|
||||
};
|
||||
constexpr bool operator==(const VAssertCtlType& lhs, const VAssertCtlType& rhs) {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
}
|
||||
constexpr bool operator==(const VAssertCtlType& lhs, VAssertCtlType::en rhs) {
|
||||
return lhs.m_e == rhs;
|
||||
}
|
||||
constexpr bool operator==(VAssertCtlType::en lhs, const VAssertCtlType& rhs) {
|
||||
return lhs == rhs.m_e;
|
||||
}
|
||||
|
||||
// ######################################################################
|
||||
|
||||
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 };
|
||||
@@ -1441,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
|
||||
@@ -1532,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
|
||||
@@ -2024,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);
|
||||
@@ -2355,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.
|
||||
@@ -2473,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
|
||||
@@ -2491,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*)', "
|
||||
@@ -2502,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*)', "
|
||||
@@ -2513,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*)', "
|
||||
@@ -2524,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*)', "
|
||||
@@ -2539,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*)', "
|
||||
@@ -2550,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*)', "
|
||||
@@ -2564,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*)', "
|
||||
@@ -2575,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) {
|
||||
|
||||
+53
-17
@@ -89,8 +89,8 @@ public:
|
||||
virtual AstNodeDType* subDTypep() const VL_MT_SAFE { return nullptr; }
|
||||
virtual bool isFourstate() const;
|
||||
// Ideally an IEEE $typename
|
||||
virtual string prettyDTypeName() const { return prettyTypeName(); }
|
||||
string prettyDTypeNameQ() const { return "'" + prettyDTypeName() + "'"; }
|
||||
virtual string prettyDTypeName(bool) const { return prettyTypeName(); }
|
||||
string prettyDTypeNameQ() const { return "'" + prettyDTypeName(false) + "'"; }
|
||||
//
|
||||
// Changing the width may confuse the data type resolution, so must clear
|
||||
// TypeTable cache after use.
|
||||
@@ -215,9 +215,11 @@ public:
|
||||
int uniqueNum() const { return m_uniqueNum; }
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string prettyDTypeName(bool) const override;
|
||||
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)
|
||||
@@ -313,7 +315,7 @@ public:
|
||||
return asamep->subDTypep()
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
||||
}
|
||||
string prettyDTypeName() const override;
|
||||
string prettyDTypeName(bool full) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* getChild2DTypep() const override { return keyChildDTypep(); }
|
||||
@@ -387,7 +389,7 @@ public:
|
||||
return type() == samep->type() && same(samep);
|
||||
}
|
||||
string name() const override VL_MT_STABLE { return m.m_keyword.ascii(); }
|
||||
string prettyDTypeName() const override;
|
||||
string prettyDTypeName(bool full) const override;
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(dtypep() != this);
|
||||
return nullptr;
|
||||
@@ -428,6 +430,9 @@ public:
|
||||
bool isDynamicTriggerScheduler() const VL_MT_SAFE {
|
||||
return keyword() == VBasicDTypeKwd::DYNAMIC_TRIGGER_SCHEDULER;
|
||||
}
|
||||
bool isRandomGenerator() const VL_MT_SAFE {
|
||||
return keyword() == VBasicDTypeKwd::RANDOM_GENERATOR;
|
||||
}
|
||||
bool isOpaque() const VL_MT_SAFE { return keyword().isOpaque(); }
|
||||
bool isString() const VL_MT_SAFE { return keyword().isString(); }
|
||||
bool isZeroInit() const { return keyword().isZeroInit(); }
|
||||
@@ -501,7 +506,7 @@ public:
|
||||
}
|
||||
bool similarDType(const AstNodeDType* samep) const override { return same(samep); }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
string prettyDTypeName() const override { return m_name; }
|
||||
string prettyDTypeName(bool) const override { return m_name; }
|
||||
// METHODS
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||
@@ -548,6 +553,7 @@ public:
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
string prettyDTypeName(bool full) const override;
|
||||
string name() const override VL_MT_STABLE;
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||
@@ -716,7 +722,7 @@ public:
|
||||
return asamep->subDTypep()
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
||||
}
|
||||
string prettyDTypeName() const override;
|
||||
string prettyDTypeName(bool full) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE {
|
||||
@@ -806,6 +812,7 @@ public:
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
string prettyDTypeName(bool full) const override;
|
||||
// METHODS
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep()->basicp(); }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return subDTypep()->skipRefp(); }
|
||||
@@ -814,9 +821,9 @@ public:
|
||||
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); }
|
||||
int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); }
|
||||
int itemCount() const {
|
||||
size_t itemCount() const {
|
||||
size_t count = 0;
|
||||
for (AstNode* itemp = itemsp(); itemp; itemp = itemp->nextp()) count++;
|
||||
for (AstNode* itemp = itemsp(); itemp; itemp = itemp->nextp()) ++count;
|
||||
return count;
|
||||
}
|
||||
bool isCompound() const override { return false; }
|
||||
@@ -869,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; }
|
||||
@@ -950,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 {
|
||||
@@ -959,6 +966,30 @@ public:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
class AstNBACommitQueueDType final : public AstNodeDType {
|
||||
// @astgen ptr := m_subDTypep : AstNodeDType // Type of the corresponding variable
|
||||
const bool m_partial; // Partial element update required
|
||||
|
||||
public:
|
||||
AstNBACommitQueueDType(FileLine* fl, AstNodeDType* subDTypep, bool partial)
|
||||
: ASTGEN_SUPER_NBACommitQueueDType(fl)
|
||||
, m_partial{partial}
|
||||
, m_subDTypep{subDTypep} {
|
||||
dtypep(this);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstNBACommitQueueDType;
|
||||
|
||||
AstNodeDType* subDTypep() const override { return m_subDTypep; }
|
||||
bool partial() const { return m_partial; }
|
||||
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
|
||||
AstBasicDType* basicp() const override { return nullptr; }
|
||||
AstNodeDType* skipRefp() const override { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
int widthAlignBytes() const override { return 1; }
|
||||
int widthTotalBytes() const override { return 24; }
|
||||
bool isCompound() const override { return true; }
|
||||
};
|
||||
class AstParamTypeDType final : public AstNodeDType {
|
||||
// Parents: MODULE
|
||||
// A parameter type statement; much like a var or typedef
|
||||
@@ -1066,7 +1097,7 @@ public:
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
||||
}
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
string prettyDTypeName() const override;
|
||||
string prettyDTypeName(bool full) const override;
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE {
|
||||
return m_refDTypep ? m_refDTypep : childDTypep();
|
||||
@@ -1129,8 +1160,8 @@ public:
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
string prettyDTypeName() const override {
|
||||
return subDTypep() ? prettyName(subDTypep()->name()) : prettyName();
|
||||
string prettyDTypeName(bool full) const override {
|
||||
return subDTypep() ? prettyName(subDTypep()->prettyDTypeName(full)) : prettyName();
|
||||
}
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE {
|
||||
return subDTypep() ? subDTypep()->basicp() : nullptr;
|
||||
@@ -1262,6 +1293,11 @@ public:
|
||||
refDTypep(nullptr);
|
||||
dtypep(nullptr); // V3Width will resolve
|
||||
}
|
||||
AstUnsizedArrayDType(FileLine* fl, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_UnsizedArrayDType(fl) {
|
||||
refDTypep(dtp);
|
||||
dtypep(nullptr); // V3Width will resolve
|
||||
}
|
||||
ASTGEN_MEMBERS_AstUnsizedArrayDType;
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(!((m_refDTypep && !childDTypep()) || (!m_refDTypep && childDTypep())));
|
||||
@@ -1357,7 +1393,7 @@ public:
|
||||
inline AstPackArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstRange* rangep);
|
||||
inline AstPackArrayDType(FileLine* fl, AstNodeDType* dtp, AstRange* rangep);
|
||||
ASTGEN_MEMBERS_AstPackArrayDType;
|
||||
string prettyDTypeName() const override;
|
||||
string prettyDTypeName(bool full) const override;
|
||||
bool isCompound() const override { return false; }
|
||||
};
|
||||
class AstUnpackArrayDType final : public AstNodeArrayDType {
|
||||
@@ -1384,7 +1420,7 @@ public:
|
||||
widthFromSub(subDTypep());
|
||||
}
|
||||
ASTGEN_MEMBERS_AstUnpackArrayDType;
|
||||
string prettyDTypeName() const override;
|
||||
string prettyDTypeName(bool full) const override;
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstUnpackArrayDType* const sp = VN_DBG_AS(samep, UnpackArrayDType);
|
||||
return m_isCompound == sp->m_isCompound;
|
||||
|
||||
+187
-81
@@ -56,6 +56,7 @@ public:
|
||||
virtual string emitVerilog() = 0; /// Format string for verilog writing; see V3EmitV
|
||||
// For documentation on emitC format see EmitCFunc::emitOpName
|
||||
virtual string emitC() = 0;
|
||||
virtual string emitSMT() const { V3ERROR_NA_RETURN(""); };
|
||||
virtual string emitSimpleOperator() { return ""; } // "" means not ok to use
|
||||
virtual bool emitCheckMaxWords() { return false; } // Check VL_MULS_MAX_WORDS
|
||||
virtual bool cleanOut() const = 0; // True if output has extra upper bits zero
|
||||
@@ -212,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(""); }
|
||||
@@ -419,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; }
|
||||
@@ -808,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]
|
||||
@@ -871,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, '{}
|
||||
@@ -1062,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
|
||||
@@ -1530,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
|
||||
|
||||
@@ -1538,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(""); }
|
||||
@@ -1562,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;
|
||||
@@ -1743,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; }
|
||||
@@ -1846,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(""); }
|
||||
@@ -2134,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"; }
|
||||
@@ -2172,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
|
||||
@@ -2259,7 +2320,7 @@ class AstWith final : public AstNodeExpr {
|
||||
// @astgen op3 := exprp : List[AstNode]
|
||||
public:
|
||||
AstWith(FileLine* fl, AstLambdaArgRef* indexArgRefp, AstLambdaArgRef* valueArgRefp,
|
||||
AstNodeExpr* exprp)
|
||||
AstNode* exprp)
|
||||
: ASTGEN_SUPER_With(fl) {
|
||||
this->indexArgRefp(indexArgRefp);
|
||||
this->valueArgRefp(valueArgRefp);
|
||||
@@ -2282,13 +2343,13 @@ class AstWithParse final : public AstNodeExpr {
|
||||
// Replaced with AstWith
|
||||
// Parents: expr|stmt
|
||||
// Children: funcref, expr
|
||||
// @astgen op1 := funcrefp : AstNode
|
||||
// @astgen op2 := exprp : Optional[AstNodeExpr]
|
||||
// @astgen op1 := funcrefp : AstNodeExpr
|
||||
// @astgen op2 := exprsp : List[AstNode]
|
||||
public:
|
||||
AstWithParse(FileLine* fl, AstNode* funcrefp, AstNodeExpr* exprp)
|
||||
AstWithParse(FileLine* fl, AstNodeExpr* funcrefp, AstNode* exprsp)
|
||||
: ASTGEN_SUPER_WithParse(fl) {
|
||||
this->funcrefp(funcrefp);
|
||||
this->exprp(exprp);
|
||||
this->addExprsp(exprsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstWithParse;
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
@@ -2401,6 +2462,7 @@ public:
|
||||
out.opConcat(lhs, rhs);
|
||||
}
|
||||
string emitC() override { return "VL_CONCAT_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(concat %l %r)"; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
@@ -2447,6 +2509,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f/ %r)"; }
|
||||
string emitC() override { return "VL_DIV_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvudiv %l %r)"; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
@@ -2493,6 +2556,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f/ %r)"; }
|
||||
string emitC() override { return "VL_DIVS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvsdiv %l %r)"; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
@@ -2519,6 +2583,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f==? %r)"; }
|
||||
string emitC() override { return "VL_EQ_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(= %l %r)"; }
|
||||
string emitSimpleOperator() override { return "=="; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -2641,6 +2706,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f> %r)"; }
|
||||
string emitC() override { return "VL_GT_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvugt %l %r)"; }
|
||||
string emitSimpleOperator() override { return ">"; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -2711,6 +2777,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f> %r)"; }
|
||||
string emitC() override { return "VL_GTS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvsgt %l %r)"; }
|
||||
string emitSimpleOperator() override { return ""; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -2734,6 +2801,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f>= %r)"; }
|
||||
string emitC() override { return "VL_GTE_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvuge %l %r)"; }
|
||||
string emitSimpleOperator() override { return ">="; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -2804,6 +2872,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f>= %r)"; }
|
||||
string emitC() override { return "VL_GTES_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvsge %l %r)"; }
|
||||
string emitSimpleOperator() override { return ""; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -2827,6 +2896,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f&& %r)"; }
|
||||
string emitC() override { return "VL_LOGAND_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(and %l %r)"; }
|
||||
string emitSimpleOperator() override { return "&&"; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -2850,6 +2920,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f-> %r)"; }
|
||||
string emitC() override { return "VL_LOGIF_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(=> %l %r)"; }
|
||||
string emitSimpleOperator() override { return "->"; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -2873,6 +2944,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f|| %r)"; }
|
||||
string emitC() override { return "VL_LOGOR_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(or %l %r)"; }
|
||||
string emitSimpleOperator() override { return "||"; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -2896,6 +2968,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f< %r)"; }
|
||||
string emitC() override { return "VL_LT_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvult %l %r)"; }
|
||||
string emitSimpleOperator() override { return "<"; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -2966,6 +3039,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f< %r)"; }
|
||||
string emitC() override { return "VL_LTS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvslt %l %r)"; }
|
||||
string emitSimpleOperator() override { return ""; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -2989,6 +3063,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f<= %r)"; }
|
||||
string emitC() override { return "VL_LTE_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvule %l %r)"; }
|
||||
string emitSimpleOperator() override { return "<="; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -3059,6 +3134,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f<= %r)"; }
|
||||
string emitC() override { return "VL_LTES_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvsle %l %r)"; }
|
||||
string emitSimpleOperator() override { return ""; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -3082,6 +3158,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f%% %r)"; }
|
||||
string emitC() override { return "VL_MODDIV_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvurem %l %r)"; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
@@ -3104,6 +3181,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f%% %r)"; }
|
||||
string emitC() override { return "VL_MODDIVS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvsmod %l %r)"; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
@@ -3277,6 +3355,9 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%f{%r{%k%l}}"; }
|
||||
string emitC() override { return "VL_REPLICATE_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override {
|
||||
return "((_ repeat " + cvtToStr(width() / lhsp()->width()) + ") %l)";
|
||||
}
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
@@ -3325,6 +3406,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f<< %r)"; }
|
||||
string emitC() override { return "VL_SHIFTL_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvshl %l %r)"; }
|
||||
string emitSimpleOperator() override {
|
||||
return (rhsp()->isWide() || rhsp()->isQuad()) ? "" : "<<";
|
||||
}
|
||||
@@ -3373,6 +3455,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f>> %r)"; }
|
||||
string emitC() override { return "VL_SHIFTR_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvlshr %l %r)"; }
|
||||
string emitSimpleOperator() override {
|
||||
return (rhsp()->isWide() || rhsp()->isQuad()) ? "" : ">>";
|
||||
}
|
||||
@@ -3425,6 +3508,7 @@ public:
|
||||
out.opShiftRS(lhs, rhs, lhsp()->widthMinV());
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f>>> %r)"; }
|
||||
string emitSMT() const override { return "(bvashr %l %r)"; }
|
||||
string emitC() override { return "VL_SHIFTRS_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
||||
string emitSimpleOperator() override { return ""; }
|
||||
bool cleanOut() const override { return false; }
|
||||
@@ -3476,6 +3560,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f- %r)"; }
|
||||
string emitC() override { return "VL_SUB_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvsub %l %r)"; }
|
||||
string emitSimpleOperator() override { return "-"; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return false; }
|
||||
@@ -3552,6 +3637,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f== %r)"; }
|
||||
string emitC() override { return "VL_EQ_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(= %l %r)"; }
|
||||
string emitSimpleOperator() override { return "=="; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -3668,6 +3754,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f<-> %r)"; }
|
||||
string emitC() override { return "VL_LOGEQ_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvxnor %l %r)"; }
|
||||
string emitSimpleOperator() override { return "<->"; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -3692,6 +3779,7 @@ public:
|
||||
string emitVerilog() override { return "%k(%l %f!= %r)"; }
|
||||
string emitC() override { return "VL_NEQ_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSimpleOperator() override { return "!="; }
|
||||
string emitSMT() const override { return "(not (= %l %r))"; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
@@ -3809,6 +3897,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f+ %r)"; }
|
||||
string emitC() override { return "VL_ADD_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvadd %l %r)"; }
|
||||
string emitSimpleOperator() override { return "+"; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return false; }
|
||||
@@ -3855,6 +3944,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f& %r)"; }
|
||||
string emitC() override { return "VL_AND_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvand %l %r)"; }
|
||||
string emitSimpleOperator() override { return "&"; }
|
||||
bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
|
||||
bool cleanLhs() const override { return false; }
|
||||
@@ -3878,6 +3968,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f* %r)"; }
|
||||
string emitC() override { return "VL_MUL_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvmul %l %r)"; }
|
||||
string emitSimpleOperator() override { return "*"; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -3950,6 +4041,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f| %r)"; }
|
||||
string emitC() override { return "VL_OR_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvor %l %r)"; }
|
||||
string emitSimpleOperator() override { return "|"; }
|
||||
bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
|
||||
bool cleanLhs() const override { return false; }
|
||||
@@ -3973,6 +4065,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f^ %r)"; }
|
||||
string emitC() override { return "VL_XOR_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvxor %l %r)"; }
|
||||
string emitSimpleOperator() override { return "^"; }
|
||||
bool cleanOut() const override { return false; } // Lclean && Rclean
|
||||
bool cleanLhs() const override { return false; }
|
||||
@@ -4164,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; }
|
||||
};
|
||||
@@ -4185,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; }
|
||||
};
|
||||
@@ -4406,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
|
||||
@@ -4427,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 ===
|
||||
@@ -4586,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; }
|
||||
@@ -4895,6 +4989,9 @@ public:
|
||||
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAssign(lhs); }
|
||||
string emitVerilog() override { return "%l"; }
|
||||
string emitC() override { return "VL_EXTEND_%nq%lq(%nw,%lw, %P, %li)"; }
|
||||
string emitSMT() const override {
|
||||
return "((_ zero_extend " + cvtToStr(width() - lhsp()->width()) + ") %l)";
|
||||
}
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override {
|
||||
@@ -4918,6 +5015,9 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%l"; }
|
||||
string emitC() override { return "VL_EXTENDS_%nq%lq(%nw,%lw, %P, %li)"; }
|
||||
string emitSMT() const override {
|
||||
return "((_ sign_extend " + cvtToStr(width() - lhsp()->width()) + ") %l)";
|
||||
}
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override {
|
||||
@@ -5050,6 +5150,7 @@ public:
|
||||
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opLogNot(lhs); }
|
||||
string emitVerilog() override { return "%f(! %l)"; }
|
||||
string emitC() override { return "VL_LOGNOT_%nq%lq(%nw,%lw, %P, %li)"; }
|
||||
string emitSMT() const override { return "(not %l)"; }
|
||||
string emitSimpleOperator() override { return "!"; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -5080,6 +5181,7 @@ public:
|
||||
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opNegate(lhs); }
|
||||
string emitVerilog() override { return "%f(- %l)"; }
|
||||
string emitC() override { return "VL_NEGATE_%lq(%lW, %P, %li)"; }
|
||||
string emitSMT() const override { return "(bvneg %l)"; }
|
||||
string emitSimpleOperator() override { return "-"; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return false; }
|
||||
@@ -5112,6 +5214,7 @@ public:
|
||||
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opNot(lhs); }
|
||||
string emitVerilog() override { return "%f(~ %l)"; }
|
||||
string emitC() override { return "VL_NOT_%lq(%lW, %P, %li)"; }
|
||||
string emitSMT() const override { return "(bvnot %l)"; }
|
||||
string emitSimpleOperator() override { return "~"; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return false; }
|
||||
@@ -5313,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()
|
||||
@@ -5574,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;
|
||||
@@ -5603,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; }
|
||||
|
||||
+167
-92
@@ -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
|
||||
@@ -956,6 +969,7 @@ class AstClockingItem final : public AstNode {
|
||||
// @astgen op2 := exprp : Optional[AstNodeExpr]
|
||||
// @astgen op3 := assignp : Optional[AstAssign]
|
||||
// @astgen op4 := varp : Optional[AstVar]
|
||||
// @astgen ptr := m_outputp : Optional[AstClockingItem]
|
||||
VDirection m_direction;
|
||||
|
||||
public:
|
||||
@@ -971,6 +985,9 @@ public:
|
||||
}
|
||||
ASTGEN_MEMBERS_AstClockingItem;
|
||||
VDirection direction() const { return m_direction; }
|
||||
AstClockingItem* outputp() const { return m_outputp; }
|
||||
void outputp(AstClockingItem* outputp) { m_outputp = outputp; }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
};
|
||||
class AstConstPool final : public AstNode {
|
||||
// Container for const static data
|
||||
@@ -1018,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; }
|
||||
@@ -1055,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.
|
||||
@@ -1102,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
|
||||
@@ -1767,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)
|
||||
@@ -1790,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
|
||||
@@ -1809,6 +1808,7 @@ class AstVar final : public AstNode {
|
||||
bool m_trace : 1; // Trace this variable
|
||||
bool m_isLatched : 1; // Not assigned in all control paths of combo always
|
||||
bool m_isForceable : 1; // May be forced/released externally from user C code
|
||||
bool m_isForcedByCode : 1; // May be forced/released from AstAssignForce/AstRelease
|
||||
bool m_isWrittenByDpi : 1; // This variable can be written by a DPI Export
|
||||
bool m_isWrittenBySuspendable : 1; // This variable can be written by a suspendable process
|
||||
|
||||
@@ -1835,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;
|
||||
@@ -1854,6 +1852,7 @@ class AstVar final : public AstNode {
|
||||
m_trace = false;
|
||||
m_isLatched = false;
|
||||
m_isForceable = false;
|
||||
m_isForcedByCode = false;
|
||||
m_isWrittenByDpi = false;
|
||||
m_isWrittenBySuspendable = false;
|
||||
m_attrClocker = VVarAttrClocker::CLOCKER_UNKNOWN;
|
||||
@@ -1960,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; }
|
||||
@@ -1976,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; }
|
||||
@@ -2009,6 +2004,8 @@ public:
|
||||
void isLatched(bool flag) { m_isLatched = flag; }
|
||||
bool isForceable() const { return m_isForceable; }
|
||||
void setForceable() { m_isForceable = true; }
|
||||
void setForcedByCode() { m_isForcedByCode = true; }
|
||||
bool isForced() const { return m_isForceable || m_isForcedByCode; }
|
||||
bool isWrittenByDpi() const { return m_isWrittenByDpi; }
|
||||
void setWrittenByDpi() { m_isWrittenByDpi = true; }
|
||||
bool isWrittenBySuspendable() const { return m_isWrittenBySuspendable; }
|
||||
@@ -2022,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(); }
|
||||
@@ -2059,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;
|
||||
@@ -2066,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; }
|
||||
@@ -2084,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; }
|
||||
@@ -2183,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; }
|
||||
@@ -2338,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)
|
||||
@@ -2434,13 +2470,11 @@ public:
|
||||
ASTGEN_MEMBERS_AstAlwaysObserved;
|
||||
};
|
||||
class AstAlwaysPost final : public AstNodeProcedure {
|
||||
// Like always but post assignments for memory assignment IFs
|
||||
// @astgen op1 := sensesp : Optional[AstSenTree] // Sensitivity list iff clocked
|
||||
// Like always but 'post' scheduled, e.g. for array NBA commits
|
||||
|
||||
public:
|
||||
AstAlwaysPost(FileLine* fl, AstSenTree* sensesp, AstNode* stmtsp)
|
||||
: ASTGEN_SUPER_AlwaysPost(fl, stmtsp) {
|
||||
this->sensesp(sensesp);
|
||||
}
|
||||
explicit AstAlwaysPost(FileLine* fl)
|
||||
: ASTGEN_SUPER_AlwaysPost(fl, nullptr) {}
|
||||
ASTGEN_MEMBERS_AstAlwaysPost;
|
||||
};
|
||||
class AstAlwaysPostponed final : public AstNodeProcedure {
|
||||
@@ -2580,6 +2614,37 @@ public:
|
||||
bool isJustOneBodyStmt() const { return stmtsp() && !stmtsp()->nextp(); }
|
||||
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
|
||||
};
|
||||
class AstAssertCtl final : public AstNodeStmt {
|
||||
// @astgen op1 := controlTypep : 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 (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* assertTypesp = nullptr,
|
||||
AstNodeExpr* directiveTypep = nullptr, AstNodeExpr* levelp = nullptr,
|
||||
AstNodeExpr* itemsp = nullptr);
|
||||
ASTGEN_MEMBERS_AstAssertCtl;
|
||||
string verilogKwd() const override { return m_ctlType.ascii(); }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() override { return false; }
|
||||
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;
|
||||
};
|
||||
class AstBreak final : public AstNodeStmt {
|
||||
public:
|
||||
explicit AstBreak(FileLine* fl)
|
||||
@@ -2657,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
|
||||
@@ -2716,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; }
|
||||
@@ -2755,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 {
|
||||
@@ -2852,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; }
|
||||
@@ -2990,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)
|
||||
@@ -3005,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
|
||||
@@ -3196,6 +3266,7 @@ public:
|
||||
this->exprp(exprp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstStmtExpr;
|
||||
bool isPure() override { return exprp()->isPure(); }
|
||||
};
|
||||
class AstStop final : public AstNodeStmt {
|
||||
public:
|
||||
@@ -3348,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; }
|
||||
@@ -3430,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 ===
|
||||
@@ -3588,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);
|
||||
}
|
||||
};
|
||||
@@ -3602,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);
|
||||
}
|
||||
};
|
||||
@@ -3611,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 ===
|
||||
@@ -3761,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});
|
||||
}
|
||||
|
||||
+142
-27
@@ -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) {
|
||||
@@ -788,6 +814,22 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe
|
||||
info.m_type = "VlUnpacked<" + sub.m_type;
|
||||
info.m_type += ", " + cvtToStr(adtypep->declRange().elements());
|
||||
info.m_type += ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, NBACommitQueueDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
compound = true;
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(compound, false);
|
||||
AstNodeDType* eDTypep = adtypep->subDTypep();
|
||||
unsigned rank = 0;
|
||||
while (AstUnpackArrayDType* const uaDTypep = VN_CAST(eDTypep, UnpackArrayDType)) {
|
||||
eDTypep = uaDTypep->subDTypep()->skipRefp();
|
||||
++rank;
|
||||
}
|
||||
info.m_type = "VlNBACommitQueue<";
|
||||
info.m_type += sub.m_type;
|
||||
info.m_type += adtypep->partial() ? ", true" : ", false";
|
||||
info.m_type += ", " + eDTypep->cTypeRecurse(compound, false).m_type;
|
||||
info.m_type += ", " + std::to_string(rank);
|
||||
info.m_type += ">";
|
||||
} else if (packed && (VN_IS(dtypep, PackArrayDType))) {
|
||||
const AstPackArrayDType* const adtypep = VN_CAST(dtypep, PackArrayDType);
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(false, true);
|
||||
@@ -827,6 +869,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe
|
||||
info.m_type = "VlForkSync";
|
||||
} else if (bdtypep->isProcessRef()) {
|
||||
info.m_type = "VlProcessRef";
|
||||
} else if (bdtypep->isRandomGenerator()) {
|
||||
info.m_type = "VlRandomizer";
|
||||
} else if (bdtypep->isEvent()) {
|
||||
info.m_type = v3Global.assignsEvents() ? "VlAssignableEvent" : "VlEvent";
|
||||
} else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width
|
||||
@@ -885,9 +929,10 @@ std::pair<uint32_t, uint32_t> AstNodeDType::dimensions(bool includeBasic) {
|
||||
}
|
||||
dtypep = adtypep->subDTypep();
|
||||
continue;
|
||||
} else if (const AstQueueDType* const qdtypep = VN_CAST(dtypep, QueueDType)) {
|
||||
} else if (VN_IS(dtypep, QueueDType) || VN_IS(dtypep, DynArrayDType)
|
||||
|| VN_IS(dtypep, AssocArrayDType) || VN_IS(dtypep, WildcardArrayDType)) {
|
||||
unpacked++;
|
||||
dtypep = qdtypep->subDTypep();
|
||||
dtypep = dtypep->subDTypep();
|
||||
continue;
|
||||
} else if (const AstBasicDType* const adtypep = VN_CAST(dtypep, BasicDType)) {
|
||||
if (includeBasic && (adtypep->isRanged() || adtypep->isString())) packed++;
|
||||
@@ -959,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();
|
||||
@@ -1442,7 +1497,29 @@ void AstAlways::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStr(str, "keyword", keyword().ascii());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
|
||||
AstAssertCtl::AstAssertCtl(FileLine* fl, VAssertCtlType ctlType, AstNodeExpr*, AstNodeExpr*)
|
||||
: ASTGEN_SUPER_AssertCtl(fl)
|
||||
, m_ctlType{ctlType} {
|
||||
controlTypep(new AstConst{fl, ctlType});
|
||||
}
|
||||
AstAssertCtl::AstAssertCtl(FileLine* fl, AstNodeExpr* controlTypep, AstNodeExpr* assertTypesp,
|
||||
AstNodeExpr* directiveTypep, AstNodeExpr*, AstNodeExpr*)
|
||||
: ASTGEN_SUPER_AssertCtl(fl)
|
||||
, m_ctlType{VAssertCtlType::_TO_BE_EVALUATED}
|
||||
, m_assertTypes{VAssertType::INTERNAL}
|
||||
, m_directiveTypes{VAssertDirectiveType::INTERNAL} {
|
||||
this->controlTypep(controlTypep);
|
||||
this->assertTypesp(assertTypesp);
|
||||
this->directiveTypesp(directiveTypep);
|
||||
}
|
||||
void AstAssertCtl::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " [" << ctlType().ascii() << "]";
|
||||
}
|
||||
void AstAssertCtl::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStr(str, "ctlType", ctlType().ascii());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstAttrOf::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " [" << attrType().ascii() << "]";
|
||||
@@ -1463,7 +1540,7 @@ void AstBasicDType::dumpJson(std::ostream& str) const {
|
||||
}
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
string AstBasicDType::prettyDTypeName() const {
|
||||
string AstBasicDType::prettyDTypeName(bool) const {
|
||||
std::ostringstream os;
|
||||
os << keyword().ascii();
|
||||
if (isRanged() && !rangep() && keyword().width() <= 1) {
|
||||
@@ -1588,13 +1665,14 @@ void AstClassRefDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "class:" << name();
|
||||
}
|
||||
string AstClassRefDType::prettyDTypeName(bool) const { return "class{}"s + prettyName(); }
|
||||
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 {
|
||||
@@ -1624,6 +1702,22 @@ void AstEnumDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "enum";
|
||||
}
|
||||
string AstEnumDType::prettyDTypeName(bool full) const {
|
||||
string result = "enum{";
|
||||
if (full) { // else shorten for error messages
|
||||
for (AstEnumItem* itemp = itemsp(); itemp; itemp = VN_AS(itemp->nextp(), EnumItem)) {
|
||||
result += itemp->prettyName() + "=";
|
||||
if (AstConst* constp = VN_CAST(itemp->valuep(), Const)) {
|
||||
result += constp->num().ascii(true, true);
|
||||
} else {
|
||||
result += "?";
|
||||
}
|
||||
result += ";";
|
||||
}
|
||||
}
|
||||
result += "}" + prettyName();
|
||||
return result;
|
||||
}
|
||||
void AstEnumItemRef::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
str << " -> ";
|
||||
@@ -1765,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())
|
||||
@@ -1938,6 +2039,18 @@ void AstNodeUOrStructDType::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFunc(str, isFourstate);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
string AstNodeUOrStructDType::prettyDTypeName(bool full) const {
|
||||
string result = verilogKwd() + "{";
|
||||
if (full) { // else shorten for errors
|
||||
for (AstMemberDType* itemp = membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
result += itemp->subDTypep()->prettyDTypeName(full);
|
||||
result += " " + itemp->prettyName() + ";";
|
||||
}
|
||||
}
|
||||
result += "}" + prettyName();
|
||||
return result;
|
||||
}
|
||||
void AstNodeDType::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (generic()) str << " [GENERIC]";
|
||||
@@ -1977,13 +2090,13 @@ void AstNodeArrayDType::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStr(str, "declRange", cvtToStr(declRange()));
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
string AstPackArrayDType::prettyDTypeName() const {
|
||||
string AstPackArrayDType::prettyDTypeName(bool full) const {
|
||||
std::ostringstream os;
|
||||
if (const auto subp = subDTypep()) os << subp->prettyDTypeName();
|
||||
if (const auto subp = subDTypep()) os << subp->prettyDTypeName(full);
|
||||
os << declRange();
|
||||
return os.str();
|
||||
}
|
||||
string AstUnpackArrayDType::prettyDTypeName() const {
|
||||
string AstUnpackArrayDType::prettyDTypeName(bool full) const {
|
||||
std::ostringstream os;
|
||||
string ranges = cvtToStr(declRange());
|
||||
// Unfortunately we need a single $ for the first unpacked, and all
|
||||
@@ -1993,7 +2106,7 @@ string AstUnpackArrayDType::prettyDTypeName() const {
|
||||
ranges += cvtToStr(adtypep->declRange());
|
||||
subp = adtypep->subDTypep()->skipRefp();
|
||||
}
|
||||
os << subp->prettyDTypeName() << "$" << ranges;
|
||||
os << subp->prettyDTypeName(full) << "$" << ranges;
|
||||
return os.str();
|
||||
}
|
||||
std::vector<AstUnpackArrayDType*> AstUnpackArrayDType::unpackDimensions() {
|
||||
@@ -2137,20 +2250,22 @@ void AstAssocArrayDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "[assoc-" << nodeAddr(keyDTypep()) << "]";
|
||||
}
|
||||
string AstAssocArrayDType::prettyDTypeName() const {
|
||||
return subDTypep()->prettyDTypeName() + "[" + keyDTypep()->prettyDTypeName() + "]";
|
||||
string AstAssocArrayDType::prettyDTypeName(bool full) const {
|
||||
return subDTypep()->prettyDTypeName(full) + "$[" + keyDTypep()->prettyDTypeName(full) + "]";
|
||||
}
|
||||
void AstDynArrayDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "[]";
|
||||
str << "$[]";
|
||||
}
|
||||
string AstDynArrayDType::prettyDTypeName(bool full) const {
|
||||
return subDTypep()->prettyDTypeName(full) + "$[]";
|
||||
}
|
||||
string AstDynArrayDType::prettyDTypeName() const { return subDTypep()->prettyDTypeName() + "[]"; }
|
||||
void AstQueueDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "[queue]";
|
||||
}
|
||||
string AstQueueDType::prettyDTypeName() const {
|
||||
string str = subDTypep()->prettyDTypeName() + "[$";
|
||||
string AstQueueDType::prettyDTypeName(bool full) const {
|
||||
string str = subDTypep()->prettyDTypeName(full) + "$[$";
|
||||
if (boundConst()) str += ":" + cvtToStr(boundConst());
|
||||
return str + "]";
|
||||
}
|
||||
@@ -2282,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]";
|
||||
@@ -2683,6 +2794,7 @@ void AstCMethodHard::setPurity() {
|
||||
{"commit", false},
|
||||
{"delay", false},
|
||||
{"done", false},
|
||||
{"enqueue", false},
|
||||
{"erase", false},
|
||||
{"evaluate", false},
|
||||
{"evaluation", false},
|
||||
@@ -2695,6 +2807,7 @@ void AstCMethodHard::setPurity() {
|
||||
{"find_last_index", true},
|
||||
{"fire", false},
|
||||
{"first", false},
|
||||
{"hard", false},
|
||||
{"init", false},
|
||||
{"insert", false},
|
||||
{"inside", true},
|
||||
@@ -2724,6 +2837,7 @@ void AstCMethodHard::setPurity() {
|
||||
{"reverse", false},
|
||||
{"rsort", false},
|
||||
{"set", false},
|
||||
{"set_randmode", false},
|
||||
{"shuffle", false},
|
||||
{"size", true},
|
||||
{"slice", true},
|
||||
@@ -2734,7 +2848,8 @@ void AstCMethodHard::setPurity() {
|
||||
{"trigger", false},
|
||||
{"unique", true},
|
||||
{"unique_index", true},
|
||||
{"word", true}};
|
||||
{"word", true},
|
||||
{"write_var", false}};
|
||||
|
||||
auto isPureIt = isPureMethod.find(name());
|
||||
UASSERT_OBJ(isPureIt != isPureMethod.end(), this, "Unknown purity of method " + name());
|
||||
|
||||
+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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -113,7 +113,9 @@ static void makeToStringMiddle(AstClass* nodep) {
|
||||
std::string comma;
|
||||
for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) {
|
||||
if (const auto* const varp = VN_CAST(itemp, Var)) {
|
||||
if (!varp->isParam() && !varp->isInternal()) {
|
||||
if (!varp->isParam() && !varp->isInternal()
|
||||
&& !(varp->dtypeSkipRefp()->basicp()
|
||||
&& varp->dtypeSkipRefp()->basicp()->isRandomGenerator())) {
|
||||
string stmt = "out += \"";
|
||||
stmt += comma;
|
||||
comma = ", ";
|
||||
|
||||
+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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+30
-43
@@ -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) {
|
||||
@@ -1556,7 +1557,9 @@ class ConstVisitor final : public VNVisitor {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool operandsSameSize(AstNode* lhsp, AstNode* rhsp) { return lhsp->width() == rhsp->width(); }
|
||||
static bool operandsSameWidth(const AstNode* lhsp, const AstNode* rhsp) {
|
||||
return lhsp->width() == rhsp->width();
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// Constant Replacement functions.
|
||||
@@ -2168,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);
|
||||
@@ -2209,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
|
||||
@@ -2242,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;
|
||||
@@ -2261,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)) {
|
||||
@@ -2847,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)
|
||||
@@ -3071,8 +3056,9 @@ class ConstVisitor final : public VNVisitor {
|
||||
&& varrefp // Don't do messes with BITREFs/ARRAYREFs
|
||||
&& !varrefp->varp()->hasStrengthAssignment() // Strengths are resolved in V3Tristate
|
||||
&& !varrefp->varp()->valuep() // Not already constified
|
||||
&& !varrefp->varScopep()) { // Not scoped (or each scope may have different initial
|
||||
// value)
|
||||
&& !varrefp->varScopep() // Not scoped (or each scope may have different initial val.)
|
||||
&& !varrefp->varp()->isForced() // Not forced (not really a constant)
|
||||
) {
|
||||
// ASSIGNW (VARREF, const) -> INITIAL ( ASSIGN (VARREF, const) )
|
||||
UINFO(4, "constAssignW " << nodep << endl);
|
||||
// Make a initial assignment
|
||||
@@ -3542,7 +3528,7 @@ class ConstVisitor final : public VNVisitor {
|
||||
TREEOP ("AstMulS {$lhsp.isOne, $rhsp}", "replaceWRhs(nodep)");
|
||||
TREEOP ("AstDiv {$lhsp, $rhsp.isOne}", "replaceWLhs(nodep)");
|
||||
TREEOP ("AstDivS {$lhsp, $rhsp.isOne}", "replaceWLhs(nodep)");
|
||||
TREEOP ("AstMul {operandIsPowTwo($lhsp), operandsSameSize($lhsp,,$rhsp)}", "replaceMulShift(nodep)"); // a*2^n -> a<<n
|
||||
TREEOP ("AstMul {operandIsPowTwo($lhsp), operandsSameWidth($lhsp,,$rhsp)}", "replaceMulShift(nodep)"); // a*2^n -> a<<n
|
||||
TREEOP ("AstDiv {$lhsp, operandIsPowTwo($rhsp)}", "replaceDivShift(nodep)"); // a/2^n -> a>>n
|
||||
TREEOP ("AstModDiv{$lhsp, operandIsPowTwo($rhsp)}", "replaceModAnd(nodep)"); // a % 2^n -> a&(2^n-1)
|
||||
TREEOP ("AstPow {operandIsTwo($lhsp), !$rhsp.isZero}", "replacePowShift(nodep)"); // 2**a == 1<<a
|
||||
@@ -3736,7 +3722,8 @@ class ConstVisitor final : public VNVisitor {
|
||||
TREEOPC("AstXor {matchBitOpTree(nodep)}", "DONE");
|
||||
// Note can't simplify a extend{extends}, extends{extend}, as the sign
|
||||
// bits end up in the wrong places
|
||||
TREEOPV("AstExtend {$lhsp.castExtend}", "replaceExtend(nodep, VN_AS(nodep->lhsp(), Extend)->lhsp())");
|
||||
TREEOPV("AstExtend{operandsSameWidth(nodep,,$lhsp)}", "replaceWLhs(nodep)");
|
||||
TREEOPV("AstExtend{$lhsp.castExtend}", "replaceExtend(nodep, VN_AS(nodep->lhsp(), Extend)->lhsp())");
|
||||
TREEOPV("AstExtendS{$lhsp.castExtendS}", "replaceExtend(nodep, VN_AS(nodep->lhsp(), ExtendS)->lhsp())");
|
||||
TREEOPV("AstReplicate{$srcp, $countp.isOne, $srcp->width()==nodep->width()}", "replaceWLhs(nodep)"); // {1{lhs}}->lhs
|
||||
TREEOPV("AstReplicateN{$lhsp, $rhsp.isOne, $lhsp->width()==nodep->width()}", "replaceWLhs(nodep)"); // {1{lhs}}->lhs
|
||||
|
||||
+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);
|
||||
}
|
||||
|
||||
+673
-435
File diff suppressed because it is too large
Load Diff
+2
-3
@@ -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: ");
|
||||
@@ -223,8 +223,7 @@ class DescopeVisitor final : public VNVisitor {
|
||||
}
|
||||
void visit(AstVarScope* nodep) override {
|
||||
// Delete the varscope when we're finished
|
||||
nodep->unlinkFrBack();
|
||||
pushDeletep(nodep);
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
|
||||
+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
|
||||
|
||||
@@ -443,7 +443,7 @@ class AstToDfgVisitor final : public VNVisitor {
|
||||
// Mark variables with external references
|
||||
if (nodep->isIO() // Ports
|
||||
|| nodep->user2() // Target of a hierarchical reference
|
||||
|| nodep->isForceable() // Forceable
|
||||
|| nodep->isForced() // Forced
|
||||
) {
|
||||
getNet(nodep)->setHasExtRefs();
|
||||
}
|
||||
|
||||
+5
-1
@@ -64,6 +64,7 @@ public:
|
||||
|
||||
struct Hash final {
|
||||
size_t operator()(const KeySel& key) const {
|
||||
// cppcheck-suppress unreadVariable // cppcheck bug
|
||||
V3Hash hash{vertexHash(key.m_fromp)};
|
||||
hash += key.m_lsb;
|
||||
hash += key.m_width;
|
||||
@@ -83,6 +84,7 @@ class KeyUnary final {
|
||||
const DfgVertex* const m_source0p;
|
||||
|
||||
public:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
KeyUnary(DfgVertex* source0p)
|
||||
: m_source0p{source0p} {}
|
||||
|
||||
@@ -110,6 +112,7 @@ public:
|
||||
|
||||
struct Hash final {
|
||||
size_t operator()(const KeyBinary& key) const {
|
||||
// cppcheck-suppress unreadVariable // cppcheck bug
|
||||
V3Hash hash{vertexHash(key.m_source0p)};
|
||||
hash += vertexHash(key.m_source1p);
|
||||
return hash.value();
|
||||
@@ -137,6 +140,7 @@ public:
|
||||
|
||||
struct Hash final {
|
||||
size_t operator()(const KeyTernary& key) const {
|
||||
// cppcheck-suppress unreadVariable // cppcheck bug
|
||||
V3Hash hash{vertexHash(key.m_source0p)};
|
||||
hash += vertexHash(key.m_source1p);
|
||||
hash += vertexHash(key.m_source2p);
|
||||
@@ -348,7 +352,7 @@ class V3DfgCache final {
|
||||
inline CacheType<Vertex>& cacheForType();
|
||||
|
||||
public:
|
||||
V3DfgCache(DfgGraph& dfg)
|
||||
explicit V3DfgCache(DfgGraph& dfg)
|
||||
: m_dfg{dfg} {}
|
||||
|
||||
// Find a vertex of type 'Vertex', with the given operands, or create a new one and add it.
|
||||
|
||||
@@ -295,7 +295,8 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
|
||||
// For each cyclic component
|
||||
for (auto& component : cyclicComponents) {
|
||||
if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
|
||||
// TODO: Apply optimizations safe for cyclic graphs
|
||||
// Converting back to Ast assumes the 'regularize' pass was run, so we must run it
|
||||
V3DfgPasses::regularize(*component, ctx.m_regularizeContext);
|
||||
// Add back under the main DFG (we will convert everything back in one go)
|
||||
dfg->addGraph(*component);
|
||||
}
|
||||
|
||||
+4
-13
@@ -160,22 +160,13 @@ 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 forceable
|
||||
if (astVarp->isForceable()) continue;
|
||||
if (driverVarp->varp()->isSc()) continue;
|
||||
}
|
||||
|
||||
varp->forEachSinkEdge([=](DfgEdge& edge) {
|
||||
// If sink is a SystemC variable, don't inline that sink
|
||||
if (DfgVertexVar* const sinkVarp = edge.sinkp()->cast<DfgVarPacked>()) {
|
||||
if (sinkVarp->varp()->isSc()) return;
|
||||
}
|
||||
edge.relinkSource(driverp);
|
||||
});
|
||||
varp->forEachSinkEdge([=](DfgEdge& edge) { edge.relinkSource(driverp); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
-6
@@ -27,6 +27,7 @@
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
std::string V3DfgRegularizeContext::tmpNamePrefix(DfgGraph& dfg) {
|
||||
// cppcheck-suppress unreadVariable // cppcheck bug
|
||||
V3Hash hash{dfg.modulep()->name()};
|
||||
hash += m_label;
|
||||
std::string name = hash.toString();
|
||||
@@ -45,11 +46,15 @@ class DfgRegularize final {
|
||||
|
||||
// Return canonical variable that can be used to hold the value of this vertex
|
||||
DfgVarPacked* getCanonicalVariable(DfgVertex& vtx) {
|
||||
// First gather all existing variables fully written by this vertex
|
||||
// First gather all existing variables fully written by this vertex. Ignore SystemC
|
||||
// variables, those cannot act as canonical variables, as they cannot participate in
|
||||
// expressions or be assigned rvalues.
|
||||
std::vector<DfgVarPacked*> varVtxps;
|
||||
vtx.forEachSink([&](DfgVertex& sink) {
|
||||
if (DfgVarPacked* const varVtxp = sink.cast<DfgVarPacked>()) {
|
||||
if (varVtxp->isDrivenFullyByDfg()) varVtxps.push_back(varVtxp);
|
||||
if (varVtxp->isDrivenFullyByDfg() && !varVtxp->varp()->isSc()) {
|
||||
varVtxps.push_back(varVtxp);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -95,10 +100,22 @@ class DfgRegularize final {
|
||||
|
||||
// Ensure intermediate values used multiple times are written to variables
|
||||
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
||||
// Operations without multiple sinks need no variables
|
||||
if (!vtx.hasMultipleSinks()) continue;
|
||||
// Array selects need no variables, they are just memory references
|
||||
if (vtx.is<DfgArraySel>()) continue;
|
||||
const bool needsIntermediateVariable = [&]() {
|
||||
// Anything that drives an SC variable needs an intermediate,
|
||||
// as we can only assign simple variables to SC variables at runtime.
|
||||
const bool hasScSink = vtx.findSink<DfgVertexVar>([](const DfgVertexVar& var) { //
|
||||
return var.varp()->isSc();
|
||||
});
|
||||
if (hasScSink) return true;
|
||||
// Operations without multiple sinks need no variables
|
||||
if (!vtx.hasMultipleSinks()) return false;
|
||||
// Array selects need no variables, they are just memory references
|
||||
if (vtx.is<DfgArraySel>()) return false;
|
||||
// Otherwise needs an intermediate variable
|
||||
return true;
|
||||
}();
|
||||
|
||||
if (!needsIntermediateVariable) continue;
|
||||
|
||||
// This is an op which has multiple sinks. Ensure it is assigned to a variable.
|
||||
DfgVarPacked* const varp = getCanonicalVariable(vtx);
|
||||
|
||||
+1
-1
@@ -239,7 +239,7 @@ public:
|
||||
ASTGEN_MEMBERS_DfgVarPacked;
|
||||
|
||||
bool isDrivenFullyByDfg() const {
|
||||
return arity() == 1 && source(0)->dtypep() == dtypep() && !varp()->isForceable();
|
||||
return arity() == 1 && source(0)->dtypep() == dtypep() && !varp()->isForced();
|
||||
}
|
||||
|
||||
void addDriver(FileLine* flp, uint32_t lsb, DfgVertex* vtxp) {
|
||||
|
||||
+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.
|
||||
|
||||
@@ -84,6 +84,7 @@ class EmitCConstPool final : public EmitCConstInit {
|
||||
putns(varp, "extern const ");
|
||||
putns(varp, varp->dtypep()->cType(nameProtect, false, false));
|
||||
putns(varp, " = ");
|
||||
UASSERT_OBJ(varp, varp->valuep(), "Var without value");
|
||||
iterateConst(varp->valuep());
|
||||
putns(varp, ";\n");
|
||||
// Keep track of stats
|
||||
|
||||
+12
-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(),
|
||||
@@ -722,6 +726,8 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP
|
||||
return "";
|
||||
} else if (basicp && basicp->isDynamicTriggerScheduler()) {
|
||||
return "";
|
||||
} else if (basicp && basicp->isRandomGenerator()) {
|
||||
return "";
|
||||
} else if (basicp) {
|
||||
const bool zeroit
|
||||
= (varp->attrFileDescr() // Zero so we don't do file IO if never $fopen
|
||||
@@ -762,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) {}
|
||||
|
||||
@@ -515,6 +515,7 @@ class EmitCHeader final : public EmitCConstInit {
|
||||
if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n");
|
||||
if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n");
|
||||
if (v3Global.usesTiming()) puts("#include \"verilated_timing.h\"\n");
|
||||
if (v3Global.useRandomizeMethods()) puts("#include \"verilated_random.h\"\n");
|
||||
|
||||
std::set<string> cuse_set;
|
||||
auto add_to_cuse_set = [&](string s) { cuse_set.insert(s); };
|
||||
|
||||
+4
-3
@@ -389,6 +389,7 @@ class EmitCImp final : EmitCFunc {
|
||||
} else if (varp->isParam()) {
|
||||
} else if (varp->isStatic() && varp->isConst()) {
|
||||
} else if (varp->basicp() && varp->basicp()->isTriggerVec()) {
|
||||
} else if (VN_IS(varp->dtypep(), NBACommitQueueDType)) {
|
||||
} else {
|
||||
int vects = 0;
|
||||
AstNodeDType* elementp = varp->dtypeSkipRefp();
|
||||
@@ -397,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");
|
||||
@@ -411,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");
|
||||
@@ -693,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) {
|
||||
|
||||
+14
-5
@@ -81,10 +81,16 @@ 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)");
|
||||
cmake_set(*of, "VERILATOR_SOLVER",
|
||||
V3OutFormatter::quoteNameControls(V3Options::getenvVERILATOR_SOLVER()), "STRING",
|
||||
"Default SMT solver for constrained randomization (from $VERILATOR_SOLVER)");
|
||||
|
||||
*of << "\n### Compiler flags...\n";
|
||||
|
||||
@@ -160,6 +166,9 @@ class CMakeEmitter final {
|
||||
if (v3Global.usesTiming()) {
|
||||
global.emplace_back("${VERILATOR_ROOT}/include/verilated_timing.cpp");
|
||||
}
|
||||
if (v3Global.useRandomizeMethods()) {
|
||||
global.emplace_back("${VERILATOR_ROOT}/include/verilated_random.cpp");
|
||||
}
|
||||
global.emplace_back("${VERILATOR_ROOT}/include/verilated_threads.cpp");
|
||||
if (v3Global.opt.usesProfiler()) {
|
||||
global.emplace_back("${VERILATOR_ROOT}/include/verilated_profiler.cpp");
|
||||
@@ -206,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();
|
||||
@@ -214,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";
|
||||
@@ -224,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";
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user