mirror of
https://github.com/verilator/verilator.git
synced 2026-10-03 00:23:35 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
522bead374 | ||
|
|
030286142f | ||
|
|
8a4ceb5717 | ||
|
|
63fa6accc4 | ||
|
|
28718f964a | ||
|
|
1d486e3a59 | ||
|
|
9b396f7125 | ||
|
|
e16cbce52e | ||
|
|
840970e8f7 | ||
|
|
e390d6779d | ||
|
|
39ee522f92 | ||
|
|
9526efcf72 | ||
|
|
d397e0d838 | ||
|
|
8301fdc6d3 | ||
|
|
f645382f11 | ||
|
|
1ed5557d2d | ||
|
|
28b9216f8a | ||
|
|
0114cb67ca | ||
|
|
ea8f86dd30 | ||
|
|
0ff77fc352 | ||
|
|
341963e820 | ||
|
|
db60b92613 | ||
|
|
98206a4f04 | ||
|
|
4df9e2e0e5 | ||
|
|
34e4cffb62 | ||
|
|
71d76c993c | ||
|
|
e67bdb4c08 | ||
|
|
23f013f2d7 | ||
|
|
9b729b80e0 | ||
|
|
38ad328956 | ||
|
|
f767f0cfaa | ||
|
|
1c79df8630 | ||
|
|
248573205b | ||
|
|
88831ca21b | ||
|
|
26f15e11c4 | ||
|
|
5c0a7e06dc | ||
|
|
290b313dc0 | ||
|
|
93e5ca3f6d | ||
|
|
6ffff8565f | ||
|
|
494e05b326 | ||
|
|
a24f61403a | ||
|
|
292cc54768 | ||
|
|
e8a9662eb5 | ||
|
|
0262819c20 | ||
|
|
d3b93a1113 | ||
|
|
5f8c06c805 | ||
|
|
a5ad26cd28 | ||
|
|
5802818b9a | ||
|
|
df519ff16e | ||
|
|
bf8a88a0ad | ||
|
|
878204db73 | ||
|
|
08c76b1da6 | ||
|
|
e82c9db6da | ||
|
|
65c3cb4708 | ||
|
|
0cab076b86 | ||
|
|
3a28ed24ea | ||
|
|
71775eae4c | ||
|
|
953249aa43 | ||
|
|
229dbbb100 | ||
|
|
4a439beae5 | ||
|
|
49022c3e73 | ||
|
|
c92ced4ac7 | ||
|
|
ba47da6587 | ||
|
|
2c27f22900 | ||
|
|
c0391990ad | ||
|
|
2247e1e345 | ||
|
|
e4847464d4 | ||
|
|
7694a7bf41 | ||
|
|
a686e547cf | ||
|
|
1481d34959 | ||
|
|
04512e5d04 | ||
|
|
9a3aed57b1 | ||
|
|
5a69321be3 | ||
|
|
5ee938fd1c | ||
|
|
f78783e187 | ||
|
|
11bfe7a7bb | ||
|
|
8ba494c71f | ||
|
|
acb63c929b | ||
|
|
26dd5d4550 | ||
|
|
32f288084a | ||
|
|
6db149c588 | ||
|
|
0fed5f8b3e | ||
|
|
a30930bdcc | ||
|
|
3a1355fb54 | ||
|
|
97db128d66 | ||
|
|
3d57256070 | ||
|
|
5dc8fb5b4f | ||
|
|
3f0d434f82 | ||
|
|
2cdc5ea48b | ||
|
|
745605efe3 | ||
|
|
0fbd4313b2 | ||
|
|
9f8f61ef2f | ||
|
|
4b1f4938b8 | ||
|
|
5f1dc73a1b | ||
|
|
5e1fc6e24d | ||
|
|
0ec32ee404 | ||
|
|
97b21b3849 | ||
|
|
214173c6b8 | ||
|
|
3786f59e03 | ||
|
|
fa7234ff68 | ||
|
|
91dd3c5fac | ||
|
|
a378dbd9b2 | ||
|
|
da9521a351 | ||
|
|
af51107587 | ||
|
|
e60f652a64 | ||
|
|
a4813ec677 | ||
|
|
01dadb0a8d | ||
|
|
9dcb7ea6a9 | ||
|
|
adfe072b89 | ||
|
|
b12f8b3d73 | ||
|
|
f56f318217 | ||
|
|
3af0eb7fa0 | ||
|
|
a69cb9b044 | ||
|
|
b68a696859 | ||
|
|
e320b0b581 | ||
|
|
42041f2403 | ||
|
|
881c6ee655 |
@@ -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)
|
||||
|
||||
@@ -17,6 +17,8 @@ env:
|
||||
CCACHE_COMPRESS: 1
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_LIMIT_MULTIPLE: 0.95
|
||||
INSTALL_DIR: ${{ github.workspace }}/install
|
||||
RELOC_DIR: ${{ github.workspace }}/relloc
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -94,22 +96,32 @@ jobs:
|
||||
compiler:
|
||||
- { cc: clang, cxx: clang++ }
|
||||
- { cc: gcc, cxx: g++ }
|
||||
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, vltmt-0, vltmt-1]
|
||||
reloc: [0]
|
||||
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1]
|
||||
include:
|
||||
# Test with GCC 10 on ubuntu-20.04
|
||||
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, suite: dist-vlt-0}
|
||||
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, suite: dist-vlt-1}
|
||||
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, suite: dist-vlt-2}
|
||||
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, suite: dist-vlt-3}
|
||||
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, suite: vltmt-0}
|
||||
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, suite: vltmt-1}
|
||||
# Test relocated installation - on most common platform only
|
||||
- {os: ubuntu-22.04, compiler: { cc: gcc, cxx: g++ }, reloc: 1, suite: dist-vlt-0}
|
||||
- {os: ubuntu-22.04, compiler: { cc: gcc, cxx: g++ }, reloc: 1, suite: dist-vlt-1}
|
||||
- {os: ubuntu-22.04, compiler: { cc: gcc, cxx: g++ }, reloc: 1, suite: dist-vlt-2}
|
||||
- {os: ubuntu-22.04, compiler: { cc: gcc, cxx: g++ }, reloc: 1, suite: dist-vlt-3}
|
||||
- {os: ubuntu-22.04, compiler: { cc: gcc, cxx: g++ }, reloc: 1, suite: vltmt-0}
|
||||
- {os: ubuntu-22.04, compiler: { cc: gcc, cxx: g++ }, reloc: 1, suite: vltmt-1}
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: Test | ${{ matrix.os }} | ${{ matrix.compiler.cc }} | ${{ matrix.suite }}
|
||||
name: Test | ${{ matrix.os }} | ${{ matrix.compiler.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
|
||||
env:
|
||||
CI_BUILD_STAGE_NAME: test
|
||||
CI_RUNS_ON: ${{ matrix.os }}
|
||||
CI_RELOC: ${{ matrix.reloc }}
|
||||
CC: ${{ matrix.compiler.cc }}
|
||||
CXX: ${{ matrix.compiler.cxx }}
|
||||
CACHE_BASE_KEY: test-${{ matrix.os }}-${{ matrix.compiler.cc }}-${{ matrix.suite }}
|
||||
CACHE_BASE_KEY: test-${{ matrix.os }}-${{ matrix.compiler.cc }}-${{ matrix.reloc }}-${{ matrix.suite }}
|
||||
CCACHE_MAXSIZE: 100M # Per build per suite (* 5 * 5 = 2500M in total)
|
||||
VERILATOR_ARCHIVE: verilator-${{ github.sha }}-${{ matrix.os }}-${{ matrix.compiler.cc }}.tar.gz
|
||||
steps:
|
||||
@@ -127,7 +139,7 @@ jobs:
|
||||
- name: Cache $CCACHE_DIR
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
|
||||
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache2
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ${{ env.CACHE_KEY }}-${{ github.sha }}
|
||||
|
||||
@@ -44,3 +44,5 @@ verilator-config-version.cmake
|
||||
/.idea/
|
||||
/cmake-build-*/
|
||||
/test_regress/snapshot/
|
||||
xmverilog.*
|
||||
xrun.history
|
||||
|
||||
@@ -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
|
||||
+1
-1
@@ -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.022
|
||||
VERSION 5.026
|
||||
HOMEPAGE_URL https://verilator.org
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
@@ -8,7 +8,127 @@ The changes in each Verilator version are described below. The
|
||||
contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
|
||||
Verilator 5.022 2024-04-24
|
||||
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).
|
||||
* 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 DPI import of null C-string (#5179).
|
||||
|
||||
|
||||
Verilator 5.024 2024-04-05
|
||||
==========================
|
||||
|
||||
**Major:**
|
||||
|
||||
* Add printing summary reports, use `--quiet` or `+verilator+quiet` to suppress (#4909).
|
||||
* Support 1800-2023 keywords, and parsing with UNDEFINED warnings.
|
||||
* Support 1800-2023 preprocessor ifdef expressions.
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Change 1800-2023 to be default language version.
|
||||
* Add DFG 'regularize' pass, and improve variable removal (#4937). [Geza Lore]
|
||||
* Add error when pass net to function argument (#4132) (#4966). [Fuad Ismail]
|
||||
* Add `UNUSEDLOOP` when unused loop is removed (#4926). [Bartłomiej Chmiel, Antmicro Ltd.]
|
||||
* Add custom version for verilator --version packaging (#4954). [Nolan Poe]
|
||||
* Add error on missing pure virtual functions (#4961).
|
||||
* Add error on calling static function without object (#4962).
|
||||
* Add JSON AST dumps (#5020). [Szymon Gizler]
|
||||
* Support 1800-2023 DPI headers, svGetTime/svgGetTimeUnit/svGetTimePrecision methods.
|
||||
* Support 1800-2023 class and function :initial, :extends, :final virtual overrides (#5025).
|
||||
* Support public packed struct / union (#860) (#4878). [Kefa Chen]
|
||||
* Support stream operation on unpacked array (#4714) (#5006). [Fuad Ismail]
|
||||
* Support implicitly-typed variable definitions in for-loop initializers (#4945) (#4986). [Kevin Nygaard]
|
||||
* Support inside range with implicit type conversion (#5026). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Improve installation to be relocatable (#4927). [Geza Lore]
|
||||
* Improve internal ordering code (#4957) (#4990) (#4994) et al. [Geza Lore]
|
||||
* Fix generate blocks in vpi_iterate (#3609) (#4913). [Andrew Nolte]
|
||||
* Fix __Vlip undefined error in --freloop (#4824). [Justin Yao Du]
|
||||
* Fix missing VPI scopes (#4918). [Andrew Nolte]
|
||||
* Fix invalid cast on string structure creation (#4921).
|
||||
* Fix try-lock spuriously fails (#4931) (#4938). [Kamil Rakoczy]
|
||||
* Fix V3Unknown unpacked struct x-assign (#4934). [Yan Xu]
|
||||
* Fix DFG removing forceable signals (#4942). [Geza Lore]
|
||||
* Fix null characters in shortened identifiers (#4946). [Abdul Hameed]
|
||||
* Fix assignment of null into struct member (#4952).
|
||||
* Fix VPI missing scopes 2 (#4965). [Andrew Nolte]
|
||||
* Fix object assignment from conditionals (#4968).
|
||||
* Fix GCC14 warnings on template specialization syntax (#4974) (#4975). [Nolan Poe]
|
||||
* Fix unpacked structure upper bit cleaning (#4978).
|
||||
* Fix tests on MacOS (#4984) (#4985). [Kevin Nygaard]
|
||||
* Fix `--prof-exec` predicted time values (#4988). [Geza Lore]
|
||||
* Fix class type as an associative array parameter (#4997).
|
||||
* Fix inout ports of unpacked struct type (#5000). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Fix `unique {}` constraints missing semicolon (#5001).
|
||||
* Fix preprocessor to respect strings in joins (#5007).
|
||||
* Fix tracing class parameters (#5014).
|
||||
* Fix memory leaks (#5016). [Geza Lore]
|
||||
* Fix $readmem with missing newline (#5019). [Josse Van Delm]
|
||||
* Fix internal error on missing pattern key (#5023).
|
||||
* Fix tracing replicated hierarchical models (#5027).
|
||||
|
||||
|
||||
Verilator 5.022 2024-02-24
|
||||
==========================
|
||||
|
||||
**Minor:**
|
||||
@@ -127,6 +247,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
|
||||
|
||||
+50
-20
@@ -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. ####
|
||||
@@ -203,11 +204,21 @@ verilator.html:
|
||||
verilator.pdf: Makefile
|
||||
$(MAKE) -C docs verilator.pdf
|
||||
|
||||
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
|
||||
VL_INST_BIN_FILES = verilator verilator_bin$(EXEEXT) verilator_bin_dbg$(EXEEXT) verilator_coverage_bin_dbg$(EXEEXT) \
|
||||
verilator_ccache_report verilator_coverage verilator_difftree verilator_gantt verilator_includer verilator_profcfunc
|
||||
# Some scripts go into both the search path and pkgdatadir,
|
||||
# so they can be found by the user, and under $VERILATOR_ROOT.
|
||||
# Public executables intended to be invoked directly by the user
|
||||
# Don't put wildcards in these variables, it might cause an uninstall of other stuff
|
||||
VL_INST_PUBLIC_SCRIPT_FILES = verilator \
|
||||
verilator_coverage \
|
||||
verilator_gantt \
|
||||
verilator_profcfunc \
|
||||
|
||||
VL_INST_PUBLIC_BIN_FILES = verilator_bin$(EXEEXT) \
|
||||
verilator_bin_dbg$(EXEEXT) \
|
||||
verilator_coverage_bin_dbg$(EXEEXT) \
|
||||
|
||||
# Private executabels intended to be invoked by internals
|
||||
# Don't put wildcards in these variables, it might cause an uninstall of other stuff
|
||||
VL_INST_PRIVATE_SCRIPT_FILES = verilator_ccache_report \
|
||||
verilator_includer \
|
||||
|
||||
VL_INST_INC_BLDDIR_FILES = \
|
||||
include/verilated_config.h \
|
||||
@@ -226,19 +237,34 @@ VL_INST_DATA_SRCDIR_FILES = \
|
||||
examples/*/Makefile* \
|
||||
examples/*/vl_* \
|
||||
|
||||
installbin:
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(bindir)
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator $(DESTDIR)$(bindir)/verilator )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_coverage $(DESTDIR)$(bindir)/verilator_coverage )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_gantt $(DESTDIR)$(bindir)/verilator_gantt )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_profcfunc $(DESTDIR)$(bindir)/verilator_profcfunc )
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_bin$(EXEEXT) $(DESTDIR)$(bindir)/verilator_bin$(EXEEXT) )
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_bin_dbg$(EXEEXT) $(DESTDIR)$(bindir)/verilator_bin_dbg$(EXEEXT) )
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_coverage_bin_dbg$(EXEEXT) $(DESTDIR)$(bindir)/verilator_coverage_bin_dbg$(EXEEXT) )
|
||||
mkbindirs:
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/bin
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_includer $(DESTDIR)$(pkgdatadir)/bin/verilator_includer )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_ccache_report $(DESTDIR)$(pkgdatadir)/bin/verilator_ccache_report )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_difftree $(DESTDIR)$(pkgdatadir)/bin/verilator_difftree )
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(bindir)
|
||||
|
||||
installbin: | mkbindirs
|
||||
cd $(srcdir)/bin; \
|
||||
for p in $(VL_INST_PUBLIC_SCRIPT_FILES) ; do \
|
||||
$(INSTALL_PROGRAM) $$p $(DESTDIR)$(pkgdatadir)/bin/$$p; \
|
||||
done
|
||||
cd bin; \
|
||||
for p in $(VL_INST_PUBLIC_BIN_FILES) ; do \
|
||||
$(INSTALL_PROGRAM) $$p $(DESTDIR)$(pkgdatadir)/bin/$$p; \
|
||||
done
|
||||
cd $(srcdir)/bin; \
|
||||
for p in $(VL_INST_PRIVATE_SCRIPT_FILES) ; do \
|
||||
$(INSTALL_PROGRAM) $$p $(DESTDIR)$(pkgdatadir)/bin/$$p; \
|
||||
done
|
||||
|
||||
installredirect: installbin | mkbindirs
|
||||
cp ${srcdir}/bin/redirect ${srcdir}/bin/redirect.tmp
|
||||
perl -p -i -e 'use File::Spec;' \
|
||||
-e' $$path = File::Spec->abs2rel("$(realpath $(DESTDIR)$(pkgdatadir)/bin)", "$(realpath $(DESTDIR)$(bindir))");' \
|
||||
-e 's/RELPATH.*/"$$path";/g' -- "${srcdir}/bin/redirect.tmp"
|
||||
cd $(srcdir)/bin; \
|
||||
for p in $(VL_INST_PUBLIC_SCRIPT_FILES) $(VL_INST_PUBLIC_BIN_FILES) ; do \
|
||||
$(INSTALL_PROGRAM) redirect.tmp $(DESTDIR)$(bindir)/$$p; \
|
||||
done
|
||||
rm ${srcdir}/bin/redirect.tmp
|
||||
|
||||
# Man files can either be part of the original kit, or built in current directory
|
||||
# So important we use $^ so VPATH is searched
|
||||
@@ -281,8 +307,11 @@ installdata:
|
||||
|
||||
# We don't trust rm -rf, so rmdir instead as it will fail if user put in other files
|
||||
uninstall:
|
||||
-cd $(DESTDIR)$(bindir) && rm -f $(VL_INST_BIN_FILES)
|
||||
-cd $(DESTDIR)$(pkgdatadir)/bin && rm -f $(VL_INST_BIN_FILES)
|
||||
-cd $(DESTDIR)$(bindir) && rm -f $(VL_INST_PUBLIC_SCRIPT_FILES)
|
||||
-cd $(DESTDIR)$(bindir) && rm -f $(VL_INST_PUBLIC_BIN_FILES)
|
||||
-cd $(DESTDIR)$(pkgdatadir)/bin && rm -f $(VL_INST_PUBLIC_SCRIPT_FILES)
|
||||
-cd $(DESTDIR)$(pkgdatadir)/bin && rm -f $(VL_INST_PUBLIC_BIN_FILES)
|
||||
-cd $(DESTDIR)$(pkgdatadir)/bin && rm -f $(VL_INST_PRIVATE_SCRIPT_FILES)
|
||||
-cd $(DESTDIR)$(mandir)/man1 && rm -f $(VL_INST_MAN_FILES)
|
||||
-cd $(DESTDIR)$(pkgdatadir) && rm -f $(VL_INST_INC_BLDDIR_FILES)
|
||||
-cd $(DESTDIR)$(pkgdatadir) && rm -f $(VL_INST_INC_SRCDIR_FILES)
|
||||
@@ -311,7 +340,7 @@ uninstall:
|
||||
-rmdir $(DESTDIR)$(pkgconfigdir)
|
||||
|
||||
install: all_nomsg install-all
|
||||
install-all: installbin installman installdata install-msg
|
||||
install-all: installbin installredirect installman installdata install-msg
|
||||
|
||||
install-here: installman info
|
||||
|
||||
@@ -410,6 +439,7 @@ PY_PROGRAMS = \
|
||||
src/cppcheck_filtered \
|
||||
src/flexfix \
|
||||
src/vlcovgen \
|
||||
src/.gdbinit.py \
|
||||
test_regress/t/*.pf \
|
||||
nodist/clang_check_attributes \
|
||||
nodist/code_coverage \
|
||||
|
||||
+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
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env perl
|
||||
######################################################################
|
||||
#
|
||||
# Copyright 2003-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
|
||||
#
|
||||
######################################################################
|
||||
|
||||
require 5.006_001;
|
||||
use warnings;
|
||||
use FindBin qw($RealBin $RealScript);
|
||||
use strict;
|
||||
|
||||
my $relpath = RELPATH; # Substituted during Verilator 'make install'
|
||||
|
||||
unshift @ARGV, $RealScript;
|
||||
|
||||
exec { "$RealBin/$relpath/$RealScript" } @ARGV;
|
||||
die "%Error: Exec failed,";
|
||||
+37
-32
@@ -15,6 +15,7 @@ use Getopt::Long;
|
||||
use FindBin qw($RealBin $RealScript);
|
||||
use IO::File;
|
||||
use Pod::Usage;
|
||||
use Cwd qw(realpath);
|
||||
|
||||
use strict;
|
||||
use vars qw($Debug @Opt_Verilator_Sw);
|
||||
@@ -57,6 +58,7 @@ if (! GetOptions(
|
||||
# Switches
|
||||
"gdb!" => \$opt_gdb,
|
||||
"gdbbt!" => \$opt_gdbbt,
|
||||
"quiet!" => \$opt_quiet_exit, # As -quiet implies -quiet-exit
|
||||
"quiet-exit!" => \$opt_quiet_exit,
|
||||
"rr!" => \$opt_rr,
|
||||
"unlimited-stack!" => \$opt_unlimited_stack,
|
||||
@@ -67,6 +69,18 @@ if (! GetOptions(
|
||||
pod2usage(-exitstatus => 2, -verbose => 0);
|
||||
}
|
||||
|
||||
my $verilator_root = realpath("$RealBin/..");
|
||||
if (defined $ENV{VERILATOR_ROOT}) {
|
||||
if ((!-d $ENV{VERILATOR_ROOT}) || $verilator_root ne realpath($ENV{VERILATOR_ROOT})) {
|
||||
warn "%Error: verilator: VERILATOR_ROOT is set to inconsistent path. Suggest leaving it unset.\n";
|
||||
warn "%Error: VERILATOR_ROOT=$ENV{VERILATOR_ROOT}\n";
|
||||
exit 1;
|
||||
}
|
||||
} else {
|
||||
print "export VERILATOR_ROOT='$verilator_root'\n" if $Debug;
|
||||
$ENV{VERILATOR_ROOT} = $verilator_root;
|
||||
}
|
||||
|
||||
if ($opt_gdbbt && !gdb_works()) {
|
||||
warn "-Info: --gdbbt ignored: gdb doesn't seem to be working\n" if $Debug;
|
||||
$opt_gdbbt = 0;
|
||||
@@ -151,30 +165,13 @@ sub debug {
|
||||
# Builds
|
||||
|
||||
sub verilator_bin {
|
||||
my $bin = "";
|
||||
# Use VERILATOR_ROOT if defined, else assume verilator_bin is in the search path
|
||||
my $basename = ($ENV{VERILATOR_BIN}
|
||||
|| ($Debug ? "verilator_bin_dbg" : "verilator_bin"));
|
||||
if (defined($ENV{VERILATOR_ROOT})) {
|
||||
my $dir = $ENV{VERILATOR_ROOT};
|
||||
if (-x "$dir/bin/$basename"
|
||||
|| -x "$dir/bin/$basename.exe") { # From a "make install" into VERILATOR_ROOT
|
||||
$bin = "$dir/bin/$basename";
|
||||
} else {
|
||||
$bin = "$dir/$basename"; # From pointing to kit directory
|
||||
}
|
||||
if (-x "$RealBin/$basename" || -x "$RealBin/$basename.exe") {
|
||||
return "$RealBin/$basename";
|
||||
} else {
|
||||
if (-x "$RealBin/$basename"
|
||||
|| -x "$RealBin/$basename.exe") {
|
||||
$bin = "$RealBin/$basename"; # From path/to/verilator with verilator_bin installed
|
||||
} else {
|
||||
$bin = $basename; # Find in PATH
|
||||
}
|
||||
# Note we don't look under bin/$basename which would be right if running
|
||||
# in the kit dir. Running that would likely break, since
|
||||
# VERILATOR_ROOT wouldn't be set and Verilator won't find internal files.
|
||||
return $basename; # Find in PATH
|
||||
}
|
||||
return $bin;
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
@@ -312,6 +309,7 @@ detailed descriptions of these arguments.
|
||||
+1800-2009ext+<ext> Use SystemVerilog 2009 with file extension <ext>
|
||||
+1800-2012ext+<ext> Use SystemVerilog 2012 with file extension <ext>
|
||||
+1800-2017ext+<ext> Use SystemVerilog 2017 with file extension <ext>
|
||||
+1800-2023ext+<ext> Use SystemVerilog 2023 with file extension <ext>
|
||||
--assert Enable all assertions
|
||||
--assert-case Enable unique/unique0/priority case related checks
|
||||
--autoflush Flush streams after all $displays
|
||||
@@ -350,10 +348,12 @@ detailed descriptions of these arguments.
|
||||
--dump-tree Enable dumping Ast .tree files
|
||||
--dump-tree-addrids Use short identifiers instead of addresses
|
||||
--dump-tree-dot Enable dumping Ast .tree.dot debug files
|
||||
--dump-tree-json Enable dumping Ast .tree.json files and .tree.meta.json file
|
||||
--dump-<srcfile> Enable dumping everything in source file
|
||||
--dumpi-dfg <level> Enable dumping DfgGraphs to .dot files at level
|
||||
--dumpi-graph <level> Enable dumping V3Graphs to .dot files at level
|
||||
--dumpi-tree <level> Enable dumping Ast .tree files at level
|
||||
--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
|
||||
--error-limit <value> Abort after this number of errors
|
||||
@@ -373,7 +373,7 @@ detailed descriptions of these arguments.
|
||||
--generate-key Create random key for --protect-key
|
||||
--getenv <var> Get environment variable with defaults
|
||||
--get-supported <feature> Get if feature is supported
|
||||
--help Display this help
|
||||
--help Show this help
|
||||
--hierarchical Enable hierarchical Verilation
|
||||
-I<dir> Directory to search for includes
|
||||
--if-depth <value> Tune IFDEPTH warning
|
||||
@@ -387,6 +387,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,6 +407,7 @@ 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-uint8 Specify types for top-level ports
|
||||
@@ -426,7 +428,9 @@ detailed descriptions of these arguments.
|
||||
--public-params Mark all parameters as public_flat
|
||||
--public-flat-rw Mark all variables, etc as public_flat_rw
|
||||
-pvalue+<name>=<value> Overwrite toplevel parameter
|
||||
--quiet Minimize additional printing
|
||||
--quiet-exit Don't print the command on failure
|
||||
--quiet-stats Don't print statistics
|
||||
--relative-includes Resolve includes relative to current file
|
||||
--reloop-limit Minimum iterations for forming loops
|
||||
--report-unoptflat Extra diagnostics for UNOPTFLAT
|
||||
@@ -441,7 +445,7 @@ detailed descriptions of these arguments.
|
||||
--no-stop-fail Do not call $stop when assertion fails
|
||||
--structs-packed Convert all unpacked structures to packed structures
|
||||
-sv Enable SystemVerilog parsing
|
||||
+systemverilogext+<ext> Synonym for +1800-2017ext+<ext>
|
||||
+systemverilogext+<ext> Synonym for +1800-2023ext+<ext>
|
||||
--threads <threads> Enable multithreading
|
||||
--threads-dpi <mode> Enable multithreaded DPI
|
||||
--threads-max-mtasks <mtasks> Tune maximum mtask partitioning
|
||||
@@ -474,7 +478,7 @@ detailed descriptions of these arguments.
|
||||
--no-verilate Skip Verilation and just compile previously Verilated code
|
||||
+verilog1995ext+<ext> Synonym for +1364-1995ext+<ext>
|
||||
+verilog2001ext+<ext> Synonym for +1364-2001ext+<ext>
|
||||
--version Displays program version and exits
|
||||
--version Show program version and exits
|
||||
--vpi Enable VPI compiles
|
||||
--waiver-output <filename> Create a waiver file based on the linter warnings
|
||||
-Wall Enable all style warnings
|
||||
@@ -508,20 +512,21 @@ description of these arguments.
|
||||
|
||||
=for VL_SPHINX_EXTRACT "_build/gen/args_verilated.rst"
|
||||
|
||||
+verilator+debug Enable debugging
|
||||
+verilator+debugi+<value> Enable debugging at a level
|
||||
+verilator+debug Enable debugging
|
||||
+verilator+debugi+<value> Enable debugging at a level
|
||||
+verilator+coverage+file+<filename> Set coverage output filename
|
||||
+verilator+error+limit+<value> Set error limit
|
||||
+verilator+help Display help
|
||||
+verilator+noassert Disable assert checking
|
||||
+verilator+error+limit+<value> Set error limit
|
||||
+verilator+help Show help
|
||||
+verilator+noassert Disable assert checking
|
||||
+verilator+prof+exec+file+<filename> Set execution profile filename
|
||||
+verilator+prof+exec+start+<value> Set execution profile starting point
|
||||
+verilator+prof+exec+window+<value> Set execution profile duration
|
||||
+verilator+prof+vlt+file+<filename> Set PGO profile filename
|
||||
+verilator+rand+reset+<value> Set random reset technique
|
||||
+verilator+seed+<value> Set random seed
|
||||
+verilator+V Verbose version and config
|
||||
+verilator+version Show version and exit
|
||||
+verilator+quiet Minimize additional printing
|
||||
+verilator+rand+reset+<value> Set random reset technique
|
||||
+verilator+seed+<value> Set random seed
|
||||
+verilator+V Show verbose version and config
|
||||
+verilator+version Show version and exit
|
||||
|
||||
|
||||
=head1 DISTRIBUTION
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
+8
-7
@@ -92,13 +92,12 @@ 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 ||
|
||||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache
|
||||
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 libclang-dev mold ||
|
||||
sudo apt-get install libclang-dev mold
|
||||
pip3 install clang==14.0
|
||||
sudo apt-get install python3-clang mold ||
|
||||
sudo apt-get install python3-clang mold
|
||||
fi
|
||||
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ] || [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
|
||||
sudo apt-get install libsystemc-dev ||
|
||||
@@ -107,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
|
||||
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
|
||||
sudo pkg install -y ccache gmake perl5 python3 jq z3
|
||||
else
|
||||
fatal "Unknown os: '$CI_OS_NAME'"
|
||||
fi
|
||||
@@ -120,6 +119,8 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
||||
fi
|
||||
yes yes | sudo cpan -M $CI_CPAN_REPO -fi Parallel::Forker
|
||||
install-vcddiff
|
||||
# Workaround -fsanitize=address crash
|
||||
sudo sysctl -w vm.mmap_rnd_bits=28
|
||||
else
|
||||
##############################################################################
|
||||
# Unknown build stage
|
||||
|
||||
+23
-6
@@ -40,7 +40,7 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
|
||||
|
||||
if [ "$COVERAGE" != 1 ]; then
|
||||
autoconf
|
||||
./configure --enable-longtests --enable-ccwarn
|
||||
./configure --enable-longtests --enable-ccwarn --prefix="$INSTALL_DIR"
|
||||
ccache -z
|
||||
"$MAKE" -j "$NPROC" -k
|
||||
# 22.04: ccache -s -v
|
||||
@@ -87,23 +87,40 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
||||
# Run sanitize on Ubuntu 22.04 only
|
||||
[ "$CI_RUNS_ON" = 'ubuntu-22.04' ] && sanitize='--sanitize' || sanitize=''
|
||||
|
||||
TEST_REGRESS=test_regress
|
||||
if [ "$CI_RELOC" == 1 ]; then
|
||||
# Testing that the installation is relocatable.
|
||||
"$MAKE" install
|
||||
mkdir -p "$RELOC_DIR"
|
||||
mv "$INSTALL_DIR" "$RELOC_DIR/relocated-install"
|
||||
export VERILATOR_ROOT="$RELOC_DIR/relocated-install/share/verilator"
|
||||
TEST_REGRESS="$RELOC_DIR/test_regress"
|
||||
mv test_regress "$TEST_REGRESS"
|
||||
# Feeling brave?
|
||||
find . -delete
|
||||
ls -la .
|
||||
fi
|
||||
|
||||
# Run the specified test
|
||||
ccache -z
|
||||
case $TESTS in
|
||||
dist-vlt-0)
|
||||
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=0/3
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=0/4
|
||||
;;
|
||||
dist-vlt-1)
|
||||
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=1/3
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=1/4
|
||||
;;
|
||||
dist-vlt-2)
|
||||
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=2/3
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=2/4
|
||||
;;
|
||||
dist-vlt-3)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=3/4
|
||||
;;
|
||||
vltmt-0)
|
||||
"$MAKE" -C test_regress SCENARIOS=--vltmt DRIVER_HASHSET=--hashset=0/2
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS=--vltmt DRIVER_HASHSET=--hashset=0/2
|
||||
;;
|
||||
vltmt-1)
|
||||
"$MAKE" -C test_regress SCENARIOS=--vltmt DRIVER_HASHSET=--hashset=1/2
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS=--vltmt DRIVER_HASHSET=--hashset=1/2
|
||||
;;
|
||||
coverage-all)
|
||||
nodist/code_coverage --stages 1-
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -43,6 +43,7 @@ RUN apt-get update \
|
||||
perl \
|
||||
python3 \
|
||||
wget \
|
||||
z3 \
|
||||
zlib1g \
|
||||
zlib1g-dev \
|
||||
&& apt-get clean \
|
||||
|
||||
+24
-2
@@ -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.022 2024-02-24],
|
||||
AC_INIT([Verilator],[5.026 2024-06-15],
|
||||
[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 "
|
||||
|
||||
@@ -9,20 +9,24 @@ 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 Randall
|
||||
Christopher Taylor
|
||||
Chuxuan Wang
|
||||
Chykon
|
||||
Conor McCullough
|
||||
@@ -33,6 +37,7 @@ David Ledger
|
||||
David Metz
|
||||
David Stanford
|
||||
David Turner
|
||||
Dercury
|
||||
Don Williamson
|
||||
Drew Ranck
|
||||
Drew Taussig
|
||||
@@ -46,10 +51,12 @@ february cozzocrea
|
||||
Felix Neumärker
|
||||
Felix Yan
|
||||
Frans Skarman
|
||||
Fuad Ismail
|
||||
G-A. Kamendje
|
||||
Garrett Smith
|
||||
Geza Lore
|
||||
Gianfranco Costamagna
|
||||
Gijs Burghoorn
|
||||
Glen Gibb
|
||||
Gökçe Aydos
|
||||
Graham Rushton
|
||||
@@ -103,8 +110,10 @@ Justin Thiel
|
||||
Kaleb Barrett
|
||||
Kamil Rakoczy
|
||||
Kanad Kanhere
|
||||
Kefa Chen
|
||||
Keith Colbert
|
||||
Kevin Kiningham
|
||||
Kevin Nygaard
|
||||
Kritik Bhimani
|
||||
Krzysztof Bieganski
|
||||
Krzysztof Boronski
|
||||
@@ -124,6 +133,7 @@ Marshal Qiao
|
||||
Martin Schmidt
|
||||
Martin Stadler
|
||||
Matthew Ballance
|
||||
Michael Bikovitsky
|
||||
Michael Killough
|
||||
Michaël Lefebvre
|
||||
Michal Czyz
|
||||
@@ -135,10 +145,13 @@ Mostafa Gamal
|
||||
Nandu Raj
|
||||
Nathan Kohagen
|
||||
Nathan Myers
|
||||
Nolan Poe
|
||||
Oleh Maksymenko
|
||||
Patrick Stewart
|
||||
Paul Bowen-Huggett
|
||||
Paul Swirhun
|
||||
Paul Wright
|
||||
Pawel Jewstafjew
|
||||
Pawel Sagan
|
||||
Pengcheng Xu
|
||||
Peter Debacker
|
||||
@@ -181,6 +194,7 @@ Tudor Timi
|
||||
Tymoteusz Blazejczyk
|
||||
Udi Finkelstein
|
||||
Unai Martinez-Corral
|
||||
Valentin Atepalikhin
|
||||
Varun Koyyalagunta
|
||||
Vassilis Papaefstathiou
|
||||
Veripool API Bot
|
||||
@@ -189,6 +203,7 @@ Vito Gamberini
|
||||
William D. Jones
|
||||
Wilson Snyder
|
||||
Xi Zhang
|
||||
Yan Xu
|
||||
Yinan Xu
|
||||
Yoda Lee
|
||||
Yossi Nivin
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -92,6 +92,11 @@ Summary:
|
||||
profile-guided optimization data runtime filename to dump to. Defaults
|
||||
to :file:`profile.vlt`.
|
||||
|
||||
.. option:: +verilator+quiet
|
||||
|
||||
Disable printing the simulation summary report, see :ref:`Simulation
|
||||
Summary Report`.
|
||||
|
||||
.. option:: +verilator+rand+reset+<value>
|
||||
|
||||
When a model was Verilated using
|
||||
|
||||
@@ -51,6 +51,8 @@ Summary:
|
||||
|
||||
.. option:: +1800-2017ext+<ext>
|
||||
|
||||
.. option:: +1800-2023ext+<ext>
|
||||
|
||||
Specifies the language standard to be used with a specific filename
|
||||
extension, <ext>.
|
||||
|
||||
@@ -199,9 +201,10 @@ Summary:
|
||||
|
||||
.. option:: --compiler <compiler-name>
|
||||
|
||||
Enables workarounds for the specified C++ compiler (list below).
|
||||
This does not change any performance tuning options, but it may
|
||||
in the future.
|
||||
Enables workarounds for the specified C++ compiler (list below). This
|
||||
does not change any performance tuning options, but it may in the
|
||||
future. This also does not change default compiler flags; these are
|
||||
determined when Verilator was configured.
|
||||
|
||||
clang
|
||||
Tune for clang. This may reduce execution speed as it enables several
|
||||
@@ -348,7 +351,7 @@ Summary:
|
||||
Select the language used by default when first processing each
|
||||
Verilog file. The language value must be "VAMS", "1364-1995",
|
||||
"1364-2001", "1364-2001-noconfig", "1364-2005", "1800-2005",
|
||||
"1800-2009", "1800-2012", "1800-2017", or "1800+VAMS".
|
||||
"1800-2009", "1800-2012", "1800-2017", "1800-2023", or "1800+VAMS".
|
||||
|
||||
Any language associated with a particular file extension (see the
|
||||
various +<lang>*\ ext+ options) will be used in preference to the
|
||||
@@ -361,7 +364,7 @@ Summary:
|
||||
``+<lang>ext+`` options should be used.
|
||||
|
||||
If no language is specified, either by this option or ``+<lang>ext+``
|
||||
options, then the latest SystemVerilog language (IEEE 1800-2017) is
|
||||
options, then the latest SystemVerilog language (IEEE 1800-2023) is
|
||||
used.
|
||||
|
||||
.. option:: +define+<var>=<value>
|
||||
@@ -410,6 +413,12 @@ Summary:
|
||||
:vlopt:`--debug --no-dump-tree <--dump-tree>` may be useful if the dump
|
||||
files are large and not desired.
|
||||
|
||||
.. option:: --dump-tree-json
|
||||
|
||||
Rarely needed. Enable dumping Ast .json.tree debug files with dumping level 3,
|
||||
which dumps the standard critical stages. For details on the format, see
|
||||
the Verilator Internals manual.
|
||||
|
||||
.. option:: --dump-tree-dot
|
||||
|
||||
Rarely needed. Enable dumping Ast .tree.dot debug files in Graphviz
|
||||
@@ -445,6 +454,11 @@ Summary:
|
||||
Rarely needed - for developer use. Set internal Ast dumping level
|
||||
globally to the specified value.
|
||||
|
||||
.. option:: --dumpi-tree-json <level>
|
||||
|
||||
Rarely needed - for developer use. Set internal Ast JSON dumping level
|
||||
globally to the specified value.
|
||||
|
||||
.. option:: --dumpi-<srcfile> <level>
|
||||
|
||||
Rarely needed - for developer use. Set the dumping level in the
|
||||
@@ -803,6 +817,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.
|
||||
@@ -971,6 +990,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
|
||||
@@ -1160,11 +1186,20 @@ Summary:
|
||||
Overwrites the given parameter(s) of the top-level module. See
|
||||
:vlopt:`-G <-G<name>>` for a detailed description.
|
||||
|
||||
.. option:: --quiet
|
||||
|
||||
Alias for :vlopt:`--quiet-exit` :vlopt:`--quiet-stats`.
|
||||
|
||||
.. option:: --quiet-exit
|
||||
|
||||
When exiting due to an error, do not display the "Exiting due to Errors"
|
||||
nor "Command Failed" messages.
|
||||
|
||||
.. option:: --quiet-stats
|
||||
|
||||
Disable printing the Verilation statistics report, see :ref:`Verilation
|
||||
Summary Report`.
|
||||
|
||||
.. option:: --relative-includes
|
||||
|
||||
When a file references an include file, resolve the filename relative to
|
||||
@@ -1298,12 +1333,12 @@ Summary:
|
||||
.. option:: -sv
|
||||
|
||||
Specifies SystemVerilog language features should be enabled; equivalent
|
||||
to :vlopt:`--language 1800-2017 <--language>`. This option is selected
|
||||
to :vlopt:`--language 1800-2023 <--language>`. This option is selected
|
||||
by default; it exists for compatibility with other simulators.
|
||||
|
||||
.. option:: +systemverilogext+<ext>
|
||||
|
||||
A synonym for :vlopt:`+1800-2017ext+\<ext\>`.
|
||||
A synonym for :vlopt:`+1800-2023ext+\<ext\>`.
|
||||
|
||||
.. option:: --threads <threads>
|
||||
|
||||
@@ -1709,9 +1744,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`.
|
||||
|
||||
@@ -1786,7 +1821,8 @@ Summary:
|
||||
format is still evolving; there will be some changes in future versions.
|
||||
|
||||
This option disables some more aggressive transformations and dumps only
|
||||
the final state of the AST.
|
||||
the final state of the AST. For more granular and unaltered dumps, meant
|
||||
mainly for debugging see :vlopt:`--dump-tree-json`.
|
||||
|
||||
.. option:: --json-only-meta-output <filename>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -36,8 +36,7 @@ or "`ifdef`"'s may break other tools.
|
||||
.. option:: """ [string] """
|
||||
|
||||
A triple-quoted block specifies a string that may include newlines and
|
||||
single quotes. This extension is experimental and may be removed
|
||||
without deprecation.
|
||||
single quotes. This extension was standardized in IEEE 1800-2023.
|
||||
|
||||
.. option:: $c([string], ...);
|
||||
|
||||
@@ -577,6 +576,10 @@ or "`ifdef`"'s may break other tools.
|
||||
|
||||
.. option:: /*verilator&32;trace_init_task*/
|
||||
|
||||
Removed.
|
||||
|
||||
In versions before 5.024:
|
||||
|
||||
Attached to a DPI import to indicate that function should be called when
|
||||
initializing tracing. This attribute is indented only to be used
|
||||
internally in code that Verilator generates when :vlopt:`--lib-create`
|
||||
@@ -619,4 +622,4 @@ or "`ifdef`"'s may break other tools.
|
||||
symbols. Also, the data represents the C++ stack; the SystemVerilog
|
||||
functions/tasks involved may be renamed and/or inlined before becoming
|
||||
the C++ functions that may be visible in the stack trace. This
|
||||
extension is experimental and may be removed without deprecation.
|
||||
extension was standardized in IEEE 1800-2023.
|
||||
|
||||
+7
-4
@@ -128,10 +128,13 @@ How do I generate waveforms (traces) in C++?
|
||||
|
||||
See also the next question for tracing in SystemC mode.
|
||||
|
||||
A. Pass the :vlopt:`--trace` option to Verilator, and in your top-level C
|
||||
code, call ``Verilated::traceEverOn(true)``. Then you may use
|
||||
``$dumpfile`` and ``$dumpvars`` to enable traces, the same as with any
|
||||
Verilog simulator. See ``examples/make_tracing_c`` in the distribution.
|
||||
A. Pass the :vlopt:`--trace` option to Verilator. Then you may use ``$dumpfile`` and
|
||||
``$dumpvars`` to enable traces, the same as with any Verilog simulator,
|
||||
although Verilator ignores the arguments to ``$dumpvars``. See
|
||||
``examples/make_tracing_c`` in the distribution.
|
||||
|
||||
If writing the top-level C code, call ``Verilated::traceEverOn(true)``;
|
||||
this is done for you if using :vlopt:`--binary`.
|
||||
|
||||
B. Or, for finer-grained control, or C++ files with multiple Verilated
|
||||
modules, you may also create the trace purely from C++. Create a
|
||||
|
||||
+17
-4
@@ -126,8 +126,9 @@ Those developing Verilator itself may also want these (see internals.rst):
|
||||
::
|
||||
|
||||
sudo apt-get install clang clang-format-14 cmake gdb gprof graphviz lcov
|
||||
sudo apt-get install libclang-dev yapf3 bear
|
||||
sudo pip3 install clang sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe ruff
|
||||
sudo apt-get install python3-clang yapf3 bear jq
|
||||
sudo pip3 install sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe ruff
|
||||
sudo pip3 install git+https://github.com/antmicro/astsee.git
|
||||
cpan install Pod::Perldoc
|
||||
cpan install Parallel::Forker
|
||||
|
||||
@@ -151,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
|
||||
@@ -192,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:
|
||||
|
||||
|
||||
@@ -62,12 +62,23 @@ Verilator supports the 2017 "for" loop constructs and several cleanups IEEE
|
||||
made in 1800-2017.
|
||||
|
||||
|
||||
SystemVerilog 2023 (IEEE 1800-2023) Support
|
||||
-------------------------------------------
|
||||
|
||||
Verilator supports some of the 2023 improvements, including triple-quoted
|
||||
string blocks that may include newlines and single quotes.
|
||||
|
||||
Verilator implements a full IEEE 1800-2023 compliant preprocessor,
|
||||
including triple-quoted strings, and \`ifdef expressions.
|
||||
|
||||
|
||||
Verilog AMS Support
|
||||
-------------------
|
||||
|
||||
Verilator implements a very small subset of Verilog AMS (Verilog Analog and
|
||||
Mixed-Signal Extensions) with the subset corresponding to those VMS
|
||||
keywords with near-equivalents in Verilog 2005 or SystemVerilog 2017.
|
||||
keywords with near-equivalents in Verilog IEEE 1364 or SystemVerilog
|
||||
IEEE 1800.
|
||||
|
||||
AMS parsing is enabled with :vlopt:`--language VAMS <--language>` or
|
||||
:vlopt:`--language 1800+VAMS <--language>`.
|
||||
|
||||
@@ -50,6 +50,6 @@ The best place to get started is to try the :ref:`Examples`.
|
||||
.. [#] SystemVerilog is defined by the `Institute of Electrical and
|
||||
Electronics Engineers (IEEE) Standard for SystemVerilog - Unified
|
||||
Hardware Design, Specification, and Verification Language`, Standard
|
||||
1800, released in 2005, 2009, 2012, and 2017. The Verilator
|
||||
documentation uses the shorthand e.g., "IEEE 1800-2017", to refer to
|
||||
the, e.g., 2017 version of this standard.
|
||||
1800, released in 2005, 2009, 2012, 2017, and 2023. The Verilator
|
||||
documentation uses the shorthand e.g., "IEEE 1800-2023", to refer to
|
||||
the, e.g., 2023 version of this standard.
|
||||
|
||||
@@ -12,6 +12,60 @@ Verilated model's executable. For the runtime arguments to a simulated
|
||||
model, see :ref:`Simulation Runtime Arguments`.
|
||||
|
||||
|
||||
.. _Simulation Summary Report:
|
||||
|
||||
Simulation Summary Report
|
||||
=========================
|
||||
|
||||
When simulation finishes, it will print a report to stdout summarizing the
|
||||
simulation. This requires the model being Verilated with :vlopt:`--main`.
|
||||
The report may be disabled with :vlopt:`+verilator+quiet`.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block::
|
||||
|
||||
- S i m u l a t i o n R e p o r t: Verilator ...
|
||||
- Verilator: End at simtime 123 ns; walltime 1234.001 s; speed 123 ns/s
|
||||
- Verilator: cpu 22.001 s on 4 threads; alloced 123 MB
|
||||
|
||||
The information in this report is:
|
||||
|
||||
.. describe:: "Verilator ..."
|
||||
|
||||
Program version.
|
||||
|
||||
.. describe:: "End at simtime 123 ns"
|
||||
|
||||
Verilog $time at which the model finished or stopped.
|
||||
|
||||
.. describe:: "walltime 1234.001 s"
|
||||
|
||||
Real elapsed wall time in seconds.
|
||||
|
||||
.. describe:: "speed 123.1 ns/s"
|
||||
|
||||
Simulated time (if non-zero) divided by wall time. e.g. `123 ns/s` means
|
||||
123 simulated nanoseconds took 1 second of wall time; for a model with
|
||||
only a 1 GHz clock that would be equivalent to 123.1 cycles per
|
||||
second. The units are automatically selected to give a number between 1
|
||||
and 1000. The wall time includes initialization, initial and final
|
||||
process blocks, so indicates a slower speed than if the model had a
|
||||
longer runtime.
|
||||
|
||||
.. describe:: "cpu 22 s"
|
||||
|
||||
CPU time used total across all CPU threads in seconds.
|
||||
|
||||
.. describe:: "4 threads"
|
||||
|
||||
Number of simultaneous threads used.
|
||||
|
||||
.. describe:: "alloced 123 MB"
|
||||
|
||||
Total memory used during simulation in megabytes.
|
||||
|
||||
|
||||
.. _Benchmarking & Optimization:
|
||||
|
||||
Benchmarking & Optimization
|
||||
|
||||
@@ -498,3 +498,78 @@ The search paths can be configured by setting some variables:
|
||||
|
||||
Sets the installation prefix of an installed SystemC library. (Same as
|
||||
SYSTEMC_ROOT).
|
||||
|
||||
|
||||
.. _Verilation Summary Report:
|
||||
|
||||
Verilation Summary Report
|
||||
=========================
|
||||
|
||||
When Verilator generates code, unless :vlopt:`--quiet-stats` is used, it
|
||||
will print a report to stdout summarizing the build. For example:
|
||||
|
||||
.. code-block::
|
||||
|
||||
- V e r i l a t i o n R e p o r t: Verilator ....
|
||||
- Verilator: Built from 354 MB sources in 247 modules,
|
||||
into 74 MB in 89 C++ files needing 0.192 MB
|
||||
- Verilator: Walltime 26.580 s (elab=2.096, cvt=18.268,
|
||||
bld=2.100); cpu 26.548 s on 1 threads; alloced 2894.672 MB
|
||||
|
||||
The information in this report is:
|
||||
|
||||
.. describe:: "Verilator ..."
|
||||
|
||||
Program version.
|
||||
|
||||
.. describe:: "234 MB sources"
|
||||
|
||||
Characters of post-preprocessed text in all input
|
||||
Verilog and Verilator Control files in megabytes.
|
||||
|
||||
.. describe:: "247 modules"
|
||||
|
||||
Number of interfaces/modules/classes/packages in design before
|
||||
elaboration.
|
||||
|
||||
.. describe:: "into 74 MB"
|
||||
|
||||
Characters of output C++ code, including comments in megabytes.
|
||||
|
||||
.. describe:: "89 C++ files"
|
||||
|
||||
Number of .cpp files created.
|
||||
|
||||
.. describe:: "needing 192MB"
|
||||
|
||||
Verilation-time minimum-bound estimate of memory needed to run model in
|
||||
megabytes. (Expect to need significantly more.)
|
||||
|
||||
.. describe:: "Walltime 26.580 s"
|
||||
|
||||
Real elapsed wall time for Verilation and build.
|
||||
|
||||
.. describe:: "elab=2.096"
|
||||
|
||||
Wall time to read in files and complete elaboration.
|
||||
|
||||
.. describe:: "cvt=18.268"
|
||||
|
||||
Wall time for Verilator to process and write output.
|
||||
|
||||
.. describe:: "bld=2.1"
|
||||
|
||||
Wall time to compile gcc/clang (if using :vlopt:`--build`).
|
||||
|
||||
.. describe:: "cpu 22.548 s"
|
||||
|
||||
CPU time used, total across all CPU threads.
|
||||
|
||||
.. describe:: "4 threads"
|
||||
|
||||
Number of simultaneous threads used.
|
||||
|
||||
.. describe:: "alloced 123 MB"
|
||||
|
||||
Total memory used during build by Verilator executable (excludes
|
||||
:vlopt:`--build` compiler's usage) in megabytes.
|
||||
|
||||
+69
-23
@@ -161,8 +161,8 @@ List Of Warnings
|
||||
.. option:: BADSTDPRAGMA
|
||||
|
||||
An error that a pragma is badly formed, for pragmas defined by IEEE
|
||||
1800-2017. For example, an empty pragma line, or an incorrectly used
|
||||
'pragma protect'. Third-party pragmas not defined by IEEE 1800-2017 are
|
||||
1800-2023. For example, an empty pragma line, or an incorrectly used
|
||||
'pragma protect'. Third-party pragmas not defined by IEEE 1800-2023 are
|
||||
ignored.
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -283,7 +296,7 @@ List Of Warnings
|
||||
Unique case statements that select on an enumerated variable, where all
|
||||
of the enumerated values are covered by case items, are considered
|
||||
complete even if the case statement does not cover illegal
|
||||
non-enumerated values (IEEE 1800-2017 12.5.3). To check that illegal
|
||||
non-enumerated values (IEEE 1800-2023 12.5.3). To check that illegal
|
||||
values are not hit, use :vlopt:`--assert`.
|
||||
|
||||
Ignoring this warning will only suppress the lint check; it will
|
||||
@@ -479,7 +492,7 @@ List Of Warnings
|
||||
|
||||
.. code-block::
|
||||
|
||||
%Warning-DEFPARAM: example.v:5:15: defparam is deprecated (IEEE 1800-2017 C.4.1)
|
||||
%Warning-DEFPARAM: example.v:5:15: defparam is deprecated (IEEE 1800-2023 C.4.1)
|
||||
: ... Suggest use instantiation with #(.MY_PARAM(...etc...))
|
||||
|
||||
To repair use :code:`#(.PARAMETER(...))` syntax. Repaired Example:
|
||||
@@ -707,7 +720,7 @@ List Of Warnings
|
||||
|
||||
.. code-block::
|
||||
|
||||
%Warning-GENUNNAMED: example.v:2:9: Unnamed generate block (IEEE 1800-2017 27.6)
|
||||
%Warning-GENUNNAMED: example.v:2:9: Unnamed generate block (IEEE 1800-2023 27.6)
|
||||
|
||||
To fix this assign a label (often with the naming convention prefix of
|
||||
:code:`gen_` or :code:`g_`), for example:
|
||||
@@ -763,7 +776,7 @@ List Of Warnings
|
||||
|
||||
.. code-block::
|
||||
|
||||
%Warning-IGNOREDRETURN: example.v:5:9: Ignoring return value of non-void function (IEEE 1800-2017 13.4.1)
|
||||
%Warning-IGNOREDRETURN: example.v:5:9: Ignoring return value of non-void function (IEEE 1800-2023 13.4.1)
|
||||
|
||||
The portable way to suppress this warning (in SystemVerilog) is to use a
|
||||
void cast, for example:
|
||||
@@ -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
|
||||
@@ -1328,10 +1349,35 @@ List Of Warnings
|
||||
.. TODO better example
|
||||
|
||||
An error that a package/class appears to have been referenced that has not
|
||||
yet been declared. According to IEEE 1800-2017 26.3, all packages must
|
||||
yet been declared. According to IEEE 1800-2023 26.3, all packages must
|
||||
be declared before being used.
|
||||
|
||||
|
||||
.. option:: PREPROCZERO
|
||||
|
||||
Warns that a preprocessor \`ifdef/\`ifndef expression (added in IEEE
|
||||
1800-2023) evaluates a define value which has a value of :code:`0`.
|
||||
This will evaluate in the expression as :code:`1` because the define has
|
||||
a definition, unlike in the C preprocessor, which evaluates using the
|
||||
define's value (of :code:`1`).
|
||||
|
||||
Referring to a define with an empty value does not give this warning, as
|
||||
in C, the preprocessor will give an error on a preprocessor expression
|
||||
of a define that is empty.
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 2
|
||||
|
||||
`define ZERO 0
|
||||
`ifdef (ZERO || ZERO) //<--- warning PREPROCZERO
|
||||
`error This_will_error_which_might_be_not_the_intent
|
||||
`endif
|
||||
|
||||
The portable way to suppress this warning is to use a define value other
|
||||
than zero to when used in a preprocessor expression.
|
||||
|
||||
|
||||
.. option:: PROCASSWIRE
|
||||
|
||||
.. TODO better example
|
||||
@@ -1707,7 +1753,7 @@ List Of Warnings
|
||||
|
||||
.. code-block::
|
||||
|
||||
%Warning-TIMESCALEMOD: example.v:1:8: Timescale missing on this module as other modules have it (IEEE 1800-2017 3.14.2.3)
|
||||
%Warning-TIMESCALEMOD: example.v:1:8: Timescale missing on this module as other modules have it (IEEE 1800-2023 3.14.2.3)
|
||||
|
||||
Recommend using :vlopt:`--timescale` argument, or in front of all
|
||||
modules use:
|
||||
@@ -1949,7 +1995,7 @@ List Of Warnings
|
||||
.. option:: USERERROR
|
||||
|
||||
A SystemVerilog elaboration-time assertion error was executed.
|
||||
IEEE 1800-2017 20.11 requires this error.
|
||||
IEEE 1800-2023 20.11 requires this error.
|
||||
|
||||
Faulty example:
|
||||
|
||||
@@ -1965,7 +2011,7 @@ List Of Warnings
|
||||
.. option:: USERFATAL
|
||||
|
||||
A SystemVerilog elaboration-time assertion fatal was executed.
|
||||
IEEE 1800-2017 20.11 requires this error.
|
||||
IEEE 1800-2023 20.11 requires this error.
|
||||
|
||||
Faulty example:
|
||||
|
||||
@@ -1981,7 +2027,7 @@ List Of Warnings
|
||||
.. option:: USERINFO
|
||||
|
||||
A SystemVerilog elaboration-time assertion print was executed. This is
|
||||
not an error or warning, and IEEE 1800-2017 20.11 requires this
|
||||
not an error or warning, and IEEE 1800-2023 20.11 requires this
|
||||
behavior.
|
||||
|
||||
Example:
|
||||
@@ -1996,7 +2042,7 @@ List Of Warnings
|
||||
.. option:: USERWARN
|
||||
|
||||
A SystemVerilog elaboration-time assertion warning was executed.
|
||||
IEEE 1800-2017 20.11 requires this warning.
|
||||
IEEE 1800-2023 20.11 requires this warning.
|
||||
|
||||
Faulty example:
|
||||
|
||||
@@ -2135,7 +2181,7 @@ List Of Warnings
|
||||
.. option:: ZEROREPL
|
||||
|
||||
Warns that zero is used as the replication value in the replication
|
||||
operator. This is specified as an error by IEEE 1800-2017 11.4.12.1.
|
||||
operator. This is specified as an error by IEEE 1800-2023 11.4.12.1.
|
||||
|
||||
Faulty example:
|
||||
|
||||
@@ -2155,7 +2201,7 @@ List Of Warnings
|
||||
|
||||
.. code-block::
|
||||
|
||||
%Error-ZEROREPL: test.v:5:22: Replication value of 0 is only legal under a concatenation (IEEE 1800-2017 11.4.12.1)
|
||||
%Error-ZEROREPL: test.v:5:22: Replication value of 0 is only legal under a concatenation (IEEE 1800-2023 11.4.12.1)
|
||||
|
||||
Note that in some cases, this warning may be false, when a condition
|
||||
upstream or downstream of the access means the zero replication will
|
||||
|
||||
+126
-19
@@ -224,7 +224,7 @@ Scheduling
|
||||
----------
|
||||
|
||||
Verilator implements the Active and NBA regions of the SystemVerilog scheduling
|
||||
model as described in IEEE 1800-2017 chapter 4, and in particular sections
|
||||
model as described in IEEE 1800-2023 chapter 4, and in particular sections
|
||||
4.5 and Figure 4.1. The static (Verilation time) scheduling of SystemVerilog
|
||||
processes is performed by code in the ``V3Sched`` namespace. The single
|
||||
entry point to the scheduling algorithm is ``V3Sched::schedule``. Some
|
||||
@@ -312,7 +312,7 @@ Scheduling of clocked and combinational logic
|
||||
For performance, clocked and combinational logic needs to be ordered.
|
||||
Conceptually this minimizes the iterations through the evaluation loop
|
||||
presented in the reference algorithm in the SystemVerilog standard (IEEE
|
||||
1800-2017 section 4.5), by evaluating logic constructs in data-flow order.
|
||||
1800-2023 section 4.5), by evaluating logic constructs in data-flow order.
|
||||
Without going into a lot of detail here, accept that well thought out ordering
|
||||
is crucial to good simulation performance, and also enables further
|
||||
optimizations later on.
|
||||
@@ -407,7 +407,7 @@ triggered due to a combinational signal change from the previous evaluation
|
||||
pass, and a combinational loop settling due to hybrid logic, if the clocked
|
||||
logic reads the not yet settled combinationally driven signal. Such a race
|
||||
is indeed possible, but our evaluation is consistent with the SystemVerilog
|
||||
scheduling semantics (IEEE 1800-2017 chapter 4), and therefore any program
|
||||
scheduling semantics (IEEE 1800-2023 chapter 4), and therefore any program
|
||||
that exhibits such a race has non-deterministic behavior according to the
|
||||
SystemVerilog semantics, so we accept this.
|
||||
|
||||
@@ -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
|
||||
==================
|
||||
|
||||
@@ -1701,7 +1786,7 @@ Similarly, the ``NETLIST`` has a list of modules referred to by its
|
||||
|
||||
``.tree.json``` is an alternative dump format to ``.tree`` that is meant for
|
||||
programmatic processing (e.g. with `astsee <https://github.com/antmicro/astsee>`_).
|
||||
To enable this dump format, use :vlopt:`--json-only`.
|
||||
To enable this dump format, use :vlopt:`--dump-tree-json` or :vlopt:`--json-only`.
|
||||
|
||||
Structure:
|
||||
::
|
||||
@@ -1743,8 +1828,8 @@ Structure:
|
||||
|
||||
.tree.meta.json Output
|
||||
----------------
|
||||
|
||||
.tree.meta.json contains metadata that is common across the whole AST tree.
|
||||
.tree.meta.json contains metadata that is common across the whole AST tree
|
||||
(in case of --dump-tree-json, multiple trees share one meta file).
|
||||
|
||||
Besides de-duplication of data shared between multiple stages, .meta.json enables offloading
|
||||
unstable data (that can vary from machine-to-machine or run-to-run) from main .tree.json.
|
||||
@@ -1754,11 +1839,11 @@ This offloading allows, for example, to use byte-to-byte comparisons of AST dump
|
||||
|
||||
{"files": {
|
||||
/* Map id to filename, and other metadata */
|
||||
"d": {"filename":"/home/ant/tmp/verilator/include/verilated_std.sv", "realpath":"/home/ant/tmp/verilator/include/verilated_std.sv", "language":"1800-2017"},
|
||||
"a": {"filename":"<built-in>", "realpath":"<built-in>", "language":"1800-2017"},
|
||||
"b": {"filename":"<command-line>", "realpath":"<command-line>", "language":"1800-2017"},
|
||||
"c": {"filename":"input.vc", "realpath":"/home/ant/tmp/verilator/test_regress/input.vc", "language":"1800-2017"},
|
||||
"e": {"filename":"t/t_EXAMPLE.v", "realpath":"/home/ant/tmp/verilator/test_regress/t/t_EXAMPLE.v", "language":"1800-2017"}
|
||||
"d": {"filename":"/home/ant/tmp/verilator/include/verilated_std.sv", "realpath":"/home/ant/tmp/verilator/include/verilated_std.sv", "language":"1800-2023"},
|
||||
"a": {"filename":"<built-in>", "realpath":"<built-in>", "language":"1800-2023"},
|
||||
"b": {"filename":"<command-line>", "realpath":"<command-line>", "language":"1800-2023"},
|
||||
"c": {"filename":"input.vc", "realpath":"/home/ant/tmp/verilator/test_regress/input.vc", "language":"1800-2023"},
|
||||
"e": {"filename":"t/t_EXAMPLE.v", "realpath":"/home/ant/tmp/verilator/test_regress/t/t_EXAMPLE.v", "language":"1800-2023"}
|
||||
},"pointers": {
|
||||
/* Map id to real address */
|
||||
"(AG)": "0x562997289180",
|
||||
@@ -1846,6 +1931,20 @@ To print a node:
|
||||
pnt nodep
|
||||
# or: call dumpTreeGdb(nodep) # aliased to "pnt" in src/.gdbinit
|
||||
|
||||
``src/.gdbinit`` and ``src/.gdbinit.py`` define handy utilities for working with
|
||||
JSON AST dumps. For example:
|
||||
|
||||
* ``jstash nodep`` - Perform a JSON AST dump and save it into GDB value history (e.g. ``$1``)
|
||||
* ``jtree nodep`` - Perform a JSON AST dump and pretty print it using ``astsee_verilator``.
|
||||
* ``jtree $1`` - Pretty print a dump that was previously saved by ``jstash``.
|
||||
* ``jtree nodep -d '.file, .timeunit'`` - Perform a JSON AST dump, filter out some fields and pretty print it.
|
||||
* ``jtree 0x55555613dca0`` - Pretty print using address literal (rather than actual pointer).
|
||||
* ``jtree $1 nodep`` - Diff ``nodep`` against an older dump.
|
||||
|
||||
A detailed description of ``jstash`` and ``jtree`` can be displayed using ``gdb``'s ``help`` command.
|
||||
|
||||
These commands require `astsee <https://github.com/antmicro/astsee>`_ to be installed.
|
||||
|
||||
When GDB halts, it is useful to understand that the backtrace will commonly
|
||||
show the iterator functions between each invocation of ``visit`` in the
|
||||
backtrace. You will typically see a frame sequence something like:
|
||||
@@ -1907,21 +2006,21 @@ Verilator ideally would support all of IEEE, and has the goal to get close
|
||||
to full support. However the following IEEE sections and features are not
|
||||
anticipated to be ever implemented for the reasons indicated.
|
||||
|
||||
IEEE 1800-2017 3.3 modules within modules
|
||||
IEEE 1800-2023 3.3 modules within modules
|
||||
Little/no tool support, and arguably not a good practice.
|
||||
IEEE 1800-2017 6.12 "shortreal"
|
||||
IEEE 1800-2023 6.12 "shortreal"
|
||||
Little/no tool support, and easily promoted to real.
|
||||
IEEE 1800-2017 11.11 Min, typ, max
|
||||
IEEE 1800-2023 11.11 Min, typ, max
|
||||
No SDF support, so will always use typical.
|
||||
IEEE 1800-2017 20.16 Stochastic analysis
|
||||
IEEE 1800-2023 20.16 Stochastic analysis
|
||||
Little industry use.
|
||||
IEEE 1800-2017 20.17 PLA modeling
|
||||
IEEE 1800-2023 20.17 PLA modeling
|
||||
Little industry use and outdated technology.
|
||||
IEEE 1800-2017 31 Timing checks
|
||||
IEEE 1800-2023 31 Timing checks
|
||||
No longer relevant with static timing analysis tools.
|
||||
IEEE 1800-2017 32 SDF annotation
|
||||
IEEE 1800-2023 32 SDF annotation
|
||||
No longer relevant with static timing analysis tools.
|
||||
IEEE 1800-2017 33 Config
|
||||
IEEE 1800-2023 33 Config
|
||||
Little industry use.
|
||||
|
||||
|
||||
@@ -1962,6 +2061,10 @@ driver.pl Non-Scenario Arguments
|
||||
files with dumping level 3, which dumps the standard critical stages.
|
||||
For details on the format see `.tree Output`.
|
||||
|
||||
--fail-max <numtests>
|
||||
Set the number of failing tests, after which the driver will stop running
|
||||
additional tests. Defaults to 20, 0 disables.
|
||||
|
||||
--gdb
|
||||
Same as ``verilator --gdb``: Run Verilator under the debugger.
|
||||
|
||||
@@ -2107,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
|
||||
@@ -71,6 +72,7 @@ Deadman
|
||||
Debacker
|
||||
Deepa
|
||||
Defparams
|
||||
Delm
|
||||
Denio
|
||||
Deprecations
|
||||
Deroo
|
||||
@@ -130,6 +132,7 @@ Gernot
|
||||
Gerst
|
||||
Gielda
|
||||
Gigerl
|
||||
Gijs
|
||||
Gileadi
|
||||
Giri
|
||||
Gisselquist
|
||||
@@ -146,6 +149,7 @@ Grulfen
|
||||
Gu
|
||||
Gunter
|
||||
Guo
|
||||
Hameed
|
||||
Hao
|
||||
Haojin
|
||||
Harboe
|
||||
@@ -162,6 +166,7 @@ Homebrew
|
||||
Hornung
|
||||
Hossell
|
||||
Hsu
|
||||
Huggett
|
||||
Hutt
|
||||
Hyperthreading
|
||||
Ibrahim
|
||||
@@ -184,6 +189,7 @@ Jiaxun
|
||||
Jiuyang
|
||||
Joannou
|
||||
Joly
|
||||
Josse
|
||||
Jullien
|
||||
Junji
|
||||
Junyi
|
||||
@@ -438,6 +444,7 @@ Verilog
|
||||
Vighnesh
|
||||
Viktor
|
||||
Vilp
|
||||
Vlip
|
||||
Vm
|
||||
Vukobratovic
|
||||
Wai
|
||||
@@ -461,6 +468,7 @@ Xiaoliang
|
||||
Xiaoyi
|
||||
Xuan
|
||||
Xuanqi
|
||||
Yao
|
||||
Yazdanbakhsh
|
||||
Yernagula
|
||||
Yi
|
||||
@@ -639,6 +647,7 @@ envvar
|
||||
eof
|
||||
errae
|
||||
erroring
|
||||
esynr
|
||||
et
|
||||
eval
|
||||
evals
|
||||
@@ -670,6 +679,7 @@ foreach
|
||||
fprintf
|
||||
fprofile
|
||||
fread
|
||||
freloop
|
||||
frewind
|
||||
fs
|
||||
fscanf
|
||||
@@ -835,6 +845,7 @@ parallelized
|
||||
param
|
||||
parameterization
|
||||
parameterized
|
||||
parameterless
|
||||
params
|
||||
parens
|
||||
pawel
|
||||
@@ -878,6 +889,7 @@ profcfunc
|
||||
profiler
|
||||
prototyptes
|
||||
ps
|
||||
psprintf
|
||||
pthread
|
||||
ptr
|
||||
pulldown
|
||||
@@ -961,13 +973,18 @@ submakes
|
||||
submodule
|
||||
submodules
|
||||
substring
|
||||
suspendable
|
||||
sv
|
||||
svBitVal
|
||||
svBitVecVal
|
||||
svGet
|
||||
svGetTime
|
||||
svGetTimePrecision
|
||||
svLogicVal
|
||||
svdpi
|
||||
svgGetTimeUnit
|
||||
swrite
|
||||
synthesizeable
|
||||
sys
|
||||
systemc
|
||||
tenghtt
|
||||
@@ -986,6 +1003,7 @@ toolchain
|
||||
topcell
|
||||
toplevel
|
||||
toupper
|
||||
traceCapable
|
||||
traceEverOn
|
||||
tran
|
||||
treei
|
||||
@@ -1050,6 +1068,7 @@ vluint
|
||||
vpi
|
||||
vpiConstType
|
||||
vpiDefName
|
||||
vpiInertialDelay
|
||||
vpiInstance
|
||||
vpiLeftRange
|
||||
vpiModule
|
||||
|
||||
@@ -30,6 +30,7 @@ endif()
|
||||
|
||||
# Create a new executable target that will contain all your sources
|
||||
add_executable(example ../make_hello_c/sim_main.cpp)
|
||||
target_compile_features(example PUBLIC cxx_std_14)
|
||||
|
||||
# Add the Verilated circuit to the target
|
||||
verilate(example
|
||||
|
||||
@@ -31,7 +31,7 @@ endif
|
||||
ifeq ($(shell which cmake),)
|
||||
TARGET := nocmake
|
||||
else
|
||||
CMAKE_VERSION := $(shell cmake --version | grep -Po '(\d[\.\d]+)')
|
||||
CMAKE_VERSION := $(shell cmake --version | grep -o '[0-9][.0-9]\+')
|
||||
CMAKE_MAJOR := $(shell echo $(CMAKE_VERSION) | cut -f1 -d.)
|
||||
CMAKE_MINOR := $(shell echo $(CMAKE_VERSION) | cut -f2 -d.)
|
||||
CMAKE_GT_3_8 := $(shell [ $(CMAKE_MAJOR) -gt 3 -o \( $(CMAKE_MAJOR) -eq 3 -a $(CMAKE_MINOR) -ge 8 \) ] && echo true)
|
||||
|
||||
@@ -37,6 +37,7 @@ find_package(SystemCLanguage QUIET)
|
||||
|
||||
# Create a new executable target that will contain all your sources
|
||||
add_executable(example ../make_hello_sc/sc_main.cpp)
|
||||
target_compile_features(example PUBLIC cxx_std_14)
|
||||
|
||||
set_property(
|
||||
TARGET example
|
||||
|
||||
@@ -31,7 +31,7 @@ endif
|
||||
ifeq ($(shell which cmake),)
|
||||
TARGET := nocmake
|
||||
else
|
||||
CMAKE_VERSION := $(shell cmake --version | grep -Po '(\d[\.\d]+)')
|
||||
CMAKE_VERSION := $(shell cmake --version | grep -o '[0-9][.0-9]\+')
|
||||
CMAKE_MAJOR := $(shell echo $(CMAKE_VERSION) | cut -f1 -d.)
|
||||
CMAKE_MINOR := $(shell echo $(CMAKE_VERSION) | cut -f2 -d.)
|
||||
CMAKE_GT_3_8 := $(shell [ $(CMAKE_MAJOR) -gt 3 -o \( $(CMAKE_MAJOR) -eq 3 -a $(CMAKE_MINOR) -ge 8 \) ] && echo true)
|
||||
|
||||
@@ -30,9 +30,12 @@ endif()
|
||||
|
||||
# Create the main executable target
|
||||
add_executable(example ../make_protect_lib/sim_main.cpp)
|
||||
target_compile_features(example PUBLIC cxx_std_14)
|
||||
|
||||
# Create a secret library
|
||||
add_library(verilated_secret STATIC) # or SHARED for a shared library
|
||||
target_compile_features(verilated_secret PUBLIC cxx_std_14)
|
||||
|
||||
target_link_libraries(example PRIVATE verilated_secret)
|
||||
# To create both libraries on CMake >= 3.12 replace the above 2 lines with the following:
|
||||
# add_library(verilated_secret OBJECT)
|
||||
|
||||
@@ -31,7 +31,7 @@ endif
|
||||
ifeq ($(shell which cmake),)
|
||||
TARGET := nocmake
|
||||
else
|
||||
CMAKE_VERSION := $(shell cmake --version | grep -Po '(\d[\.\d]+)')
|
||||
CMAKE_VERSION := $(shell cmake --version | grep -o '[0-9][.0-9]\+')
|
||||
CMAKE_MAJOR := $(shell echo $(CMAKE_VERSION) | cut -f1 -d.)
|
||||
CMAKE_MINOR := $(shell echo $(CMAKE_VERSION) | cut -f2 -d.)
|
||||
CMAKE_GT_3_8 := $(shell [ $(CMAKE_MAJOR) -gt 3 -o \( $(CMAKE_MAJOR) -eq 3 -a $(CMAKE_MINOR) -ge 8 \) ] && echo true)
|
||||
|
||||
@@ -30,6 +30,7 @@ endif()
|
||||
|
||||
# Create a new executable target that will contain all your sources
|
||||
add_executable(example ../make_tracing_c/sim_main.cpp)
|
||||
target_compile_features(example PUBLIC cxx_std_14)
|
||||
|
||||
# Add the Verilated circuit to the target
|
||||
verilate(example COVERAGE TRACE
|
||||
|
||||
@@ -31,7 +31,7 @@ endif
|
||||
ifeq ($(shell which cmake),)
|
||||
TARGET := nocmake
|
||||
else
|
||||
CMAKE_VERSION := $(shell cmake --version | grep -Po '(\d[\.\d]+)')
|
||||
CMAKE_VERSION := $(shell cmake --version | grep -o '[0-9][.0-9]\+')
|
||||
CMAKE_MAJOR := $(shell echo $(CMAKE_VERSION) | cut -f1 -d.)
|
||||
CMAKE_MINOR := $(shell echo $(CMAKE_VERSION) | cut -f2 -d.)
|
||||
CMAKE_GT_3_8 := $(shell [ $(CMAKE_MAJOR) -gt 3 -o \( $(CMAKE_MAJOR) -eq 3 -a $(CMAKE_MINOR) -ge 8 \) ] && echo true)
|
||||
|
||||
@@ -38,6 +38,7 @@ find_package(SystemCLanguage QUIET)
|
||||
|
||||
# Create a new executable target that will contain all your sources
|
||||
add_executable(example ../make_tracing_sc/sc_main.cpp)
|
||||
target_compile_features(example PUBLIC cxx_std_14)
|
||||
|
||||
set_property(
|
||||
TARGET example
|
||||
|
||||
@@ -31,7 +31,7 @@ endif
|
||||
ifeq ($(shell which cmake),)
|
||||
TARGET := nocmake
|
||||
else
|
||||
CMAKE_VERSION := $(shell cmake --version | grep -Po '(\d[\.\d]+)')
|
||||
CMAKE_VERSION := $(shell cmake --version | grep -o '[0-9][.0-9]\+')
|
||||
CMAKE_MAJOR := $(shell echo $(CMAKE_VERSION) | cut -f1 -d.)
|
||||
CMAKE_MINOR := $(shell echo $(CMAKE_VERSION) | cut -f2 -d.)
|
||||
CMAKE_GT_3_8 := $(shell [ $(CMAKE_MAJOR) -gt 3 -o \( $(CMAKE_MAJOR) -eq 3 -a $(CMAKE_MINOR) -ge 8 \) ] && echo true)
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
#endif
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (false && argc && argv) {}
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
// Construct context to hold simulation time, etc
|
||||
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
|
||||
@@ -46,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
|
||||
|
||||
@@ -117,6 +117,9 @@ int main(int argc, char** argv) {
|
||||
contextp->coveragep()->write("logs/coverage.dat");
|
||||
#endif
|
||||
|
||||
// Final simulation summary
|
||||
contextp->statsPrintSummary();
|
||||
|
||||
// Return good completion status
|
||||
// Don't use exit() or destructor won't get called
|
||||
return 0;
|
||||
|
||||
@@ -347,8 +347,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; }
|
||||
|
||||
@@ -1646,7 +1647,11 @@ if(1)
|
||||
{
|
||||
if(vm4ip[2] != prev_alias)
|
||||
{
|
||||
fpos += fstWriterSVarint(f, (((int64_t)((int32_t)(prev_alias = vm4ip[2]))) << 1) | 1);
|
||||
int32_t t_i32 = ((int32_t)(prev_alias = vm4ip[2])); /* vm4ip is generic unsigned data */
|
||||
int64_t t_i64 = (int64_t)t_i32; /* convert to signed */
|
||||
uint64_t t_u64 = (uint64_t)t_i64; /* sign extend through 64b */
|
||||
|
||||
fpos += fstWriterSVarint(f, (int64_t)((t_u64 << 1) | 1)); /* all in this block was: fpos += fstWriterSVarint(f, (((int64_t)((int32_t)(prev_alias = vm4ip[2]))) << 1) | 1); */
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+206
-68
@@ -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;
|
||||
|
||||
@@ -169,9 +175,12 @@ void vl_stop_maybe(const char* filename, int linenum, const char* hier, bool may
|
||||
Verilated::threadContextp()->errorCountInc();
|
||||
if (maybe
|
||||
&& Verilated::threadContextp()->errorCount() < Verilated::threadContextp()->errorLimit()) {
|
||||
VL_PRINTF( // Not VL_PRINTF_MT, already on main thread
|
||||
"-Info: %s:%d: %s\n", filename, linenum,
|
||||
"Verilog $stop, ignored due to +verilator+error+limit");
|
||||
// Do just once when cross error limit
|
||||
if (Verilated::threadContextp()->errorCount() == 1) {
|
||||
VL_PRINTF( // Not VL_PRINTF_MT, already on main thread
|
||||
"-Info: %s:%d: %s\n", filename, linenum,
|
||||
"Verilog $stop, ignored due to +verilator+error+limit");
|
||||
}
|
||||
} else {
|
||||
vl_stop(filename, linenum, hier);
|
||||
}
|
||||
@@ -602,7 +611,7 @@ WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, const WDataInP
|
||||
const int words = VL_WORDS_I(obits);
|
||||
VL_ZERO_W(obits, owp);
|
||||
EData lor = 0; // 0=all zeros, ~0=all ones, else mix
|
||||
for (int i = 1; i < (words - 1); ++i) { lor |= lwp[i]; }
|
||||
for (int i = 1; i < (words - 1); ++i) lor |= lwp[i];
|
||||
lor |= ((lwp[words - 1] == VL_MASK_E(rbits)) ? ~VL_EUL(0) : 0);
|
||||
if (lor == 0 && lwp[0] == 0) { // "X" so return 0
|
||||
return owp;
|
||||
@@ -814,7 +823,7 @@ void _vl_vsformat(std::string& output, const std::string& format, va_list ap) VL
|
||||
bool widthSet = false;
|
||||
bool left = false;
|
||||
size_t width = 0;
|
||||
for (std::string::const_iterator pos = format.begin(); pos != format.end(); ++pos) {
|
||||
for (std::string::const_iterator pos = format.cbegin(); pos != format.cend(); ++pos) {
|
||||
if (!inPct && pos[0] == '%') {
|
||||
pctit = pos;
|
||||
inPct = true;
|
||||
@@ -883,7 +892,7 @@ void _vl_vsformat(std::string& output, const std::string& format, va_list ap) VL
|
||||
case '^': { // Realtime
|
||||
const int lbits = va_arg(ap, int);
|
||||
const double d = va_arg(ap, double);
|
||||
if (lbits) {} // UNUSED - always 64
|
||||
(void)lbits; // UNUSED - always 64
|
||||
if (fmt == '^') { // Realtime
|
||||
if (!widthSet) width = Verilated::threadContextp()->impp()->timeFormatWidth();
|
||||
const int timeunit = va_arg(ap, int);
|
||||
@@ -1168,8 +1177,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;
|
||||
@@ -1212,15 +1221,15 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
|
||||
IData got = 0;
|
||||
bool inPct = false;
|
||||
bool inIgnore = false;
|
||||
std::string::const_iterator pos = format.begin();
|
||||
for (; pos != format.end() && !_vl_vsss_eof(fp, floc); ++pos) {
|
||||
std::string::const_iterator pos = format.cbegin();
|
||||
for (; pos != format.cend() && !_vl_vsss_eof(fp, floc); ++pos) {
|
||||
// VL_DBG_MSGF("_vlscan fmt='"<<pos[0]<<"' floc="<<floc<<" file='"<<_vl_vsss_peek(fp, floc,
|
||||
// fromp, fstr)<<"'\n");
|
||||
if (!inPct && pos[0] == '%') {
|
||||
inPct = true;
|
||||
inIgnore = false;
|
||||
} else if (!inPct && std::isspace(pos[0])) { // Format spaces
|
||||
while (std::isspace(pos[1])) pos++;
|
||||
while (std::isspace(pos[1])) ++pos;
|
||||
_vl_vsss_skipspace(fp, floc, fromp, fstr);
|
||||
} else if (!inPct) { // Expected Format
|
||||
_vl_vsss_skipspace(fp, floc, fromp, fstr);
|
||||
@@ -1436,8 +1445,8 @@ void _vl_string_to_vint(int obits, void* destp, size_t srclen, const char* srcp)
|
||||
char* op = reinterpret_cast<char*>(destp);
|
||||
if (srclen > bytes) srclen = bytes; // Don't overflow destination
|
||||
size_t i = 0;
|
||||
for (i = 0; i < srclen; ++i) { *op++ = srcp[srclen - 1 - i]; }
|
||||
for (; i < bytes; ++i) { *op++ = 0; }
|
||||
for (i = 0; i < srclen; ++i) *op++ = srcp[srclen - 1 - i];
|
||||
for (; i < bytes; ++i) *op++ = 0;
|
||||
}
|
||||
|
||||
static IData getLine(std::string& str, IData fpi, size_t maxLen) VL_MT_SAFE {
|
||||
@@ -1652,7 +1661,8 @@ IData VL_SSCANF_INNX(int, const std::string& ld, const std::string& format, int
|
||||
...) VL_MT_SAFE {
|
||||
va_list ap;
|
||||
va_start(ap, argc);
|
||||
const IData got = _vl_vsscanf(nullptr, ld.length() * 8, nullptr, ld, format, ap);
|
||||
const IData got
|
||||
= _vl_vsscanf(nullptr, static_cast<int>(ld.length() * 8), nullptr, ld, format, ap);
|
||||
va_end(ap);
|
||||
return got;
|
||||
}
|
||||
@@ -1745,7 +1755,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
|
||||
}
|
||||
|
||||
@@ -1941,7 +1954,7 @@ std::string VL_SUBSTR_N(const std::string& lhs, IData rhs, IData ths) VL_PURE {
|
||||
|
||||
IData VL_ATOI_N(const std::string& str, int base) VL_PURE {
|
||||
std::string str_mod = str;
|
||||
// IEEE 1800-2017 6.16.9 says '_' may exist.
|
||||
// IEEE 1800-2023 6.16.9 says '_' may exist.
|
||||
str_mod.erase(std::remove(str_mod.begin(), str_mod.end(), '_'), str_mod.end());
|
||||
|
||||
errno = 0;
|
||||
@@ -2033,22 +2046,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;
|
||||
@@ -2058,58 +2075,55 @@ 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)) {
|
||||
VL_WARN_MT(m_filename.c_str(), m_linenum, "",
|
||||
"$readmem file ended before specified final address (IEEE 1800-2017 21.4)");
|
||||
"$readmem file ended before specified final address (IEEE 1800-2023 21.4)");
|
||||
}
|
||||
|
||||
return false; // EOF
|
||||
addrr = m_addr;
|
||||
return inData; // EOF
|
||||
}
|
||||
void VlReadMem::setData(void* valuep, const std::string& rhs) {
|
||||
const QData shift = m_hex ? 4ULL : 1ULL;
|
||||
@@ -2117,8 +2131,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;
|
||||
@@ -2258,6 +2273,7 @@ void VL_READMEM_N(bool hex, // Hex format, else binary
|
||||
QData addr = 0;
|
||||
std::string value;
|
||||
if (rmem.get(addr /*ref*/, value /*ref*/)) {
|
||||
// printf("readmem.get [%" PRIu64 "]=%s\n", addr, value.c_str());
|
||||
if (VL_UNLIKELY(addr < static_cast<QData>(array_lsb)
|
||||
|| addr >= static_cast<QData>(array_lsb + depth))) {
|
||||
VL_FATAL_MT(filename.c_str(), rmem.linenum(), "",
|
||||
@@ -2412,6 +2428,22 @@ uint64_t vl_time_pow10(int n) {
|
||||
return pow10[n];
|
||||
}
|
||||
|
||||
std::string vl_timescaled_double(double value, const char* format) VL_PURE {
|
||||
const char* suffixp = "s";
|
||||
// clang-format off
|
||||
if (value >= 1e0) { suffixp = "s"; value *= 1e0; }
|
||||
else if (value >= 1e-3) { suffixp = "ms"; value *= 1e3; }
|
||||
else if (value >= 1e-6) { suffixp = "us"; value *= 1e6; }
|
||||
else if (value >= 1e-9) { suffixp = "ns"; value *= 1e9; }
|
||||
else if (value >= 1e-12) { suffixp = "ps"; value *= 1e12; }
|
||||
else if (value >= 1e-15) { suffixp = "fs"; value *= 1e15; }
|
||||
else if (value >= 1e-18) { suffixp = "as"; value *= 1e18; }
|
||||
// clang-format on
|
||||
char valuestr[100];
|
||||
VL_SNPRINTF(valuestr, 100, format, value, suffixp);
|
||||
return std::string{valuestr}; // Gets converted to string, so no ref to stack
|
||||
}
|
||||
|
||||
void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp,
|
||||
const VerilatedContext* contextp) VL_MT_SAFE {
|
||||
VL_PRINTF_MT("Time scale of %s is %s / %s\n", namep, timeunitp,
|
||||
@@ -2435,10 +2467,12 @@ 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);
|
||||
for (std::size_t i = 0, id = 1; i < m_fdFreeMct.size(); ++i, ++id) m_fdFreeMct[i] = id;
|
||||
IData id = 1;
|
||||
for (std::size_t i = 0; i < m_fdFreeMct.size(); ++i, ++id) m_fdFreeMct[i] = id;
|
||||
}
|
||||
|
||||
// Must declare here not in interface, as otherwise forward declarations not known
|
||||
@@ -2544,6 +2578,18 @@ 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;
|
||||
}
|
||||
void VerilatedContext::randReset(int val) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_randReset = val;
|
||||
@@ -2611,8 +2657,24 @@ void VerilatedContext::internalsDump() const VL_MT_SAFE {
|
||||
}
|
||||
|
||||
void VerilatedContext::addModel(VerilatedModel* modelp) {
|
||||
threadPoolp(); // Ensure thread pool is created, so m_threads cannot change any more
|
||||
if (!quiet()) {
|
||||
// CPU time isn't read as starting point until model creation, so that quiet() is set
|
||||
// Thus if quiet(), avoids slow OS read affecting some usages that make many models
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_ns.m_cpuTimeStart.start();
|
||||
m_ns.m_wallTimeStart.start();
|
||||
}
|
||||
|
||||
// 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;
|
||||
msg << "VerilatedContext has " << m_threads << " threads but model '"
|
||||
@@ -2645,6 +2707,7 @@ VerilatedContext::enableExecutionProfiler(VerilatedVirtualBase* (*construct)(Ver
|
||||
|
||||
//======================================================================
|
||||
// VerilatedContextImp:: Methods - command line
|
||||
|
||||
void VerilatedContextImp::commandArgsGuts(int argc, const char** argv)
|
||||
VL_MT_SAFE_EXCLUDES(m_argMutex) {
|
||||
const VerilatedLockGuard lock{m_argMutex};
|
||||
@@ -2741,6 +2804,8 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
|
||||
profExecFilename(str);
|
||||
} else if (commandArgVlString(arg, "+verilator+prof+vlt+file+", str)) {
|
||||
profVltFilename(str);
|
||||
} else if (arg == "+verilator+quiet") {
|
||||
quiet(true);
|
||||
} else if (commandArgVlUint64(arg, "+verilator+rand+reset+", u64, 0, 2)) {
|
||||
randReset(static_cast<int>(u64));
|
||||
} else if (commandArgVlUint64(arg, "+verilator+seed+", u64, 1,
|
||||
@@ -2786,7 +2851,7 @@ bool VerilatedContextImp::commandArgVlUint64(const std::string& arg, const std::
|
||||
VL_FATAL_MT("COMMAND_LINE", 0, "", msg.c_str());
|
||||
};
|
||||
|
||||
if (std::any_of(str.begin(), str.end(), [](int c) { return !std::isdigit(c); })) fail();
|
||||
if (std::any_of(str.cbegin(), str.cend(), [](int c) { return !std::isdigit(c); })) fail();
|
||||
char* end;
|
||||
valuer = std::strtoull(str.c_str(), &end, 10);
|
||||
if (errno == ERANGE) fail("Value out of range of uint64_t");
|
||||
@@ -2818,6 +2883,36 @@ uint64_t VerilatedContextImp::randSeedDefault64() const VL_MT_SAFE {
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// VerilatedContext:: Statistics
|
||||
|
||||
double VerilatedContext::statCpuTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
return m_ns.m_cpuTimeStart.deltaTime();
|
||||
}
|
||||
double VerilatedContext::statWallTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
return m_ns.m_wallTimeStart.deltaTime();
|
||||
}
|
||||
void VerilatedContext::statsPrintSummary() VL_MT_UNSAFE {
|
||||
if (quiet()) return;
|
||||
VL_PRINTF("- S i m u l a t i o n R e p o r t: %s %s\n", Verilated::productName(),
|
||||
Verilated::productVersion());
|
||||
const std::string endwhy = gotError() ? "$stop" : gotFinish() ? "$finish" : "end";
|
||||
const double simtimeInUnits = VL_TIME_Q() * vl_time_multiplier(timeunit())
|
||||
* vl_time_multiplier(timeprecision() - timeunit());
|
||||
const std::string simtime = vl_timescaled_double(simtimeInUnits);
|
||||
const double walltime = statWallTimeSinceStart();
|
||||
const double cputime = statCpuTimeSinceStart();
|
||||
const std::string simtimePerf
|
||||
= vl_timescaled_double((cputime != 0.0) ? (simtimeInUnits / cputime) : 0, "%0.3f %s");
|
||||
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 %u threads; alloced %0.0f MB\n", cputime,
|
||||
threadsInModels(), modelMB);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// VerilatedContext:: Methods - scopes
|
||||
|
||||
@@ -2856,6 +2951,36 @@ const VerilatedScopeNameMap* VerilatedContext::scopeNameMap() VL_MT_SAFE {
|
||||
return &(impp()->m_impdatap->m_nameMap);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// VerilatedContext:: Methods - trace
|
||||
|
||||
void VerilatedContext::trace(VerilatedTraceBaseC* tfp, int levels, int options) VL_MT_SAFE {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("+ VerilatedContext::trace\n"););
|
||||
if (tfp->isOpen()) {
|
||||
VL_FATAL_MT("", 0, "",
|
||||
"Testbench C call to 'VerilatedContext::trace()' must not be called"
|
||||
" after 'VerilatedTrace*::open()'\n");
|
||||
}
|
||||
{
|
||||
// Legacy usage may call {modela}->trace(...) then {modelb}->trace(...)
|
||||
// So check for and suppress second and later calls
|
||||
if (tfp->modelConnected()) return;
|
||||
tfp->modelConnected(true);
|
||||
}
|
||||
// We rely on m_ns.m_traceBaseModelCbs being stable when trace() is called
|
||||
// nope: const VerilatedLockGuard lock{m_mutex};
|
||||
if (m_ns.m_traceBaseModelCbs.empty())
|
||||
VL_FATAL_MT("", 0, "",
|
||||
"Testbench C call to 'VerilatedContext::trace()' requires model(s) Verilated"
|
||||
" with --trace or --trace-vcd option");
|
||||
for (auto& cbr : m_ns.m_traceBaseModelCbs) cbr(tfp, levels, options);
|
||||
}
|
||||
void VerilatedContext::traceBaseModelCbAdd(traceBaseModelCb_t cb) VL_MT_SAFE {
|
||||
// Model creation registering a callback for when Verilated::trace() called
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_ns.m_traceBaseModelCbs.push_back(cb);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// VerilatedSyms:: Methods
|
||||
|
||||
@@ -3027,15 +3152,24 @@ 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 -c %" 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
|
||||
if (false && needSize) {} // Unused argument
|
||||
(void)needSize; // Unused argument
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -3234,7 +3368,7 @@ void* VerilatedScope::exportFindNullError(int funcnum) VL_MT_SAFE {
|
||||
// Slowpath - Called only when find has failed
|
||||
const std::string msg = ("Testbench C called '"s + VerilatedImp::exportName(funcnum)
|
||||
+ "' but scope wasn't set, perhaps due to dpi import call without "
|
||||
+ "'context', or missing svSetScope. See IEEE 1800-2017 35.5.3.");
|
||||
+ "'context', or missing svSetScope. See IEEE 1800-2023 35.5.3.");
|
||||
VL_FATAL_MT("unknown", 0, "", msg.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
@@ -3300,3 +3434,7 @@ void VlDeleter::deleteAll() VL_EXCLUDES(m_mutex) VL_EXCLUDES(m_deleteMutex) VL_M
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// OS functions (last, so we have minimal OS dependencies above)
|
||||
|
||||
#define VL_ALLOW_VERILATEDOS_C
|
||||
#include "verilatedos_c.h"
|
||||
|
||||
+36
-2
@@ -101,6 +101,7 @@ class VerilatedScope;
|
||||
class VerilatedScopeNameMap;
|
||||
template <class, class>
|
||||
class VerilatedTrace;
|
||||
class VerilatedTraceBaseC;
|
||||
class VerilatedTraceConfig;
|
||||
class VerilatedVar;
|
||||
class VerilatedVarNameMap;
|
||||
@@ -347,6 +348,10 @@ class VerilatedContext VL_NOT_FINAL {
|
||||
friend class VerilatedContextImp;
|
||||
|
||||
protected:
|
||||
// TYPES
|
||||
using traceBaseModelCb_t
|
||||
= std::function<void(VerilatedTraceBaseC*, int, int)>; // Type of traceBaseModel callbacks
|
||||
|
||||
// MEMBERS
|
||||
// Slow path variables
|
||||
mutable VerilatedMutex m_mutex; // Mutex for most s_s/s_ns members
|
||||
@@ -354,13 +359,14 @@ protected:
|
||||
struct Serialized final { // All these members serialized/deserialized
|
||||
// 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
|
||||
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
|
||||
bool m_gotError = false; // A $finish statement executed
|
||||
bool m_gotFinish = false; // A $finish or $stop statement executed
|
||||
uint64_t m_time = 0; // Current $time (unscaled), 0=at zero, or legacy
|
||||
bool m_quiet = false; // Quiet, no summary report
|
||||
// Slow path
|
||||
int8_t m_timeunit; // Time unit as 0..15
|
||||
int8_t m_timeprecision; // Time precision as 0..15
|
||||
@@ -390,6 +396,10 @@ 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
|
||||
} m_ns;
|
||||
|
||||
mutable VerilatedMutex m_argMutex; // Protect m_argVec, m_argVecLoaded
|
||||
@@ -405,6 +415,8 @@ protected:
|
||||
const std::unique_ptr<VerilatedContextImpData> m_impdatap;
|
||||
// Number of threads to use for simulation (size of m_threadPool + 1 for main thread)
|
||||
unsigned m_threads = std::thread::hardware_concurrency();
|
||||
// Number of threads in added models
|
||||
unsigned m_threadsInModels = 0;
|
||||
// The thread pool shared by all models added to this context
|
||||
std::unique_ptr<VerilatedVirtualBase> m_threadPool;
|
||||
// The execution profiler shared by all models added to this context
|
||||
@@ -489,6 +501,10 @@ public:
|
||||
void gotFinish(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;
|
||||
/// Return if quiet enabled
|
||||
bool quiet() const VL_MT_SAFE { return m_s.m_quiet; }
|
||||
/// Select initial value of otherwise uninitialized signals.
|
||||
/// 0 = Set to zeros
|
||||
/// 1 = Set all bits to one
|
||||
@@ -501,6 +517,13 @@ public:
|
||||
/// Set default random seed, 0 = seed it automatically
|
||||
int randSeed() const VL_MT_SAFE { return m_s.m_randSeed; }
|
||||
|
||||
/// Return statistic: CPU time delta from model created until now
|
||||
double statCpuTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
/// Return statistic: Wall time delta from model created until now
|
||||
double statWallTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
/// Print statistics summary (if not quiet)
|
||||
void statsPrintSummary() VL_MT_UNSAFE;
|
||||
|
||||
// Time handling
|
||||
/// Returns current simulation time in units of timeprecision().
|
||||
///
|
||||
@@ -544,10 +567,14 @@ public:
|
||||
|
||||
/// Get number of threads used for simulation (including the main thread)
|
||||
unsigned threads() const { return m_threads; }
|
||||
/// Get number of threads in added models (for statistical use only)
|
||||
unsigned threadsInModels() const { return m_threadsInModels; }
|
||||
/// Set number of threads used for simulation (including the main thread)
|
||||
/// Can only be called before the thread pool is created (before first model is added).
|
||||
void threads(unsigned n);
|
||||
|
||||
/// Trace signals in models within the context; called by application code
|
||||
void trace(VerilatedTraceBaseC* tfp, int levels, int options = 0);
|
||||
/// Allow traces to at some point be enabled (disables some optimizations)
|
||||
void traceEverOn(bool flag) VL_MT_SAFE {
|
||||
if (flag) calcUnusedSigs(true);
|
||||
@@ -571,8 +598,8 @@ public:
|
||||
return reinterpret_cast<const VerilatedContextImp*>(this);
|
||||
}
|
||||
|
||||
// Internal: Model and thread setup
|
||||
void addModel(VerilatedModel*);
|
||||
|
||||
VerilatedVirtualBase* threadPoolp();
|
||||
void prepareClone();
|
||||
VerilatedVirtualBase* threadPoolpOnClone();
|
||||
@@ -598,6 +625,10 @@ public:
|
||||
void profVltFilename(const std::string& flag) VL_MT_SAFE;
|
||||
std::string profVltFilename() const VL_MT_SAFE;
|
||||
|
||||
// Internal: SMT solver program
|
||||
void solverProgram(const std::string& flag) VL_MT_SAFE;
|
||||
std::string solverProgram() const VL_MT_SAFE;
|
||||
|
||||
// Internal: Find scope
|
||||
const VerilatedScope* scopeFind(const char* namep) const VL_MT_SAFE;
|
||||
const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE;
|
||||
@@ -606,6 +637,9 @@ public:
|
||||
static constexpr size_t serialized1Size() VL_PURE { return sizeof(m_s); }
|
||||
void* serialized1Ptr() VL_MT_UNSAFE { return &m_s; }
|
||||
|
||||
// Internal: trace registration
|
||||
void traceBaseModelCbAdd(traceBaseModelCb_t cb) VL_MT_SAFE;
|
||||
|
||||
// Internal: Check magic number
|
||||
static void checkMagic(const VerilatedContext* contextp);
|
||||
void selfTestClearMagic() { m_magic = 0x2; }
|
||||
|
||||
@@ -225,7 +225,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 $@; \
|
||||
|
||||
@@ -121,7 +121,7 @@ private:
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VerilatedCovImp(VerilatedContext* contextp)
|
||||
explicit VerilatedCovImp(VerilatedContext* contextp)
|
||||
: m_contextp{contextp} {}
|
||||
VL_UNCOPYABLE(VerilatedCovImp);
|
||||
|
||||
|
||||
@@ -165,7 +165,7 @@ public:
|
||||
/// Return default filename for the current thread
|
||||
static std::string defaultFilename() VL_MT_SAFE { return threadCovp()->defaultFilename(); }
|
||||
/// Write all coverage data to a file for the current thread
|
||||
static void write(const std::string filename) VL_MT_SAFE { threadCovp()->write(filename); }
|
||||
static void write(const std::string& filename) VL_MT_SAFE { threadCovp()->write(filename); }
|
||||
static void write() VL_MT_SAFE { write(defaultFilename()); }
|
||||
/// Clear coverage points (and call delete on all items) for the current thread
|
||||
static void clear() VL_MT_SAFE { threadCovp()->clear(); }
|
||||
|
||||
@@ -770,6 +770,36 @@ int svGetCallerInfo(const char** fileNamepp, int* lineNumberp) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Time
|
||||
|
||||
int svGetTime(const svScope scope, svTimeVal* time) {
|
||||
if (VL_UNLIKELY(!time)) return -1;
|
||||
const QData qtime = VL_TIME_Q();
|
||||
VlWide<2> itime;
|
||||
VL_SET_WQ(itime, qtime);
|
||||
time->low = itime[0];
|
||||
time->high = itime[1];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int svGetTimeUnit(const svScope scope, int32_t* time_unit) {
|
||||
if (VL_UNLIKELY(!time_unit)) return -1;
|
||||
const VerilatedScope* const vscopep = reinterpret_cast<const VerilatedScope*>(scope);
|
||||
if (!vscopep) { // Null asks for global, not unlikely
|
||||
*time_unit = Verilated::threadContextp()->timeunit();
|
||||
} else {
|
||||
*time_unit = vscopep->timeunit();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int svGetTimePrecision(const svScope scope, int32_t* time_precision) {
|
||||
if (VL_UNLIKELY(!time_precision)) return -1;
|
||||
*time_precision = Verilated::threadContextp()->timeprecision();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Disables
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ class VerilatedFstBuffer VL_NOT_FINAL {
|
||||
/// Create a FST dump file in C standalone (no SystemC) simulations.
|
||||
/// Also derived for use in SystemC simulations.
|
||||
|
||||
class VerilatedFstC VL_NOT_FINAL {
|
||||
class VerilatedFstC VL_NOT_FINAL : public VerilatedTraceBaseC {
|
||||
VerilatedFst m_sptrace; // Trace file being created
|
||||
|
||||
// CONSTRUCTORS
|
||||
@@ -208,11 +208,14 @@ public:
|
||||
// METHODS - User called
|
||||
|
||||
/// Return if file is open
|
||||
bool isOpen() const VL_MT_SAFE { return m_sptrace.isOpen(); }
|
||||
bool isOpen() const override VL_MT_SAFE { return m_sptrace.isOpen(); }
|
||||
/// Open a new FST file
|
||||
virtual void open(const char* filename) VL_MT_SAFE { m_sptrace.open(filename); }
|
||||
/// Close dump
|
||||
void close() VL_MT_SAFE { m_sptrace.close(); }
|
||||
void close() VL_MT_SAFE {
|
||||
m_sptrace.close();
|
||||
modelConnected(false);
|
||||
}
|
||||
/// Flush dump
|
||||
void flush() VL_MT_SAFE { m_sptrace.flush(); }
|
||||
/// Write one cycle of dump data
|
||||
|
||||
+149
-8
@@ -115,6 +115,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 +155,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 +219,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 +246,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
|
||||
@@ -263,7 +271,7 @@ static inline QData VL_EXTENDSIGN_Q(int lbits, QData lhs) VL_PURE {
|
||||
extern void _vl_debug_print_w(int lbits, WDataInP const iwp) VL_MT_SAFE;
|
||||
|
||||
//=========================================================================
|
||||
// Pli macros
|
||||
// Time handling
|
||||
|
||||
// clang-format off
|
||||
|
||||
@@ -321,6 +329,8 @@ uint64_t VerilatedContext::time() const VL_MT_SAFE {
|
||||
double vl_time_multiplier(int scale) VL_PURE;
|
||||
// Return power of 10. e.g. returns 100 if n==2
|
||||
uint64_t vl_time_pow10(int n) VL_PURE;
|
||||
// Return time as string with timescale suffix
|
||||
std::string vl_timescaled_double(double value, const char* format = "%0.0f%s") VL_PURE;
|
||||
|
||||
//=========================================================================
|
||||
// Functional macros/routines
|
||||
@@ -502,19 +512,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); \
|
||||
@@ -1309,6 +1318,26 @@ static inline void _vl_insert_WI(WDataOutP iowp, IData ld, int hbit, int lbit,
|
||||
}
|
||||
}
|
||||
|
||||
// Copy bits from lwp[hbit:lbit] to low bits of lhsr. rbits is real width of lshr
|
||||
static inline void _vl_insert_IW(IData& lhsr, WDataInP const lwp, int hbit, int lbit,
|
||||
int rbits = 0) VL_MT_SAFE {
|
||||
const int hoffset = VL_BITBIT_E(hbit);
|
||||
const int loffset = VL_BITBIT_E(lbit);
|
||||
const int hword = VL_BITWORD_E(hbit);
|
||||
const int lword = VL_BITWORD_E(lbit);
|
||||
const IData cleanmask = VL_MASK_I(rbits);
|
||||
if (hword == lword) {
|
||||
const IData insmask = (VL_MASK_I(hoffset - loffset + 1));
|
||||
lhsr = (lhsr & ~insmask) | ((lwp[lword] >> loffset) & (insmask & cleanmask));
|
||||
} else {
|
||||
const int nbitsonright = VL_IDATASIZE - loffset; // bits that filled by lword
|
||||
const IData hinsmask = (VL_MASK_E(hoffset - 0 + 1)) << nbitsonright;
|
||||
const IData linsmask = VL_MASK_E(VL_EDATASIZE - loffset);
|
||||
lhsr = (lhsr & ~linsmask) | ((lwp[lword] >> loffset) & (linsmask & cleanmask));
|
||||
lhsr = (lhsr & ~hinsmask) | ((lwp[hword] << nbitsonright) & (hinsmask & cleanmask));
|
||||
}
|
||||
}
|
||||
|
||||
// INTERNAL: Stuff large LHS bit 0++ into OUTPUT at specified offset
|
||||
// lwp may be "dirty"
|
||||
static inline void _vl_insert_WW(WDataOutP iowp, WDataInP const lwp, int hbit, int lbit,
|
||||
@@ -1556,6 +1585,13 @@ static inline void VL_ASSIGN_DYN_Q(VlQueue<T>& q, int elem_size, int lbits, QDat
|
||||
for (int i = 0; i < size; ++i) q.at(i) = (T)((from >> (i * elem_size)) & mask);
|
||||
}
|
||||
|
||||
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);
|
||||
for (size_t i = 0; i < T_Depth; ++i)
|
||||
q[i] = (T)((from >> ((T_Depth - 1 - i) * elem_size)) & mask);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline IData VL_DYN_TO_I(const VlQueue<T>& q, int elem_size) {
|
||||
IData ret = 0;
|
||||
@@ -1570,6 +1606,20 @@ static inline QData VL_DYN_TO_Q(const VlQueue<T>& q, int 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) {
|
||||
IData ret = 0;
|
||||
for (size_t i = 0; i < T_Depth; ++i) ret |= q[T_Depth - 1 - i] << (i * elem_size);
|
||||
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) {
|
||||
QData ret = 0;
|
||||
for (size_t i = 0; i < T_Depth; ++i) ret |= q[T_Depth - 1 - i] << (i * elem_size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -2083,6 +2133,97 @@ static inline void VL_ASSIGNSEL_WW(int rbits, int obits, int lsb, WDataOutP iowp
|
||||
_vl_insert_WW(iowp, rwp, lsb + obits - 1, lsb, rbits);
|
||||
}
|
||||
|
||||
//====================================================
|
||||
// Range assignments
|
||||
|
||||
// These additional functions copy bits range [obis+roffset-1:roffset] from rhs to lower bits
|
||||
// of lhs(select before assigning). Rhs should always be wider than lhs.
|
||||
static inline void VL_SELASSIGN_II(int rbits, int obits, CData& lhsr, IData rhs,
|
||||
int roffset) VL_PURE {
|
||||
_vl_insert_II(lhsr, rhs >> roffset, obits - 1, 0, rbits);
|
||||
}
|
||||
static inline void VL_SELASSIGN_II(int rbits, int obits, SData& lhsr, IData rhs,
|
||||
int roffset) VL_PURE {
|
||||
_vl_insert_II(lhsr, rhs >> roffset, obits - 1, 0, rbits);
|
||||
}
|
||||
static inline void VL_SELASSIGN_II(int rbits, int obits, IData& lhsr, IData rhs,
|
||||
int roffset) VL_PURE {
|
||||
_vl_insert_II(lhsr, rhs >> roffset, obits - 1, 0, rbits);
|
||||
}
|
||||
static inline void VL_SELASSIGN_IQ(int rbits, int obits, CData& lhsr, QData rhs,
|
||||
int roffset) VL_PURE {
|
||||
// it will be truncated to right CData mask
|
||||
const CData cleanmask = VL_MASK_I(rbits);
|
||||
const CData insmask = VL_MASK_I(obits);
|
||||
lhsr = (lhsr & ~insmask) | (static_cast<CData>(rhs >> roffset) & (insmask & cleanmask));
|
||||
}
|
||||
static inline void VL_SELASSIGN_IQ(int rbits, int obits, SData& lhsr, QData rhs,
|
||||
int roffset) VL_PURE {
|
||||
// it will be truncated to right CData mask
|
||||
const SData cleanmask = VL_MASK_I(rbits);
|
||||
const SData insmask = VL_MASK_I(obits);
|
||||
lhsr = (lhsr & ~insmask) | (static_cast<SData>(rhs >> roffset) & (insmask & cleanmask));
|
||||
}
|
||||
static inline void VL_SELASSIGN_IQ(int rbits, int obits, IData& lhsr, QData rhs,
|
||||
int roffset) VL_PURE {
|
||||
const IData cleanmask = VL_MASK_I(rbits);
|
||||
const IData insmask = VL_MASK_I(obits);
|
||||
lhsr = (lhsr & ~insmask) | (static_cast<IData>(rhs >> roffset) & (insmask & cleanmask));
|
||||
}
|
||||
|
||||
static inline void VL_SELASSIGN_QQ(int rbits, int obits, QData& lhsr, QData rhs,
|
||||
int roffset) VL_PURE {
|
||||
_vl_insert_QQ(lhsr, rhs >> roffset, obits - 1, 0, rbits);
|
||||
}
|
||||
|
||||
static inline void VL_SELASSIGN_IW(int rbits, int obits, CData& lhsr, WDataInP const rhs,
|
||||
int roffset) VL_MT_SAFE {
|
||||
IData l = static_cast<IData>(lhsr);
|
||||
_vl_insert_IW(l, rhs, roffset + obits - 1, roffset, rbits);
|
||||
lhsr = static_cast<CData>(l);
|
||||
}
|
||||
static inline void VL_SELASSIGN_IW(int rbits, int obits, SData& lhsr, WDataInP const rhs,
|
||||
int roffset) VL_MT_SAFE {
|
||||
IData l = static_cast<IData>(lhsr);
|
||||
_vl_insert_IW(l, rhs, roffset + obits - 1, roffset, rbits);
|
||||
lhsr = static_cast<SData>(l);
|
||||
}
|
||||
static inline void VL_SELASSIGN_IW(int rbits, int obits, IData& lhsr, WDataInP const rhs,
|
||||
int roffset) VL_MT_SAFE {
|
||||
_vl_insert_IW(lhsr, rhs, roffset + obits - 1, roffset, rbits);
|
||||
}
|
||||
static inline void VL_SELASSIGN_QW(int rbits, int obits, QData& lhsr, WDataInP const rhs,
|
||||
int roffset) VL_MT_SAFE {
|
||||
// assert VL_QDATASIZE >= rbits > VL_IDATASIZE;
|
||||
IData low = static_cast<IData>(lhsr);
|
||||
IData high = static_cast<IData>(lhsr >> VL_IDATASIZE);
|
||||
if (obits <= VL_IDATASIZE) {
|
||||
_vl_insert_IW(low, rhs, obits + roffset - 1, roffset, VL_IDATASIZE);
|
||||
} else {
|
||||
_vl_insert_IW(low, rhs, roffset + VL_IDATASIZE - 1, roffset, VL_IDATASIZE);
|
||||
_vl_insert_IW(high, rhs, roffset + obits - 1, roffset + VL_IDATASIZE,
|
||||
rbits - VL_IDATASIZE);
|
||||
}
|
||||
lhsr = (static_cast<QData>(high) << VL_IDATASIZE) | low;
|
||||
}
|
||||
|
||||
static inline void VL_SELASSIGN_WW(int rbits, int obits, WDataOutP iowp, WDataInP const rwp,
|
||||
int roffset) VL_MT_SAFE {
|
||||
// assert rbits > VL_QDATASIZE
|
||||
const int wordoff = roffset / VL_EDATASIZE;
|
||||
const int lsb = roffset & VL_SIZEBITS_E;
|
||||
const int upperbits = lsb == 0 ? 0 : VL_EDATASIZE - lsb;
|
||||
// If roffset is not aligned, we copy some bits to align it.
|
||||
if (lsb != 0) {
|
||||
const int w = obits < upperbits ? obits : upperbits;
|
||||
const int insmask = VL_MASK_E(w);
|
||||
iowp[0] = (iowp[0] & ~insmask) | ((rwp[wordoff] >> lsb) & insmask);
|
||||
if (w == obits) return;
|
||||
obits -= w;
|
||||
}
|
||||
_vl_insert_WW(iowp, rwp + wordoff + (lsb != 0), upperbits + obits - 1, upperbits, rbits);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Triops
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ IData VL_DIST_ERLANG(IData& seedr, IData uk, IData umean) VL_MT_SAFE {
|
||||
return 0;
|
||||
}
|
||||
double x = 1.0;
|
||||
for (int32_t i = 1; i <= k; i++) { x = x * _vl_dbase_uniform(seedr, 0, 1); }
|
||||
for (int32_t i = 1; i <= k; i++) x = x * _vl_dbase_uniform(seedr, 0, 1);
|
||||
const double a = static_cast<double>(mean);
|
||||
const double b = static_cast<double>(k);
|
||||
double r = -a * log(x) / b;
|
||||
|
||||
@@ -0,0 +1,424 @@
|
||||
// -*- 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;
|
||||
|
||||
it->second->set(std::move(value));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VlRandomizer::hard(std::string&& constraint) {
|
||||
m_constraints.emplace_back(std::move(constraint));
|
||||
}
|
||||
|
||||
#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,120 @@
|
||||
// -*- 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
|
||||
|
||||
public:
|
||||
VlRandomVar(const char* name, int width, void* datap)
|
||||
: m_name{name}
|
||||
, m_datap{datap}
|
||||
, m_width{width} {}
|
||||
const char* name() const { return m_name; }
|
||||
int width() const { return m_width; }
|
||||
void* datap() const { return m_datap; }
|
||||
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
|
||||
|
||||
// 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) {
|
||||
auto it = m_vars.find(name);
|
||||
if (it != m_vars.end()) return;
|
||||
m_vars[name] = std::make_shared<const VlRandomVar>(name, width, &var);
|
||||
}
|
||||
void hard(std::string&& constraint);
|
||||
#ifdef VL_DEBUG
|
||||
void dump() const;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
@@ -59,7 +59,7 @@ void VlDelayScheduler::resume() {
|
||||
#endif
|
||||
bool resumed = false;
|
||||
|
||||
while (!m_queue.empty() && (m_queue.begin()->first == m_context.time())) {
|
||||
while (!m_queue.empty() && (m_queue.cbegin()->first == m_context.time())) {
|
||||
VlCoroutineHandle handle = std::move(m_queue.begin()->second);
|
||||
m_queue.erase(m_queue.begin());
|
||||
handle.resume();
|
||||
@@ -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) {
|
||||
@@ -80,7 +87,7 @@ void VlDelayScheduler::resume() {
|
||||
}
|
||||
|
||||
uint64_t VlDelayScheduler::nextTimeSlot() const {
|
||||
if (!m_queue.empty()) return m_queue.begin()->first;
|
||||
if (!m_queue.empty()) return m_queue.cbegin()->first;
|
||||
if (m_zeroDelayed.empty())
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "%Error: There is no next time slot scheduled");
|
||||
return m_context.time();
|
||||
|
||||
+19
-12
@@ -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
|
||||
@@ -180,7 +187,7 @@ public:
|
||||
bool empty() const { return m_queue.empty() && m_zeroDelayed.empty(); }
|
||||
// Are there coroutines to resume at the current simulation time?
|
||||
bool awaitingCurrentTime() const {
|
||||
return (!m_queue.empty() && (m_queue.begin()->first <= m_context.time()))
|
||||
return (!m_queue.empty() && (m_queue.cbegin()->first <= m_context.time()))
|
||||
|| !m_zeroDelayed.empty();
|
||||
}
|
||||
#ifdef VL_DEBUG
|
||||
@@ -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>
|
||||
@@ -179,6 +180,21 @@ public:
|
||||
, m_useFstWriterThread{useFstWriterThread} {}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedTraceBaseC - base class of all Verilated*C trace classes
|
||||
// Internal use only
|
||||
|
||||
class VerilatedTraceBaseC VL_NOT_FINAL {
|
||||
bool m_modelConnected = false; // Model connected by calling Verilated::trace()
|
||||
public:
|
||||
/// True if file currently open
|
||||
virtual bool isOpen() const VL_MT_SAFE = 0;
|
||||
|
||||
// internal use only
|
||||
bool modelConnected() const VL_MT_SAFE { return m_modelConnected; }
|
||||
void modelConnected(bool flag) VL_MT_SAFE { m_modelConnected = flag; }
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedTrace
|
||||
|
||||
@@ -205,10 +221,6 @@ private:
|
||||
friend OffloadBuffer;
|
||||
|
||||
struct CallbackRecord final {
|
||||
// Note: would make these fields const, but some old STL implementations
|
||||
// (the one in Ubuntu 14.04 with GCC 4.8.4 in particular) use the
|
||||
// assignment operator on inserting into collections, so they don't work
|
||||
// with const fields...
|
||||
union { // The callback
|
||||
const initCb_t m_initCb;
|
||||
const dumpCb_t m_dumpCb;
|
||||
@@ -283,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);
|
||||
@@ -582,7 +594,7 @@ public:
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_WDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep += 2;
|
||||
for (int i = 0; i < (bits + 31) / 32; ++i) { *m_offloadBufferWritep++ = newvalp[i]; }
|
||||
for (int i = 0; i < (bits + 31) / 32; ++i) *m_offloadBufferWritep++ = newvalp[i];
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
void chgDouble(uint32_t code, double newval) {
|
||||
|
||||
@@ -60,22 +60,6 @@ static double timescaleToDouble(const char* unitp) VL_PURE {
|
||||
return value;
|
||||
}
|
||||
|
||||
static std::string doubleToTimescale(double value) VL_PURE {
|
||||
const char* suffixp = "s";
|
||||
// clang-format off
|
||||
if (value >= 1e0) { suffixp = "s"; value *= 1e0; }
|
||||
else if (value >= 1e-3) { suffixp = "ms"; value *= 1e3; }
|
||||
else if (value >= 1e-6) { suffixp = "us"; value *= 1e6; }
|
||||
else if (value >= 1e-9) { suffixp = "ns"; value *= 1e9; }
|
||||
else if (value >= 1e-12) { suffixp = "ps"; value *= 1e12; }
|
||||
else if (value >= 1e-15) { suffixp = "fs"; value *= 1e15; }
|
||||
else if (value >= 1e-18) { suffixp = "as"; value *= 1e18; }
|
||||
// clang-format on
|
||||
char valuestr[100];
|
||||
VL_SNPRINTF(valuestr, 100, "%0.0f%s", value, suffixp);
|
||||
return valuestr; // Gets converted to string, so no ref to stack
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Buffer management
|
||||
|
||||
@@ -190,13 +174,16 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
|
||||
|
||||
//===
|
||||
// Rare commands
|
||||
case VerilatedTraceOffloadCommand::TIME_CHANGE:
|
||||
case VerilatedTraceOffloadCommand::TIME_CHANGE: {
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command TIME_CHANGE " << top);
|
||||
readp -= 1; // No code in this command, undo increment
|
||||
emitTimeChange(*reinterpret_cast<const uint64_t*>(readp));
|
||||
const uint64_t timeui
|
||||
= static_cast<uint64_t>(*reinterpret_cast<const uint32_t*>(readp)) << 32ULL
|
||||
| static_cast<uint64_t>(*reinterpret_cast<const uint32_t*>(readp + 1));
|
||||
emitTimeChange(timeui);
|
||||
readp += 2;
|
||||
continue;
|
||||
|
||||
}
|
||||
case VerilatedTraceOffloadCommand::TRACE_BUFFER:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command TRACE_BUFFER " << top);
|
||||
readp -= 1; // No code in this command, undo increment
|
||||
@@ -418,7 +405,7 @@ bool VerilatedTrace<VL_SUB_T, VL_BUF_T>::declCode(uint32_t code, const std::stri
|
||||
|
||||
template <>
|
||||
std::string VerilatedTrace<VL_SUB_T, VL_BUF_T>::timeResStr() const {
|
||||
return doubleToTimescale(m_timeRes);
|
||||
return vl_timescaled_double(m_timeRes);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
@@ -574,7 +561,10 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
|
||||
|
||||
// Tell worker to update time point
|
||||
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::TIME_CHANGE;
|
||||
*reinterpret_cast<uint64_t*>(m_offloadBufferWritep + 1) = timeui;
|
||||
*reinterpret_cast<uint32_t*>(m_offloadBufferWritep + 1)
|
||||
= static_cast<uint32_t>(timeui >> 32ULL);
|
||||
*reinterpret_cast<uint32_t*>(m_offloadBufferWritep + 2)
|
||||
= static_cast<uint32_t>(timeui);
|
||||
m_offloadBufferWritep += 3;
|
||||
} else {
|
||||
// Update time point
|
||||
@@ -643,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__, "",
|
||||
|
||||
+239
-42
@@ -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
|
||||
@@ -615,7 +626,7 @@ public:
|
||||
|
||||
// inside (set membership operator)
|
||||
bool inside(const T_Value& value) const {
|
||||
return std::find(m_deque.begin(), m_deque.end(), value) != m_deque.end();
|
||||
return std::find(m_deque.cbegin(), m_deque.cend(), value) != m_deque.cend();
|
||||
}
|
||||
|
||||
// Return slice q[lsb:msb]
|
||||
@@ -776,13 +787,13 @@ public:
|
||||
// Reduction operators
|
||||
VlQueue min() const {
|
||||
if (m_deque.empty()) return VlQueue{};
|
||||
const auto it = std::min_element(m_deque.begin(), m_deque.end());
|
||||
const auto it = std::min_element(m_deque.cbegin(), m_deque.cend());
|
||||
return VlQueue::cons(*it);
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue min(Func with_func) const {
|
||||
if (m_deque.empty()) return VlQueue{};
|
||||
const auto it = std::min_element(m_deque.begin(), m_deque.end(),
|
||||
const auto it = std::min_element(m_deque.cbegin(), m_deque.cend(),
|
||||
[&with_func](const IData& a, const IData& b) {
|
||||
return with_func(0, a) < with_func(0, b);
|
||||
});
|
||||
@@ -790,13 +801,13 @@ public:
|
||||
}
|
||||
VlQueue max() const {
|
||||
if (m_deque.empty()) return VlQueue{};
|
||||
const auto it = std::max_element(m_deque.begin(), m_deque.end());
|
||||
const auto it = std::max_element(m_deque.cbegin(), m_deque.cend());
|
||||
return VlQueue::cons(*it);
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue max(Func with_func) const {
|
||||
if (m_deque.empty()) return VlQueue{};
|
||||
const auto it = std::max_element(m_deque.begin(), m_deque.end(),
|
||||
const auto it = std::max_element(m_deque.cbegin(), m_deque.cend(),
|
||||
[&with_func](const IData& a, const IData& b) {
|
||||
return with_func(0, a) < with_func(0, b);
|
||||
});
|
||||
@@ -817,40 +828,40 @@ public:
|
||||
}
|
||||
T_Value r_product() const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
auto it = m_deque.cbegin();
|
||||
T_Value out{*it};
|
||||
++it;
|
||||
for (; it != m_deque.end(); ++it) out *= *it;
|
||||
for (; it != m_deque.cend(); ++it) out *= *it;
|
||||
return out;
|
||||
}
|
||||
template <typename Func>
|
||||
T_Value r_product(Func with_func) const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
auto it = m_deque.cbegin();
|
||||
IData index = 0;
|
||||
T_Value out{with_func(index, *it)};
|
||||
++it;
|
||||
++index;
|
||||
for (; it != m_deque.end(); ++it) out *= with_func(index++, *it);
|
||||
for (; it != m_deque.cend(); ++it) out *= with_func(index++, *it);
|
||||
return out;
|
||||
}
|
||||
T_Value r_and() const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
auto it = m_deque.cbegin();
|
||||
T_Value out{*it};
|
||||
++it;
|
||||
for (; it != m_deque.end(); ++it) out &= *it;
|
||||
for (; it != m_deque.cend(); ++it) out &= *it;
|
||||
return out;
|
||||
}
|
||||
template <typename Func>
|
||||
T_Value r_and(Func with_func) const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
auto it = m_deque.cbegin();
|
||||
IData index = 0;
|
||||
T_Value out{with_func(index, *it)};
|
||||
++it;
|
||||
++index;
|
||||
for (; it != m_deque.end(); ++it) out &= with_func(index, *it);
|
||||
for (; it != m_deque.cend(); ++it) out &= with_func(index, *it);
|
||||
return out;
|
||||
}
|
||||
T_Value r_or() const {
|
||||
@@ -891,8 +902,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();
|
||||
}
|
||||
|
||||
@@ -948,7 +959,7 @@ public:
|
||||
// Return last element. Verilog: function int last(ref index)
|
||||
int last(T_Key& indexr) const {
|
||||
const auto it = m_map.crbegin();
|
||||
if (it == m_map.rend()) return 0;
|
||||
if (it == m_map.crend()) return 0;
|
||||
indexr = it->first;
|
||||
return 1;
|
||||
}
|
||||
@@ -1077,7 +1088,7 @@ public:
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> find_first(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
= std::find_if(m_map.cbegin(), m_map.cend(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
if (it == m_map.end()) return VlQueue<T_Value>{};
|
||||
@@ -1086,7 +1097,7 @@ public:
|
||||
template <typename Func>
|
||||
VlQueue<T_Key> find_first_index(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
= std::find_if(m_map.cbegin(), m_map.cend(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
if (it == m_map.end()) return VlQueue<T_Value>{};
|
||||
@@ -1094,19 +1105,17 @@ public:
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> find_last(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
const auto it = std::find_if(
|
||||
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);
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Key> find_last_index(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
const auto it = std::find_if(
|
||||
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);
|
||||
}
|
||||
@@ -1115,7 +1124,7 @@ public:
|
||||
VlQueue<T_Value> min() const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
const auto it = std::min_element(
|
||||
m_map.begin(), m_map.end(),
|
||||
m_map.cbegin(), m_map.cend(),
|
||||
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
|
||||
return a.second < b.second;
|
||||
});
|
||||
@@ -1125,7 +1134,7 @@ public:
|
||||
VlQueue<T_Value> min(Func with_func) const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
const auto it = std::min_element(
|
||||
m_map.begin(), m_map.end(),
|
||||
m_map.cbegin(), m_map.cend(),
|
||||
[&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);
|
||||
});
|
||||
@@ -1134,7 +1143,7 @@ public:
|
||||
VlQueue<T_Value> max() const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
const auto it = std::max_element(
|
||||
m_map.begin(), m_map.end(),
|
||||
m_map.cbegin(), m_map.cend(),
|
||||
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
|
||||
return a.second < b.second;
|
||||
});
|
||||
@@ -1144,7 +1153,7 @@ public:
|
||||
VlQueue<T_Value> max(Func with_func) const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
const auto it = std::max_element(
|
||||
m_map.begin(), m_map.end(),
|
||||
m_map.cbegin(), m_map.cend(),
|
||||
[&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);
|
||||
});
|
||||
@@ -1164,36 +1173,36 @@ public:
|
||||
}
|
||||
T_Value r_product() const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
auto it = m_map.cbegin();
|
||||
T_Value out{it->second};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out *= it->second;
|
||||
for (; it != m_map.cend(); ++it) out *= it->second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func>
|
||||
T_Value r_product(Func with_func) const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
auto it = m_map.cbegin();
|
||||
T_Value out{with_func(it->first, it->second)};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out *= with_func(it->first, it->second);
|
||||
for (; it != m_map.cend(); ++it) out *= with_func(it->first, it->second);
|
||||
return out;
|
||||
}
|
||||
T_Value r_and() const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
auto it = m_map.cbegin();
|
||||
T_Value out{it->second};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out &= it->second;
|
||||
for (; it != m_map.cend(); ++it) out &= it->second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func>
|
||||
T_Value r_and(Func with_func) const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
auto it = m_map.cbegin();
|
||||
T_Value out{with_func(it->first, it->second)};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out &= with_func(it->first, it->second);
|
||||
for (; it != m_map.cend(); ++it) out &= with_func(it->first, it->second);
|
||||
return out;
|
||||
}
|
||||
T_Value r_or() const {
|
||||
@@ -1311,7 +1320,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 {
|
||||
@@ -1388,7 +1401,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);
|
||||
@@ -1501,6 +1514,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 !=
|
||||
@@ -1513,6 +1535,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
|
||||
|
||||
@@ -1707,7 +1904,7 @@ public:
|
||||
T_Class* operator->() const { return m_objp; }
|
||||
// For 'if (ptr)...'
|
||||
operator bool() const { return m_objp; }
|
||||
// In SV A == B iff both are handles to the same object (IEEE 1800-2017 8.4)
|
||||
// In SV A == B iff both are handles to the same object (IEEE 1800-2023 8.4)
|
||||
template <typename T_OtherClass>
|
||||
bool operator==(const VlClassRef<T_OtherClass>& rhs) const {
|
||||
return m_objp == rhs.m_objp;
|
||||
|
||||
@@ -268,7 +268,7 @@ void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
|
||||
// We add output data to m_writep.
|
||||
// When it gets nearly full we dump it using this routine which calls write()
|
||||
// This is much faster than using buffered I/O
|
||||
if (VL_UNLIKELY(!isOpen())) return;
|
||||
if (VL_UNLIKELY(!m_isOpen)) return;
|
||||
const char* wp = m_wrBufp;
|
||||
while (true) {
|
||||
const ssize_t remaining = (m_writep - wp);
|
||||
|
||||
@@ -251,7 +251,7 @@ public:
|
||||
/// Class representing a VCD dump file in C standalone (no SystemC)
|
||||
/// simulations. Also derived for use in SystemC simulations.
|
||||
|
||||
class VerilatedVcdC VL_NOT_FINAL {
|
||||
class VerilatedVcdC VL_NOT_FINAL : public VerilatedTraceBaseC {
|
||||
VerilatedVcd m_sptrace; // Trace file being created
|
||||
|
||||
// CONSTRUCTORS
|
||||
@@ -267,7 +267,7 @@ public:
|
||||
// METHODS - User called
|
||||
|
||||
/// Return if file is open
|
||||
bool isOpen() const VL_MT_SAFE { return m_sptrace.isOpen(); }
|
||||
bool isOpen() const override VL_MT_SAFE { return m_sptrace.isOpen(); }
|
||||
/// Open a new VCD file
|
||||
/// This includes a complete header dump each time it is called,
|
||||
/// just as if this object was deleted and reconstructed.
|
||||
@@ -283,7 +283,10 @@ public:
|
||||
/// first may be removed. Cat files together to create viewable vcd.
|
||||
void rolloverSize(size_t size) VL_MT_SAFE { m_sptrace.rolloverSize(size); }
|
||||
/// Close dump
|
||||
void close() VL_MT_SAFE { m_sptrace.close(); }
|
||||
void close() VL_MT_SAFE {
|
||||
m_sptrace.close();
|
||||
modelConnected(false);
|
||||
}
|
||||
/// Flush dump
|
||||
void flush() VL_MT_SAFE { m_sptrace.flush(); }
|
||||
/// Write one cycle of dump data
|
||||
|
||||
+292
-117
@@ -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>
|
||||
|
||||
@@ -263,33 +266,24 @@ class VerilatedVpioScope VL_NOT_FINAL : public VerilatedVpio {
|
||||
protected:
|
||||
const VerilatedScope* const m_scopep;
|
||||
bool m_toplevel = false;
|
||||
const char* m_name;
|
||||
const char* m_fullname;
|
||||
|
||||
public:
|
||||
explicit VerilatedVpioScope(const VerilatedScope* scopep)
|
||||
: m_scopep{scopep} {
|
||||
std::string scopename = m_scopep->name();
|
||||
std::string::size_type pos = std::string::npos;
|
||||
// Look for '.' not inside escaped identifier
|
||||
size_t i = 0;
|
||||
while (i < scopename.length()) {
|
||||
if (scopename[i] == '\\') {
|
||||
while (i < scopename.length() && scopename[i] != ' ') ++i;
|
||||
++i; // Proc ' ', it should always be there. Then grab '.' on next cycle
|
||||
} else {
|
||||
while (i < scopename.length() && scopename[i] != '.') ++i;
|
||||
if (i < scopename.length()) pos = i++;
|
||||
}
|
||||
}
|
||||
if (VL_UNLIKELY(pos == std::string::npos)) m_toplevel = true;
|
||||
m_fullname = m_scopep->name();
|
||||
if (std::strncmp(m_fullname, "TOP.", 4) == 0) m_fullname += 4;
|
||||
m_name = m_scopep->identifier();
|
||||
}
|
||||
~VerilatedVpioScope() override = default;
|
||||
static VerilatedVpioScope* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioScope*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
uint32_t type() const override { return vpiScope; }
|
||||
uint32_t type() const override { return vpiGenScope; }
|
||||
const VerilatedScope* scopep() const { return m_scopep; }
|
||||
const char* name() const override { return m_scopep->name(); }
|
||||
const char* fullname() const override { return m_scopep->name(); }
|
||||
const char* name() const override { return m_name; }
|
||||
const char* fullname() const override { return m_fullname; }
|
||||
bool toplevel() const { return m_toplevel; }
|
||||
};
|
||||
|
||||
@@ -469,22 +463,29 @@ public:
|
||||
};
|
||||
|
||||
class VerilatedVpioModule final : public VerilatedVpioScope {
|
||||
const char* m_name;
|
||||
const char* m_fullname;
|
||||
|
||||
public:
|
||||
explicit VerilatedVpioModule(const VerilatedScope* modulep)
|
||||
: VerilatedVpioScope{modulep} {
|
||||
m_fullname = m_scopep->name();
|
||||
if (std::strncmp(m_fullname, "TOP.", 4) == 0) m_fullname += 4;
|
||||
m_name = m_scopep->identifier();
|
||||
// Look for '.' not inside escaped identifier
|
||||
const std::string scopename = m_fullname;
|
||||
std::string::size_type pos = std::string::npos;
|
||||
size_t i = 0;
|
||||
while (i < scopename.length()) {
|
||||
if (scopename[i] == '\\') {
|
||||
while (i < scopename.length() && scopename[i] != ' ') ++i;
|
||||
++i; // Proc ' ', it should always be there. Then grab '.' on next cycle
|
||||
} else {
|
||||
while (i < scopename.length() && scopename[i] != '.') ++i;
|
||||
if (i < scopename.length()) pos = i++;
|
||||
}
|
||||
}
|
||||
if (VL_UNLIKELY(pos == std::string::npos)) m_toplevel = true;
|
||||
}
|
||||
static VerilatedVpioModule* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioModule*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
uint32_t type() const override { return vpiModule; }
|
||||
const char* name() const override { return m_name; }
|
||||
const char* fullname() const override { return m_fullname; }
|
||||
};
|
||||
|
||||
class VerilatedVpioModuleIter final : public VerilatedVpio {
|
||||
@@ -507,8 +508,8 @@ public:
|
||||
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
||||
return nullptr;
|
||||
}
|
||||
const VerilatedScope::Type itype = (*m_it)->type();
|
||||
const VerilatedScope* const modp = *m_it++;
|
||||
const VerilatedScope::Type itype = modp->type();
|
||||
if (itype == VerilatedScope::SCOPE_MODULE) {
|
||||
return (new VerilatedVpioModule{modp})->castVpiHandle();
|
||||
}
|
||||
@@ -516,26 +517,54 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class VerilatedVpioScopeIter final : public VerilatedVpio {
|
||||
const std::vector<const VerilatedScope*>* m_vec;
|
||||
std::vector<const VerilatedScope*>::const_iterator m_it;
|
||||
|
||||
public:
|
||||
explicit VerilatedVpioScopeIter(const std::vector<const VerilatedScope*>& vec)
|
||||
: m_vec{&vec} {
|
||||
m_it = m_vec->begin();
|
||||
}
|
||||
~VerilatedVpioScopeIter() override = default;
|
||||
static VerilatedVpioScopeIter* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioScopeIter*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
uint32_t type() const override { return vpiIterator; }
|
||||
vpiHandle dovpi_scan() override {
|
||||
while (true) {
|
||||
if (m_it == m_vec->end()) {
|
||||
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
||||
return nullptr;
|
||||
}
|
||||
const VerilatedScope* const modp = *m_it++;
|
||||
const VerilatedScope::Type itype = modp->type();
|
||||
if (itype == VerilatedScope::SCOPE_OTHER) {
|
||||
return (new VerilatedVpioScope{modp})->castVpiHandle();
|
||||
} else if (itype == VerilatedScope::SCOPE_MODULE) {
|
||||
return (new VerilatedVpioModule{modp})->castVpiHandle();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static const char* d_unit = "$unit";
|
||||
class VerilatedVpioPackage final : public VerilatedVpioScope {
|
||||
std::string m_name;
|
||||
std::string m_fullname;
|
||||
std::string m_fullname_string;
|
||||
|
||||
public:
|
||||
explicit VerilatedVpioPackage(const VerilatedScope* modulep)
|
||||
: VerilatedVpioScope{modulep} {
|
||||
const char* sfullname = m_scopep->name();
|
||||
if (std::strncmp(sfullname, "TOP.", 4) == 0) sfullname += 4;
|
||||
m_fullname = std::string{sfullname} + "::";
|
||||
if (m_fullname == "\\$unit ::") m_fullname = "$unit::";
|
||||
m_name = std::string(m_scopep->identifier());
|
||||
if (m_name == "\\$unit ") m_name = "$unit";
|
||||
m_fullname_string = std::string{m_fullname} + "::";
|
||||
if (m_fullname_string == "\\$unit ::") m_fullname_string = "$unit::";
|
||||
|
||||
if (strcmp(m_name, "\\$unit ") == 0) m_name = d_unit;
|
||||
}
|
||||
static VerilatedVpioPackage* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioPackage*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
const char* fullname() const override { return m_fullname_string.c_str(); }
|
||||
uint32_t type() const override { return vpiPackage; }
|
||||
const char* name() const override { return m_name.c_str(); }
|
||||
const char* fullname() const override { return m_fullname.c_str(); }
|
||||
};
|
||||
|
||||
class VerilatedVpioInstanceIter final : public VerilatedVpio {
|
||||
@@ -604,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,
|
||||
@@ -626,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;
|
||||
@@ -750,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;) {
|
||||
@@ -787,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 void evalNeeded(bool evalNeeded) { s().m_evalNeeded = evalNeeded; }
|
||||
static bool evalNeeded() { return s().m_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();
|
||||
}
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
@@ -889,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
|
||||
|
||||
@@ -1978,13 +2165,21 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
|
||||
return ((new VerilatedVpioVarIter{vop, true})->castVpiHandle());
|
||||
}
|
||||
case vpiModule: {
|
||||
const VerilatedVpioModule* const vop = VerilatedVpioModule::castp(object);
|
||||
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
|
||||
const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
|
||||
const VerilatedScope* const modp = vop ? vop->scopep() : nullptr;
|
||||
const auto it = vlstd::as_const(map)->find(const_cast<VerilatedScope*>(modp));
|
||||
if (it == map->end()) return nullptr;
|
||||
return ((new VerilatedVpioModuleIter{it->second})->castVpiHandle());
|
||||
}
|
||||
case vpiInternalScope: {
|
||||
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
|
||||
const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
|
||||
const VerilatedScope* const modp = vop ? vop->scopep() : nullptr;
|
||||
const auto it = vlstd::as_const(map)->find(const_cast<VerilatedScope*>(modp));
|
||||
if (it == map->end()) return nullptr;
|
||||
return ((new VerilatedVpioScopeIter{it->second})->castVpiHandle());
|
||||
}
|
||||
case vpiInstance: {
|
||||
if (object) return nullptr;
|
||||
const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
|
||||
@@ -2160,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
|
||||
@@ -2210,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) {
|
||||
@@ -2265,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
|
||||
@@ -2315,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) {
|
||||
@@ -2330,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) {
|
||||
@@ -2398,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_();
|
||||
@@ -2406,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__,
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include "verilated_syms.h"
|
||||
|
||||
//======================================================================
|
||||
// From IEEE 1800-2017 annex M
|
||||
// From IEEE 1800-2023 annex M
|
||||
|
||||
#include "vltstd/sv_vpi_user.h"
|
||||
|
||||
@@ -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;
|
||||
|
||||
+51
-8
@@ -58,6 +58,9 @@
|
||||
# define VL_ATTR_PRINTF(fmtArgNum) __attribute__((format(printf, (fmtArgNum), (fmtArgNum) + 1)))
|
||||
# define VL_ATTR_PURE __attribute__((pure))
|
||||
# define VL_ATTR_UNUSED __attribute__((unused))
|
||||
#ifndef VL_ATTR_WARN_UNUSED_RESULT
|
||||
# define VL_ATTR_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
|
||||
#endif
|
||||
# if !defined(_WIN32) && !defined(__MINGW32__)
|
||||
// All VL_ATTR_WEAK symbols must be marked with the macOS -U linker flag in verilated.mk.in
|
||||
# define VL_ATTR_WEAK __attribute__((weak))
|
||||
@@ -164,6 +167,9 @@
|
||||
#ifndef VL_ATTR_UNUSED
|
||||
# define VL_ATTR_UNUSED ///< Attribute that function that may be never used
|
||||
#endif
|
||||
#ifndef VL_ATTR_WARN_UNUSED_RESULT
|
||||
# define VL_ATTR_WARN_UNUSED_RESULT ///< Attribute that return value of function must be used
|
||||
#endif
|
||||
#ifndef VL_ATTR_WEAK
|
||||
# define VL_ATTR_WEAK ///< Attribute that function external that is optionally defined
|
||||
#endif
|
||||
@@ -337,6 +343,7 @@ extern "C" void __gcov_dump();
|
||||
#include <cstdint>
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
|
||||
#ifndef VL_NO_LEGACY
|
||||
using vluint8_t = uint8_t; ///< 8-bit unsigned type (backward compatibility)
|
||||
@@ -604,6 +611,50 @@ static inline double VL_ROUND(double n) {
|
||||
#define VL_OFFSETOF(type, field) \
|
||||
(reinterpret_cast<size_t>(&(reinterpret_cast<type*>(0x10000000)->field)) - 0x10000000)
|
||||
|
||||
//=========================================================================
|
||||
// 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
|
||||
class DeltaCpuTime final {
|
||||
double m_start{}; // Time constructed at
|
||||
static double gettime() VL_MT_SAFE;
|
||||
|
||||
public:
|
||||
// Construct, and if startit is true, start() timer
|
||||
explicit DeltaCpuTime(bool startit) {
|
||||
if (startit) start();
|
||||
}
|
||||
void start() VL_MT_SAFE { m_start = gettime(); } // Start timer; record current time
|
||||
double deltaTime() const VL_MT_SAFE { // Return time between now and start()
|
||||
return (m_start == 0.0) ? 0.0 : gettime() - m_start;
|
||||
}
|
||||
};
|
||||
// Internal: Record wall time, starting point on construction, and current delta from that
|
||||
class DeltaWallTime final {
|
||||
double m_start{}; // Time constructed at
|
||||
static double gettime() VL_MT_SAFE;
|
||||
|
||||
public:
|
||||
// Construct, and if startit is true, start() timer
|
||||
explicit DeltaWallTime(bool startit) {
|
||||
if (startit) start();
|
||||
}
|
||||
void start() VL_MT_SAFE { m_start = gettime(); } // Start timer; record current time
|
||||
double deltaTime() const VL_MT_SAFE { // Return time between now and start()
|
||||
return (m_start == 0.0) ? 0.0 : gettime() - m_start;
|
||||
}
|
||||
};
|
||||
} //namespace VlOs
|
||||
|
||||
//=========================================================================
|
||||
// Conversions
|
||||
|
||||
@@ -637,14 +688,6 @@ T const& as_const(T& v) VL_MT_SAFE {
|
||||
return v;
|
||||
}
|
||||
|
||||
// C++14's std::exchange
|
||||
template <class T, class U = T>
|
||||
T exchange(T& obj, U&& new_value) {
|
||||
T old_value = std::move(obj);
|
||||
obj = std::forward<U>(new_value);
|
||||
return old_value;
|
||||
}
|
||||
|
||||
}; // namespace vlstd
|
||||
|
||||
//=========================================================================
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
// Copyright 2003-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/Verilator common implementation for OS portability
|
||||
///
|
||||
/// This is compiled as part of other .cpp files to reduce compile time
|
||||
/// and as such is a .h file rather than .cpp file.
|
||||
///
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VL_ALLOW_VERILATEDOS_C
|
||||
#error "This file should be included only from V3Os.cpp/Verilated.cpp"
|
||||
#endif
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
// clang-format off
|
||||
#if defined(_WIN32) || defined(__MINGW32__)
|
||||
# include <windows.h> // LONG for bcrypt.h on MINGW
|
||||
# include <processthreadsapi.h> // GetProcessTimes
|
||||
# include <psapi.h> // GetProcessMemoryInfo
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
namespace VlOs {
|
||||
|
||||
//=========================================================================
|
||||
// VlOs::VlGetCpuTime/VlGetWallTime implementation
|
||||
|
||||
double DeltaCpuTime::gettime() VL_MT_SAFE {
|
||||
#if defined(_WIN32) || defined(__MINGW32__)
|
||||
FILETIME lpCreationTime, lpExitTime, lpKernelTime, lpUserTime;
|
||||
if (0
|
||||
!= GetProcessTimes(GetCurrentProcess(), &lpCreationTime, &lpExitTime, &lpKernelTime,
|
||||
&lpUserTime)) {
|
||||
return static_cast<double>(static_cast<uint64_t>(lpUserTime.dwLowDateTime)
|
||||
| static_cast<uint64_t>(lpUserTime.dwHighDateTime) << 32ULL)
|
||||
* 1e-7;
|
||||
}
|
||||
#else
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
|
||||
timespec ts;
|
||||
if (0 != clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts)) // MT-Safe
|
||||
return ts.tv_sec + ts.tv_nsec * 1e-9;
|
||||
#endif
|
||||
return 0.0;
|
||||
}
|
||||
double DeltaWallTime::gettime() VL_MT_SAFE {
|
||||
#if defined(_WIN32) || defined(__MINGW32__)
|
||||
FILETIME ft; // contains number of 0.1us intervals since the beginning of 1601 UTC.
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
const uint64_t tenthus
|
||||
= ((static_cast<uint64_t>(ft.dwHighDateTime) << 32) + ft.dwLowDateTime + 5ULL);
|
||||
return static_cast<double>(tenthus) * 1e-7;
|
||||
#else
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
|
||||
timespec ts;
|
||||
if (0 == clock_gettime(CLOCK_MONOTONIC, &ts)) // MT-Safe
|
||||
return ts.tv_sec + ts.tv_nsec * 1e-9;
|
||||
return 0.0;
|
||||
#endif
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// VlOs::memUsageBytes implementation
|
||||
|
||||
uint64_t memUsageBytes() VL_MT_SAFE {
|
||||
#if defined(_WIN32) || defined(__MINGW32__)
|
||||
const HANDLE process = GetCurrentProcess();
|
||||
PROCESS_MEMORY_COUNTERS pmc;
|
||||
if (GetProcessMemoryInfo(process, &pmc, sizeof(pmc))) {
|
||||
// The best we can do using simple Windows APIs is to get the size of the working set.
|
||||
return pmc.WorkingSetSize;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
// Highly unportable. Sorry
|
||||
const char* const statmFilename = "/proc/self/statm";
|
||||
FILE* const fp = fopen(statmFilename, "r");
|
||||
if (!fp) return 0;
|
||||
uint64_t size, resident, share, text, lib, data, dt; // All in pages
|
||||
const int items = fscanf(
|
||||
fp, "%" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64,
|
||||
&size, &resident, &share, &text, &lib, &data, &dt);
|
||||
fclose(fp);
|
||||
if (VL_UNCOVERABLE(7 != items)) return 0;
|
||||
return (text + data) * getpagesize();
|
||||
#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
|
||||
@@ -150,10 +150,21 @@ extern "C" {
|
||||
#define vpiIntegerNet 681
|
||||
#define vpiLogicNet vpiNet
|
||||
#define vpiTimeNet 682
|
||||
#define vpiUnionNet 525
|
||||
#define vpiShortRealNet 526
|
||||
#define vpiRealNet 527
|
||||
#define vpiByteNet 528
|
||||
#define vpiShortIntNet 529
|
||||
#define vpiIntNet 530
|
||||
#define vpiLongIntNet 531
|
||||
#define vpiBitNet 532
|
||||
#define vpiInterconnectNet 533
|
||||
#define vpiInterconnectArray 534
|
||||
#define vpiStructNet 683
|
||||
#define vpiBreak 684
|
||||
#define vpiContinue 685
|
||||
#define vpiPackedArrayNet 693
|
||||
#define vpiNettypeDecl 523
|
||||
#define vpiConstraintExpr 747
|
||||
#define vpiElseConst 748
|
||||
#define vpiImplication 749
|
||||
@@ -174,6 +185,8 @@ extern "C" {
|
||||
#define vpiBaseTypespec 703
|
||||
#define vpiElemTypespec 704
|
||||
|
||||
#define vpiNetTypedefAlias 705
|
||||
|
||||
#define vpiInputSkew 706
|
||||
#define vpiOutputSkew 707
|
||||
#define vpiGlobalClocking 708
|
||||
@@ -206,7 +219,8 @@ extern "C" {
|
||||
#define vpiMessages 735
|
||||
#define vpiLoopVars 737
|
||||
|
||||
#define vpiConcurrentAssertions 740
|
||||
#define vpiConcurrentAssertion 740
|
||||
#define vpiConcurrentAssertions vpiConcurrentAssertion
|
||||
#define vpiMatchItem 741
|
||||
#define vpiMember 742
|
||||
#define vpiElement 743
|
||||
@@ -221,6 +235,7 @@ extern "C" {
|
||||
/**************************************************************************/
|
||||
/************************ generic object properties ***********************/
|
||||
/**************************************************************************/
|
||||
|
||||
#define vpiTop 600
|
||||
|
||||
#define vpiUnit 602
|
||||
@@ -255,7 +270,6 @@ extern "C" {
|
||||
#define vpiModportPort 2
|
||||
/* vpiPort is also a port type. It is defined in vpi_user.h */
|
||||
|
||||
|
||||
#define vpiConstantVariable 612
|
||||
#define vpiStructUnionMember 615
|
||||
|
||||
@@ -407,6 +421,7 @@ extern "C" {
|
||||
/***************************** structure *****************************/
|
||||
|
||||
/**************************** CALLBACK REASONS ****************************/
|
||||
#define vpiMethodFuncCall 648
|
||||
#define cbStartOfThread 600 /* callback on thread creation */
|
||||
#define cbEndOfThread 601 /* callback on thread termination */
|
||||
#define cbEnterThread 602 /* callback on reentering thread */
|
||||
|
||||
+51
-6
@@ -7,7 +7,7 @@
|
||||
* This file contains the constant definitions, structure definitions,
|
||||
* and routine declarations used by SystemVerilog DPI.
|
||||
*
|
||||
* This file is from the SystemVerilog IEEE 1800-2017 Annex I.
|
||||
* This file is from the SystemVerilog IEEE 1800-2023 Annex I.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDED_SVDPI
|
||||
@@ -111,6 +111,26 @@ typedef uint32_t svBitVecVal;
|
||||
((N) == 32 ? (VALUE) : \
|
||||
(((VALUE) & (1 << (N))) ? ((VALUE) | ~SV_MASK(N)) : ((VALUE) & SV_MASK(N))))
|
||||
|
||||
#ifndef VPI_TIME
|
||||
#define VPI_TIME
|
||||
typedef struct t_vpi_time {
|
||||
int32_t type;
|
||||
uint32_t high, low;
|
||||
double real;
|
||||
} s_vpi_time, *p_vpi_time;
|
||||
|
||||
#define vpiScaledRealTime 1
|
||||
#define vpiSimTime 2
|
||||
#define vpiSuppressTime 3
|
||||
#endif
|
||||
|
||||
/* time value */
|
||||
typedef s_vpi_time svTimeVal;
|
||||
|
||||
/* time value types */
|
||||
#define sv_scaled_real_time vpiScaledRealTime
|
||||
#define sv_sim_time vpiSimTime
|
||||
|
||||
/*
|
||||
* Implementation-dependent representation.
|
||||
*/
|
||||
@@ -277,7 +297,7 @@ XXTERN svScope svGetScope( void );
|
||||
|
||||
/*
|
||||
* Set context for subsequent export function execution.
|
||||
* This function must be called before calling an export function, unless
|
||||
* This function shall be called before calling an export function, unless
|
||||
* the export function is called while executing an import function. In that
|
||||
* case the export function shall inherit the scope of the surrounding import
|
||||
* function. This is known as the "default scope".
|
||||
@@ -297,7 +317,7 @@ XXTERN svScope svGetScopeFromName(const char* scopeName);
|
||||
|
||||
/*
|
||||
* Store an arbitrary user data pointer for later retrieval by svGetUserData()
|
||||
* The userKey is generated by the user. It must be guaranteed by the user to
|
||||
* The userKey is generated by the user. It needs to be guaranteed by the user to
|
||||
* be unique from all other userKey's for all unique data storage requirements
|
||||
* It is recommended that the address of static functions or variables in the
|
||||
* user's C code be used as the userKey.
|
||||
@@ -329,24 +349,49 @@ XXTERN void* svGetUserData(const svScope scope, void* userKey);
|
||||
* modified. Whether this information is available or not is implementation-
|
||||
* specific. Note that the string provided (if any) is owned by the SV
|
||||
* implementation and is valid only until the next call to any SV function.
|
||||
* Applications must not modify this string or free it
|
||||
* Applications shall not modify this string or free it.
|
||||
*/
|
||||
XXTERN int svGetCallerInfo(const char** fileName, int *lineNumber);
|
||||
|
||||
/*
|
||||
* Returns 1 if the current execution thread is in the disabled state.
|
||||
* Disable protocol must be adhered to if in the disabled state.
|
||||
* Disable protocol shall be adhered to if in the disabled state.
|
||||
*/
|
||||
XXTERN int svIsDisabledState( void );
|
||||
|
||||
/*
|
||||
* Imported functions call this API function during disable processing to
|
||||
* acknowledge that they are correctly participating in the DPI disable protocol.
|
||||
* This function must be called before returning from an imported function that is
|
||||
* This function shall be called before returning from an imported function that is
|
||||
* in the disabled state.
|
||||
*/
|
||||
XXTERN void svAckDisabledState( void );
|
||||
|
||||
/* Mantis 5713/D9
|
||||
* Retrieve the current simulation time, scaled to the time unit of the scope.
|
||||
* If scope is NULL, then time is scaled to the simulation time unit.
|
||||
* It is an error to call svGetTime() with an invalid svScope.
|
||||
* This function returns -1 for all error cases, 0 upon success.
|
||||
*/
|
||||
XXTERN int svGetTime(const svScope scope, svTimeVal* time);
|
||||
|
||||
/*
|
||||
* Retrieve the time unit for scope.
|
||||
* If scope is NULL, then simulation time unit is retrieved.
|
||||
* It is an error to call svGetTimeUnit() with an invalid svScope.
|
||||
* This function returns -1 for all error cases, 0 upon success.
|
||||
*/
|
||||
XXTERN int svGetTimeUnit(const svScope scope, int32_t* time_unit);
|
||||
|
||||
/*
|
||||
* Retrieve the time precision for scope.
|
||||
* If scope is NULL, then simulation time unit is retrieved.
|
||||
* It is an error to call svGetTimePrecision() with an invalid svScope.
|
||||
* This function returns -1 for all error cases, 0 upon success.
|
||||
*/
|
||||
XXTERN int svGetTimePrecision(const svScope scope, int32_t* time_precision);
|
||||
|
||||
|
||||
/*
|
||||
**********************************************************
|
||||
* DEPRECATED PORTION OF FILE STARTS FROM HERE.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*******************************************************************************
|
||||
* vpi_user.h
|
||||
*
|
||||
* IEEE Std 1800-2017 Programming Language Interface (PLI)
|
||||
* IEEE Std 1800-2023 Programming Language Interface (PLI)
|
||||
*
|
||||
* This file contains the constant definitions, structure definitions, and
|
||||
* routine declarations used by the SystemVerilog Verification Procedural
|
||||
@@ -344,6 +344,9 @@ typedef PLI_UINT32 *vpiHandle;
|
||||
#define vpiSupply0 11 /* supply-0 net */
|
||||
#define vpiNone 12 /* no default net type (1364-2001) */
|
||||
#define vpiUwire 13 /* unresolved wire net (1364-2005) */
|
||||
#define vpiNettypeNet 14 /* user-defined nettype net */
|
||||
#define vpiNettypeNetSelect 15 /* user-defined nettype net subelement */
|
||||
#define vpiInterconnect 16 /* interconnect net */
|
||||
|
||||
#define vpiExplicitScalared 23 /* explicitly scalared (Boolean) */
|
||||
#define vpiExplicitVectored 24 /* explicitly vectored (Boolean) */
|
||||
@@ -573,6 +576,9 @@ typedef PLI_UINT32 *vpiHandle;
|
||||
|
||||
/******************************* time structure *******************************/
|
||||
|
||||
#ifndef VPI_TIME /* added in 1800-2023 */
|
||||
#define VPI_TIME
|
||||
|
||||
typedef struct t_vpi_time
|
||||
{
|
||||
PLI_INT32 type; /* [vpiScaledRealTime, vpiSimTime,
|
||||
@@ -587,6 +593,8 @@ typedef struct t_vpi_time
|
||||
#define vpiSimTime 2
|
||||
#define vpiSuppressTime 3
|
||||
|
||||
#endif
|
||||
|
||||
/****************************** delay structures ******************************/
|
||||
|
||||
typedef struct t_vpi_delay
|
||||
@@ -741,9 +749,8 @@ typedef struct t_vpi_arrayvalue
|
||||
typedef struct t_vpi_systf_data
|
||||
{
|
||||
PLI_INT32 type; /* vpiSysTask, vpiSysFunc */
|
||||
PLI_INT32 sysfunctype; /* vpiSysTask, vpi[Int,Real,Time,Sized,
|
||||
SizedSigned]Func */
|
||||
PLI_BYTE8 *tfname; /* first character must be '$' */
|
||||
PLI_INT32 sysfunctype; /* vpi[Int,Real,Time,Sized,SizedSigned]Func */
|
||||
PLI_BYTE8 *tfname; /* first character has to be '$' */
|
||||
PLI_INT32 (*calltf)(PLI_BYTE8 *);
|
||||
PLI_INT32 (*compiletf)(PLI_BYTE8 *);
|
||||
PLI_INT32 (*sizetf)(PLI_BYTE8 *); /* for sized function callbacks only */
|
||||
|
||||
@@ -434,7 +434,7 @@ class CallAnnotationsValidator:
|
||||
self.process_translation_unit(translation_unit)
|
||||
self._main_source_file = ""
|
||||
self._defines = {}
|
||||
else:
|
||||
elif len(errors) != 0:
|
||||
print(f"%Error: parsing failed: {filename}", file=sys.stderr)
|
||||
for error in errors:
|
||||
print(f" {error}", file=sys.stderr)
|
||||
|
||||
@@ -20,6 +20,26 @@ document pnt
|
||||
Verilator: Print AstNode NODEP's tree
|
||||
end
|
||||
|
||||
# Source python-based gdb config with jshow/jdiff definitions
|
||||
# (Stored in separate file, so it can be highlighted/linted/formatted as Python)
|
||||
python
|
||||
import os
|
||||
if "VERILATOR_ROOT" in os.environ:
|
||||
gdbinit_py = os.environ["VERILATOR_ROOT"] + "/src/.gdbinit.py"
|
||||
gdb.execute("source" + gdbinit_py)
|
||||
end
|
||||
|
||||
define jstash
|
||||
call (char*) &(AstNode::dumpTreeJsonGdb($arg0)[0])
|
||||
end
|
||||
document jstash
|
||||
Verilator: Perform a JSON dump of the given AST node and save it in value history (e.g. $1) for later
|
||||
inspection using jtree. The node can be a pointer identifier or an address literal.
|
||||
end
|
||||
|
||||
alias -a js=jstash
|
||||
alias -a jt=jtree
|
||||
|
||||
define dtf
|
||||
call AstNode::dumpTreeFileGdb($arg0, 0)
|
||||
end
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
# pylint: disable=line-too-long,invalid-name,multiple-statements,missing-function-docstring,missing-class-docstring,missing-module-docstring,no-else-return,too-few-public-methods,unused-argument
|
||||
# DESCRIPTION: Verilator: GDB startup file with useful define
|
||||
#
|
||||
# Copyright 2023 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify the Verilator internals 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
|
||||
######################################################################
|
||||
|
||||
import tempfile
|
||||
import gdb # pylint: disable=import-error
|
||||
|
||||
|
||||
def _vltgdb_get_dump(node):
|
||||
gdb.execute(f'set $_gdb_dump_json_str = AstNode::dumpTreeJsonGdb({node})')
|
||||
dump = gdb.execute('printf "%s", $_gdb_dump_json_str', to_string=True)
|
||||
gdb.execute('call free($_gdb_dump_json_str)')
|
||||
return dump
|
||||
|
||||
|
||||
def _vltgdb_tmpfile():
|
||||
return tempfile.NamedTemporaryFile(mode="wt") # write, text mode
|
||||
|
||||
|
||||
def _vltgdb_fwrite(file, s):
|
||||
"""Write to file and flush buffer before passing the file to astsee"""
|
||||
file.write(s)
|
||||
file.flush()
|
||||
|
||||
|
||||
class AstseeCmd(gdb.Command):
|
||||
"""Verilator: Pretty print or diff nodes using `astsee`. A node can be:
|
||||
* an pointer identifier,
|
||||
* an address literal,
|
||||
* a GDB value (like `$1`) that stores a dump previously done by the `jstash` command.
|
||||
Apart from not taking input from a file, it works exactly like `verilator_astsee`:
|
||||
* passing one node gives you a pretty print,
|
||||
* passing two nodes gives you a diff,
|
||||
* for more options see `astsee` readme/help.
|
||||
For examples see internals.rst
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("jtree", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION)
|
||||
|
||||
def _null_check(self, old, new):
|
||||
err = ""
|
||||
if old == "<nullptr>\n": err += "old == <nullptr>\n"
|
||||
if new == "<nullptr>\n": err += "new == <nullptr>"
|
||||
if err: raise gdb.GdbError(err.strip("\n"))
|
||||
|
||||
def invoke(self, arg_str, from_tty):
|
||||
from astsee import verilator_cli as astsee # pylint: disable=import-error,import-outside-toplevel
|
||||
# we import it here so "no module X" error would occur on typing `jtree` rather than on every gdb start
|
||||
|
||||
# We hack `astsee_verilator`'s arg parser to find arguments with nodes
|
||||
# After finding them, we replace them with proper files
|
||||
astsee_args = astsee.parser.parse_args(gdb.string_to_argv(arg_str))
|
||||
with _vltgdb_tmpfile() as oldfile, _vltgdb_tmpfile(
|
||||
) as newfile, _vltgdb_tmpfile() as metafile:
|
||||
if astsee_args.file:
|
||||
_vltgdb_fwrite(oldfile, _vltgdb_get_dump(astsee_args.file))
|
||||
astsee_args.file = oldfile.name
|
||||
if astsee_args.newfile:
|
||||
_vltgdb_fwrite(newfile, _vltgdb_get_dump(astsee_args.newfile))
|
||||
astsee_args.newfile = newfile.name
|
||||
if astsee_args.meta is None:
|
||||
# pass
|
||||
gdb.execute(
|
||||
f'call AstNode::dumpJsonMetaFileGdb("{metafile.name}")')
|
||||
astsee_args.meta = metafile.name
|
||||
try:
|
||||
astsee.main(astsee_args)
|
||||
except SystemExit as e: # astsee prints nice errmsgs on exit(), so rethrow it as GdbError to suppress cryptic python trace
|
||||
raise gdb.GdbError("astsee exited with non-zero code") from e
|
||||
|
||||
|
||||
AstseeCmd()
|
||||
+14
-3
@@ -62,8 +62,10 @@ set(HEADERS
|
||||
V3DepthBlock.h
|
||||
V3Descope.h
|
||||
V3Dfg.h
|
||||
V3DfgCache.h
|
||||
V3DfgOptimizer.h
|
||||
V3DfgPasses.h
|
||||
V3DfgPatternStats.h
|
||||
V3DfgPeephole.h
|
||||
V3DfgVertices.h
|
||||
V3DupFinder.h
|
||||
@@ -77,6 +79,7 @@ set(HEADERS
|
||||
V3EmitV.h
|
||||
V3EmitXml.h
|
||||
V3Error.h
|
||||
V3ExecGraph.h
|
||||
V3Expand.h
|
||||
V3File.h
|
||||
V3FileLine.h
|
||||
@@ -119,6 +122,7 @@ set(HEADERS
|
||||
V3OptionParser.h
|
||||
V3Options.h
|
||||
V3Order.h
|
||||
V3OrderInternal.h
|
||||
V3OrderGraph.h
|
||||
V3OrderMoveGraph.h
|
||||
V3Os.h
|
||||
@@ -127,10 +131,9 @@ set(HEADERS
|
||||
V3Parse.h
|
||||
V3ParseImp.h
|
||||
V3ParseSym.h
|
||||
V3Partition.h
|
||||
V3PartitionGraph.h
|
||||
V3PchAstMT.h
|
||||
V3PchAstNoMT.h
|
||||
V3PreExpr.h
|
||||
V3PreLex.h
|
||||
V3PreProc.h
|
||||
V3PreShell.h
|
||||
@@ -212,11 +215,13 @@ set(COMMON_SOURCES
|
||||
V3Descope.cpp
|
||||
V3Dfg.cpp
|
||||
V3DfgAstToDfg.cpp
|
||||
V3DfgCache.cpp
|
||||
V3DfgDecomposition.cpp
|
||||
V3DfgDfgToAst.cpp
|
||||
V3DfgOptimizer.cpp
|
||||
V3DfgPasses.cpp
|
||||
V3DfgPeephole.cpp
|
||||
V3DfgRegularize.cpp
|
||||
V3DupFinder.cpp
|
||||
V3Timing.cpp
|
||||
V3EmitCBase.cpp
|
||||
@@ -234,6 +239,7 @@ set(COMMON_SOURCES
|
||||
V3EmitV.cpp
|
||||
V3EmitXml.cpp
|
||||
V3Error.cpp
|
||||
V3ExecGraph.cpp
|
||||
V3Expand.cpp
|
||||
V3File.cpp
|
||||
V3FileLine.cpp
|
||||
@@ -270,9 +276,13 @@ set(COMMON_SOURCES
|
||||
V3OptionParser.cpp
|
||||
V3Options.cpp
|
||||
V3Order.cpp
|
||||
V3OrderGraphBuilder.cpp
|
||||
V3OrderMoveGraph.cpp
|
||||
V3OrderParallel.cpp
|
||||
V3OrderProcessDomains.cpp
|
||||
V3OrderSerial.cpp
|
||||
V3Os.cpp
|
||||
V3Param.cpp
|
||||
V3Partition.cpp
|
||||
V3PreShell.cpp
|
||||
V3Premit.cpp
|
||||
V3ProtectLib.cpp
|
||||
@@ -470,6 +480,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}
|
||||
|
||||
+16
-1
@@ -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)
|
||||
@@ -228,11 +236,13 @@ RAW_OBJS_PCH_ASTNOMT = \
|
||||
V3Descope.o \
|
||||
V3Dfg.o \
|
||||
V3DfgAstToDfg.o \
|
||||
V3DfgCache.o \
|
||||
V3DfgDecomposition.o \
|
||||
V3DfgDfgToAst.o \
|
||||
V3DfgOptimizer.o \
|
||||
V3DfgPasses.o \
|
||||
V3DfgPeephole.o \
|
||||
V3DfgRegularize.o \
|
||||
V3DupFinder.o \
|
||||
V3EmitCMain.o \
|
||||
V3EmitCMake.o \
|
||||
@@ -240,6 +250,7 @@ RAW_OBJS_PCH_ASTNOMT = \
|
||||
V3EmitCSyms.o \
|
||||
V3EmitMk.o \
|
||||
V3EmitXml.o \
|
||||
V3ExecGraph.o \
|
||||
V3Expand.o \
|
||||
V3Force.o \
|
||||
V3Fork.o \
|
||||
@@ -263,8 +274,12 @@ RAW_OBJS_PCH_ASTNOMT = \
|
||||
V3MergeCond.o \
|
||||
V3Name.o \
|
||||
V3Order.o \
|
||||
V3OrderGraphBuilder.o \
|
||||
V3OrderMoveGraph.o \
|
||||
V3OrderParallel.o \
|
||||
V3OrderProcessDomains.o \
|
||||
V3OrderSerial.o \
|
||||
V3Param.o \
|
||||
V3Partition.o \
|
||||
V3Premit.o \
|
||||
V3ProtectLib.o \
|
||||
V3Randomize.o \
|
||||
|
||||
+5
-6
@@ -97,8 +97,8 @@ protected:
|
||||
result = vertexp->user();
|
||||
break;
|
||||
case LatchDetectGraphVertex::VT_BLOCK: // (OR of potentially many siblings)
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
if (latchCheckInternal(castVertexp(edgep->top()))) {
|
||||
for (V3GraphEdge& edge : vertexp->outEdges()) {
|
||||
if (latchCheckInternal(castVertexp(edge.top()))) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
@@ -106,9 +106,8 @@ protected:
|
||||
break;
|
||||
case LatchDetectGraphVertex::VT_BRANCH: // (AND of both sibling)
|
||||
// A BRANCH vertex always has exactly 2 siblings
|
||||
LatchDetectGraphVertex* const ifp = castVertexp(vertexp->outBeginp()->top());
|
||||
LatchDetectGraphVertex* const elsp
|
||||
= castVertexp(vertexp->outBeginp()->outNextp()->top());
|
||||
LatchDetectGraphVertex* const ifp = castVertexp(vertexp->outEdges().frontp()->top());
|
||||
LatchDetectGraphVertex* const elsp = castVertexp(vertexp->outEdges().backp()->top());
|
||||
result = latchCheckInternal(ifp) && latchCheckInternal(elsp);
|
||||
break;
|
||||
}
|
||||
@@ -170,7 +169,7 @@ public:
|
||||
for (const auto& vrp : m_outputs) {
|
||||
LatchDetectGraphVertex* const vertp = castVertexp(vrp->varp()->user1p());
|
||||
vertp->user(true); // Identify the output vertex we are checking paths _to_
|
||||
if (!latchCheckInternal(castVertexp(verticesBeginp()))) latch_detected = true;
|
||||
if (!latchCheckInternal(castVertexp(vertices().frontp()))) latch_detected = true;
|
||||
if (latch_detected && !latch_expected) {
|
||||
nodep->v3warn(
|
||||
LATCH,
|
||||
|
||||
+17
-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.
|
||||
@@ -77,6 +77,21 @@ class ActiveTopVisitor final : public VNVisitor {
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete empty procedures
|
||||
for (AstNode *stmtp = nodep->stmtsp(), *nextp; stmtp; stmtp = nextp) {
|
||||
nextp = stmtp->nextp();
|
||||
if (AstNodeProcedure* const procp = VN_CAST(stmtp, NodeProcedure)) {
|
||||
if (!procp->stmtsp()) VL_DO_DANGLING(pushDeletep(procp->unlinkFrBack()), stmtp);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete empty actives
|
||||
if (!nodep->stmtsp()) {
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
|
||||
// Move the SENTREE for each active up to the global level.
|
||||
// This way we'll easily see what clock domains are identical
|
||||
AstSenTree* const wantp = m_finder.getSenTree(sensesp);
|
||||
@@ -91,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);
|
||||
}
|
||||
|
||||
+50
-2
@@ -170,7 +170,7 @@ class AssertVisitor final : public VNVisitor {
|
||||
if (m_procedurep) {
|
||||
// To support this need queue of asserts to activate
|
||||
nodep->v3error("Unsupported: Procedural concurrent assertion with"
|
||||
" clocking event inside always (IEEE 1800-2017 16.14.6)");
|
||||
" clocking event inside always (IEEE 1800-2023 16.14.6)");
|
||||
}
|
||||
}
|
||||
//
|
||||
@@ -287,7 +287,7 @@ class AssertVisitor final : public VNVisitor {
|
||||
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);
|
||||
}
|
||||
@@ -518,6 +518,54 @@ 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 (const AstConst* const constp = VN_CAST(nodep->controlTypep(), Const)) {
|
||||
nodep->ctlType(constp->toSInt());
|
||||
} else if (nodep->ctlType() == VAssertCtlType::_TO_BE_EVALUATED) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: non-const assert control type expression");
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (nodep->ctlType()) {
|
||||
case VAssertCtlType::ON:
|
||||
case VAssertCtlType::OFF:
|
||||
case VAssertCtlType::KILL: {
|
||||
UINFO(9, "Generating assertctl for a module: " << m_modp << endl);
|
||||
FileLine* const fl = nodep->fileline();
|
||||
const string assertOnStmt
|
||||
= string{"vlSymsp->_vm_contextp__->assertOn("}
|
||||
+ (nodep->ctlType() == VAssertCtlType::ON ? "true" : "false") + ");\n";
|
||||
nodep->replaceWith(new AstCExpr{fl, assertOnStmt, 1});
|
||||
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());
|
||||
|
||||
+28
-17
@@ -115,8 +115,9 @@ private:
|
||||
AstArg* const argp = tconnect.second;
|
||||
AstNode* const pinp = argp->exprp()->unlinkFrBack();
|
||||
replaceVarRefsWithExprRecurse(propExprp, portp, pinp);
|
||||
VL_DO_DANGLING(pushDeletep(pinp), pinp);
|
||||
}
|
||||
// Handle case with 2 disable iff statement (IEEE 1800-2017 16.12.1)
|
||||
// Handle case with 2 disable iff statement (IEEE 1800-2023 16.12.1)
|
||||
if (nodep->disablep() && propExprp->disablep()) {
|
||||
nodep->v3error("disable iff expression before property call and in its "
|
||||
"body is not legal");
|
||||
@@ -157,26 +158,31 @@ private:
|
||||
iterateChildren(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)) {
|
||||
nodep->skewp()->v3error("Skew must be constant (IEEE 1800-2017 14.4)");
|
||||
nodep->skewp()->v3error("Skew must be constant (IEEE 1800-2023 14.4)");
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
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);
|
||||
m_clockingp->addVarsp(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};
|
||||
if (skewp->isZero()) {
|
||||
// Drive the var in Re-NBA (IEEE 1800-2017 14.16)
|
||||
// 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});
|
||||
} else if (skewp->fileline()->timingOn()) {
|
||||
@@ -206,12 +212,12 @@ private:
|
||||
AstVarRef* const refp = new AstVarRef{flp, varp, VAccess::WRITE};
|
||||
refp->user1(true);
|
||||
if (skewp->num().is1Step()) {
|
||||
// Assign the sampled expression to the clockvar (IEEE 1800-2017 14.13)
|
||||
// Assign the sampled expression to the clockvar (IEEE 1800-2023 14.13)
|
||||
AstSampled* const sampledp = new AstSampled{flp, exprp->cloneTreePure(false)};
|
||||
sampledp->dtypeFrom(exprp);
|
||||
m_clockingp->addNextHere(new AstAssignW{flp, refp, sampledp});
|
||||
} else if (skewp->isZero()) {
|
||||
// #0 means the var has to be sampled in Observed (IEEE 1800-2017 14.13)
|
||||
// #0 means the var has to be sampled in Observed (IEEE 1800-2023 14.13)
|
||||
AstAssign* const assignp = new AstAssign{flp, refp, exprp->cloneTreePure(false)};
|
||||
m_clockingp->addNextHere(new AstAlwaysObserved{
|
||||
flp, new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)}, assignp});
|
||||
@@ -223,6 +229,7 @@ private:
|
||||
AstVar* const queueVarp = new AstVar{
|
||||
flp, VVarType::MODULETEMP,
|
||||
"__Vqueue__" + m_clockingp->name() + "__DOT__" + varp->name(), queueDtp};
|
||||
queueVarp->lifetime(VLifetime::STATIC);
|
||||
m_clockingp->addNextHere(queueVarp);
|
||||
// Create a process like this:
|
||||
// always queue.push(<sampled var>);
|
||||
@@ -249,20 +256,20 @@ 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
|
||||
if (!nodep->isCycleDelay()) {
|
||||
if (m_inSynchDrive) {
|
||||
nodep->v3error("Only cycle delays can be used in synchronous drives"
|
||||
" (IEEE 1800-2017 14.16)");
|
||||
" (IEEE 1800-2023 14.16)");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (m_inAssign && !m_inSynchDrive) {
|
||||
nodep->v3error("Cycle delays not allowed as intra-assignment delays"
|
||||
" (IEEE 1800-2017 14.11)");
|
||||
" (IEEE 1800-2023 14.11)");
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
@@ -273,12 +280,14 @@ private:
|
||||
if (constp->isZero()) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: ##0 delays");
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
VL_DO_DANGLING(valuep->deleteTree(), valuep);
|
||||
return;
|
||||
}
|
||||
if (!m_defaultClockingp) {
|
||||
nodep->v3error("Usage of cycle delays requires default clocking"
|
||||
" (IEEE 1800-2017 14.11)");
|
||||
" (IEEE 1800-2023 14.11)");
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
VL_DO_DANGLING(valuep->deleteTree(), valuep);
|
||||
return;
|
||||
}
|
||||
AstEventControl* const controlp = new AstEventControl{
|
||||
@@ -287,6 +296,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{
|
||||
@@ -302,24 +313,24 @@ private:
|
||||
void visit(AstSenTree* nodep) override {
|
||||
if (m_inSynchDrive) {
|
||||
nodep->v3error("Event controls cannot be used in "
|
||||
"synchronous drives (IEEE 1800-2017 14.16)");
|
||||
"synchronous drives (IEEE 1800-2023 14.16)");
|
||||
}
|
||||
}
|
||||
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) {
|
||||
nodep->v3error("Cannot read from output clockvar (IEEE 1800-2017 14.3)");
|
||||
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-2017 14.16)");
|
||||
"to clockvars (IEEE 1800-2023 14.16)");
|
||||
}
|
||||
if (m_inAssign) m_inSynchDrive = true;
|
||||
} else if (!nodep->user1() && itemp->direction() == VDirection::INPUT) {
|
||||
nodep->v3error("Cannot write to input clockvar (IEEE 1800-2017 14.3)");
|
||||
nodep->v3error("Cannot write to input clockvar (IEEE 1800-2023 14.3)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -463,7 +474,7 @@ private:
|
||||
if (clockingp->isDefault()) {
|
||||
if (m_defaultClockingp) {
|
||||
clockingp->v3error("Only one default clocking block allowed per module"
|
||||
" (IEEE 1800-2017 14.12)");
|
||||
" (IEEE 1800-2023 14.12)");
|
||||
}
|
||||
m_defaultClockingp = clockingp;
|
||||
}
|
||||
|
||||
+54
-21
@@ -17,7 +17,6 @@
|
||||
#include "V3PchAstMT.h"
|
||||
|
||||
#include "V3Broken.h"
|
||||
#include "V3EmitV.h"
|
||||
#include "V3File.h"
|
||||
|
||||
#include <iomanip>
|
||||
@@ -1171,8 +1170,50 @@ void AstNode::checkTreeIter(const AstNode* prevBackp) const VL_MT_STABLE {
|
||||
default: this->v3fatalSrc("Bad case"); break;
|
||||
}
|
||||
}
|
||||
if (v3Global.opt.debugWidth() && v3Global.widthMinUsage() == VWidthMinUsage::VERILOG_WIDTH) {
|
||||
if (const AstNodeExpr* const exprp = VN_CAST(this, NodeExpr)) {
|
||||
const char* const whyp = exprp->widthMismatch();
|
||||
if (whyp) {
|
||||
auto dtypeStr = [](const AstNodeExpr* exprp) VL_MT_STABLE {
|
||||
std::ostringstream ss;
|
||||
exprp->dtypep()->dumpSmall(ss);
|
||||
return ss.str();
|
||||
};
|
||||
if (const AstNodeUniop* const uniopp = VN_CAST(exprp, NodeUniop)) {
|
||||
UASSERT_OBJ(!whyp, uniopp,
|
||||
"widthMismatch detected " << whyp << "OUT:" << dtypeStr(uniopp)
|
||||
<< " LHS:" << dtypeStr(uniopp->lhsp()));
|
||||
} else if (const AstNodeBiop* const biopp = VN_CAST(exprp, NodeBiop)) {
|
||||
UASSERT_OBJ(!whyp, biopp,
|
||||
"widthMismatch detected " << whyp << "OUT:" << dtypeStr(biopp)
|
||||
<< " LHS:" << dtypeStr(biopp->lhsp())
|
||||
<< " RHS:" << dtypeStr(biopp->rhsp()));
|
||||
} else {
|
||||
UASSERT_OBJ(false, exprp,
|
||||
"widthMismatch detected " << whyp << " in an unexpected type");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cppcheck-suppress unusedFunction // Debug only
|
||||
char* AstNode::dumpTreeJsonGdb(const AstNode* nodep) {
|
||||
if (!nodep) return strdup("{\"addr\":\"NULL\"}\n");
|
||||
std::stringstream nodepStream;
|
||||
nodep->dumpTreeJson(nodepStream);
|
||||
const std::string str = nodepStream.rdbuf()->str();
|
||||
return strdup(str.c_str());
|
||||
}
|
||||
// cppcheck-suppress unusedFunction // Debug only
|
||||
// identity func to allow for passing already done dumps to jtree
|
||||
char* AstNode::dumpTreeJsonGdb(const char* str) { return strdup(str); }
|
||||
// cppcheck-suppress unusedFunction // Debug only
|
||||
// allow for passing pointer literals like 0x42.. without manual cast
|
||||
char* AstNode::dumpTreeJsonGdb(intptr_t nodep) {
|
||||
if (!nodep) return strdup("{\"addr\":\"NULL\"}\n");
|
||||
return dumpTreeJsonGdb((const AstNode*)nodep);
|
||||
}
|
||||
// cppcheck-suppress unusedFunction // Debug only
|
||||
void AstNode::dumpGdb(const AstNode* nodep) { // For GDB only // LCOV_EXCL_LINE
|
||||
if (!nodep) {
|
||||
@@ -1276,7 +1317,7 @@ void AstNode::dumpTreeAndNext(std::ostream& os, const string& indent, int maxDep
|
||||
}
|
||||
}
|
||||
|
||||
void AstNode::dumpTreeFile(const string& filename, bool doDump, bool doCheck) {
|
||||
void AstNode::dumpTreeFile(const string& filename, bool doDump) {
|
||||
// Not const function as calls checkTree
|
||||
if (doDump) {
|
||||
{ // Write log & close
|
||||
@@ -1294,14 +1335,6 @@ void AstNode::dumpTreeFile(const string& filename, bool doDump, bool doCheck) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (doDump && v3Global.opt.debugEmitV()) V3EmitV::debugEmitV(filename + ".v");
|
||||
if (doCheck && (v3Global.opt.debugCheck() || ::dumpTreeLevel())) {
|
||||
// Error check
|
||||
checkTree();
|
||||
// Broken isn't part of check tree because it can munge iterp's
|
||||
// set by other steps if it is called in the middle of other operations
|
||||
if (AstNetlist* const netp = VN_CAST(this, Netlist)) V3Broken::brokenAll(netp);
|
||||
}
|
||||
}
|
||||
|
||||
static void drawChildren(std::ostream& os, const AstNode* thisp, const AstNode* childp,
|
||||
@@ -1340,10 +1373,11 @@ void AstNode::dumpTreeJsonFile(const string& filename, bool doDump) {
|
||||
*treejsonp << '\n';
|
||||
}
|
||||
|
||||
void AstNode::dumpJsonMetaFileGdb(const char* filename) { dumpJsonMetaFile(filename); }
|
||||
void AstNode::dumpJsonMetaFile(const string& filename) {
|
||||
UINFO(2, "Dumping " << filename << endl);
|
||||
const std::unique_ptr<std::ofstream> treejsonp{V3File::new_ofstream(filename)};
|
||||
if (treejsonp->fail()) v3fatal("Can't write " << filename);
|
||||
if (treejsonp->fail()) v3fatalStatic("Can't write " << filename);
|
||||
*treejsonp << '{';
|
||||
FileLine::fileNameNumMapDumpJson(*treejsonp);
|
||||
*treejsonp << ',';
|
||||
@@ -1530,8 +1564,7 @@ static VCastable computeCastableImp(const AstNodeDType* toDtp, const AstNodeDTyp
|
||||
return VCastable::ENUM_IMPLICIT;
|
||||
if (fromNumericable) return VCastable::ENUM_EXPLICIT;
|
||||
} else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromConstp, Const)) {
|
||||
if (VN_IS(fromConstp, Const) && VN_AS(fromConstp, Const)->num().isNull())
|
||||
return VCastable::COMPATIBLE;
|
||||
if (fromConstp->isNull()) return VCastable::COMPATIBLE;
|
||||
} else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromDtp, ClassRefDType)) {
|
||||
const auto toClassp = VN_AS(toDtp, ClassRefDType)->classp();
|
||||
const auto fromClassp = VN_AS(fromDtp, ClassRefDType)->classp();
|
||||
@@ -1557,26 +1590,26 @@ VCastable AstNode::computeCastable(const AstNodeDType* toDtp, const AstNodeDType
|
||||
return castable;
|
||||
}
|
||||
|
||||
AstNodeDType* AstNode::getCommonClassTypep(AstNode* nodep1, AstNode* nodep2) {
|
||||
// Return the class type that both nodep1 and nodep2 are castable to.
|
||||
AstNodeDType* AstNode::getCommonClassTypep(AstNode* node1p, AstNode* node2p) {
|
||||
// Return the class type that both node1p and node2p are castable to.
|
||||
// If both are null, return the type of null constant.
|
||||
// If one is a class and one is null, return AstClassRefDType that points to that class.
|
||||
// If no common class type exists, return nullptr.
|
||||
|
||||
// First handle cases with null values and when one class is a super class of the other.
|
||||
if (VN_IS(nodep1, Const)) std::swap(nodep1, nodep2);
|
||||
if (VN_IS(node1p, Const)) std::swap(node1p, node2p);
|
||||
{
|
||||
const VCastable castable = computeCastable(nodep1->dtypep(), nodep2->dtypep(), nodep2);
|
||||
const VCastable castable = computeCastable(node1p->dtypep(), node2p->dtypep(), node2p);
|
||||
if (castable == VCastable::SAMEISH || castable == VCastable::COMPATIBLE) {
|
||||
return nodep1->dtypep();
|
||||
return node1p->dtypep();
|
||||
} else if (castable == VCastable::DYNAMIC_CLASS) {
|
||||
return nodep2->dtypep();
|
||||
return node2p->dtypep();
|
||||
}
|
||||
}
|
||||
|
||||
AstClassRefDType* classDtypep1 = VN_CAST(nodep1->dtypep(), ClassRefDType);
|
||||
AstClassRefDType* classDtypep1 = VN_CAST(node1p->dtypep(), ClassRefDType);
|
||||
while (classDtypep1) {
|
||||
const VCastable castable = computeCastable(classDtypep1, nodep2->dtypep(), nodep2);
|
||||
const VCastable castable = computeCastable(classDtypep1, node2p->dtypep(), node2p);
|
||||
if (castable == VCastable::COMPATIBLE) return classDtypep1;
|
||||
AstClassExtends* const extendsp = classDtypep1->classp()->extendsp();
|
||||
classDtypep1 = extendsp ? VN_AS(extendsp->dtypep(), ClassRefDType) : nullptr;
|
||||
|
||||
+126
-33
@@ -49,9 +49,6 @@ class VFlagLogicPacked {};
|
||||
class VFlagBitPacked {};
|
||||
class VFlagChildDType {}; // Used by parser.y to select constructor that sets childDType
|
||||
|
||||
// Used as key for another map, needs operator<, hence not an unordered_set
|
||||
using MTaskIdSet = std::set<int>; // Set of mtaskIds for Var sorting
|
||||
|
||||
//######################################################################
|
||||
|
||||
// For broken() function, return error string if have a match
|
||||
@@ -245,6 +242,51 @@ inline std::ostream& operator<<(std::ostream& os, const VAccess& rhs) { return o
|
||||
|
||||
// ######################################################################
|
||||
|
||||
class VBaseOverride final {
|
||||
bool m_extends : 1;
|
||||
bool m_final : 1;
|
||||
bool m_initial : 1;
|
||||
|
||||
public:
|
||||
VBaseOverride()
|
||||
: m_extends{false}
|
||||
, m_final{false}
|
||||
, m_initial{false} {}
|
||||
class Extends {};
|
||||
explicit VBaseOverride(Extends)
|
||||
: m_extends{true}
|
||||
, m_final{false}
|
||||
, m_initial{false} {}
|
||||
class Final {};
|
||||
explicit VBaseOverride(Final)
|
||||
: m_extends{false}
|
||||
, m_final{true}
|
||||
, m_initial{false} {}
|
||||
class Initial {};
|
||||
explicit VBaseOverride(Initial)
|
||||
: m_extends{false}
|
||||
, m_final{false}
|
||||
, m_initial{true} {}
|
||||
void combine(const VBaseOverride& other) {
|
||||
m_extends |= other.m_extends;
|
||||
m_final |= other.m_final;
|
||||
m_initial |= other.m_initial;
|
||||
}
|
||||
bool isAny() const { return m_extends | m_final | m_initial; }
|
||||
bool isExtends() const { return m_extends; }
|
||||
bool isFinal() const { return m_final; }
|
||||
bool isInitial() const { return m_initial; }
|
||||
string ascii() const {
|
||||
string out;
|
||||
if (m_initial) out = VString::dot(out, " ", "initial");
|
||||
if (m_extends) out = VString::dot(out, " ", "extends");
|
||||
if (m_final) out = VString::dot(out, " ", "final");
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
// ######################################################################
|
||||
|
||||
class VSigning final {
|
||||
public:
|
||||
enum en : uint8_t {
|
||||
@@ -323,7 +365,6 @@ public:
|
||||
enum en : uint8_t {
|
||||
// These must be in general -> most specific order, as we sort by it
|
||||
// in V3Const::visit AstSenTree
|
||||
ET_ILLEGAL,
|
||||
// Involving a variable
|
||||
ET_CHANGED, // Value changed
|
||||
ET_BOTHEDGE, // POSEDGE | NEGEDGE (i.e.: 'edge' in Verilog)
|
||||
@@ -343,8 +384,6 @@ public:
|
||||
enum en m_e;
|
||||
bool clockedStmt() const {
|
||||
static const bool clocked[] = {
|
||||
false, // ET_ILLEGAL
|
||||
|
||||
true, // ET_CHANGED
|
||||
true, // ET_BOTHEDGE
|
||||
true, // ET_POSEDGE
|
||||
@@ -368,20 +407,19 @@ public:
|
||||
case ET_BOTHEDGE: return ET_BOTHEDGE;
|
||||
case ET_POSEDGE: return ET_NEGEDGE;
|
||||
case ET_NEGEDGE: return ET_POSEDGE;
|
||||
default: UASSERT_STATIC(0, "Inverting bad edgeType()");
|
||||
default: UASSERT_STATIC(0, "Inverting bad edgeType()"); return ET_NEGEDGE;
|
||||
}
|
||||
return VEdgeType::ET_ILLEGAL;
|
||||
}
|
||||
const char* ascii() const {
|
||||
static const char* const names[]
|
||||
= {"%E-edge", "CHANGED", "BOTH", "POS", "NEG", "EVENT", "TRUE",
|
||||
"COMBO", "HYBRID", "STATIC", "INITIAL", "FINAL", "NEVER"};
|
||||
= {"CHANGED", "BOTH", "POS", "NEG", "EVENT", "TRUE",
|
||||
"COMBO", "HYBRID", "STATIC", "INITIAL", "FINAL", "NEVER"};
|
||||
return names[m_e];
|
||||
}
|
||||
const char* verilogKwd() const {
|
||||
static const char* const names[]
|
||||
= {"%E-edge", "[changed]", "edge", "posedge", "negedge", "[event]", "[true]",
|
||||
"*", "[hybrid]", "[static]", "[initial]", "[final]", "[never]"};
|
||||
= {"[changed]", "edge", "posedge", "negedge", "[event]", "[true]",
|
||||
"*", "[hybrid]", "[static]", "[initial]", "[final]", "[never]"};
|
||||
return names[m_e];
|
||||
}
|
||||
// Return true iff this and the other have mutually exclusive transitions
|
||||
@@ -397,8 +435,6 @@ public:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
VEdgeType()
|
||||
: m_e{ET_ILLEGAL} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
constexpr VEdgeType(en _e)
|
||||
: m_e{_e} {}
|
||||
@@ -523,6 +559,7 @@ public:
|
||||
DYNAMIC_TRIGGER_SCHEDULER,
|
||||
FORK_SYNC,
|
||||
PROCESS_REFERENCE,
|
||||
RANDOM_GENERATOR,
|
||||
// Unsigned and two state; fundamental types
|
||||
UINT32,
|
||||
UINT64,
|
||||
@@ -556,6 +593,7 @@ public:
|
||||
"VlDynamicTriggerScheduler",
|
||||
"VlFork",
|
||||
"VlProcessRef",
|
||||
"VlRandomizer",
|
||||
"IData",
|
||||
"QData",
|
||||
"LOGIC_IMPLICIT",
|
||||
@@ -563,20 +601,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() {
|
||||
@@ -616,6 +647,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;
|
||||
@@ -655,7 +687,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; }
|
||||
@@ -867,7 +900,7 @@ public:
|
||||
TRIWIRE,
|
||||
TRI0,
|
||||
TRI1,
|
||||
PORT, // Temp type used in parser only
|
||||
PORT, // Used in parser and V3Fork to recognize ports
|
||||
BLOCKTEMP,
|
||||
MODULETEMP,
|
||||
STMTTEMP,
|
||||
@@ -1066,6 +1099,61 @@ 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 VCaseType final {
|
||||
public:
|
||||
enum en : uint8_t { CT_CASE, CT_CASEX, CT_CASEZ, CT_CASEINSIDE };
|
||||
@@ -2002,7 +2090,7 @@ public:
|
||||
|
||||
// TODO stomp these width functions out, and call via dtypep() instead
|
||||
inline int width() const VL_MT_STABLE;
|
||||
inline int widthMin() const;
|
||||
inline int widthMin() const VL_MT_STABLE;
|
||||
int widthMinV() const {
|
||||
return v3Global.widthMinUsage() == VWidthMinUsage::VERILOG_WIDTH ? widthMin() : width();
|
||||
}
|
||||
@@ -2088,6 +2176,7 @@ public:
|
||||
// ACCESSORS for specific types
|
||||
// Alas these can't be virtual or they break when passed a nullptr
|
||||
inline bool isClassHandleValue() const;
|
||||
inline bool isNull() const;
|
||||
inline bool isZero() const;
|
||||
inline bool isOne() const;
|
||||
inline bool isNeqZero() const;
|
||||
@@ -2166,6 +2255,9 @@ public:
|
||||
string warnOther() const VL_REQUIRES(V3Error::s().m_mutex) { return fileline()->warnOther(); }
|
||||
|
||||
virtual void dump(std::ostream& str = std::cout) const;
|
||||
static char* dumpTreeJsonGdb(const AstNode* nodep); // For GDB only, free()d by caller
|
||||
static char* dumpTreeJsonGdb(const char* str); // For GDB only, free()d by caller
|
||||
static char* dumpTreeJsonGdb(intptr_t nodep); // For GDB only, free()d by caller
|
||||
static void dumpGdb(const AstNode* nodep); // For GDB only
|
||||
void dumpGdbHeader() const;
|
||||
|
||||
@@ -2216,7 +2308,7 @@ public:
|
||||
static void dumpTreeGdb(const AstNode* nodep); // For GDB only
|
||||
void dumpTreeAndNext(std::ostream& os = std::cout, const string& indent = " ",
|
||||
int maxDepth = 0) const;
|
||||
void dumpTreeFile(const string& filename, bool doDump = true, bool doCheck = true);
|
||||
void dumpTreeFile(const string& filename, bool doDump = true);
|
||||
static void dumpTreeFileGdb(const AstNode* nodep, const char* filenamep = nullptr);
|
||||
void dumpTreeDot(std::ostream& os = std::cout) const;
|
||||
void dumpTreeDotFile(const string& filename, bool doDump = true);
|
||||
@@ -2227,7 +2319,8 @@ public:
|
||||
virtual void dumpTreeJsonOpGen(std::ostream& os, const string& indent) const {};
|
||||
void dumpTreeJson(std::ostream& os, const string& indent = "") const;
|
||||
void dumpTreeJsonFile(const string& filename, bool doDump = true);
|
||||
void dumpJsonMetaFile(const string& filename);
|
||||
static void dumpJsonMetaFileGdb(const char* filename);
|
||||
static void dumpJsonMetaFile(const string& filename);
|
||||
|
||||
// Render node address for dumps. By default this is just the address
|
||||
// printed as hex, but with --dump-tree-addrids we map addresses to short
|
||||
|
||||
+3
-2
@@ -26,7 +26,7 @@
|
||||
// Inline METHODS
|
||||
|
||||
int AstNode::width() const VL_MT_STABLE { return dtypep() ? dtypep()->width() : 0; }
|
||||
int AstNode::widthMin() const { return dtypep() ? dtypep()->widthMin() : 0; }
|
||||
int AstNode::widthMin() const VL_MT_STABLE { return dtypep() ? dtypep()->widthMin() : 0; }
|
||||
bool AstNode::width1() const { // V3Const uses to know it can optimize
|
||||
return dtypep() && dtypep()->width() == 1;
|
||||
}
|
||||
@@ -47,8 +47,9 @@ bool AstNode::isSigned() const VL_MT_STABLE { return dtypep() && dtypep()->isSig
|
||||
|
||||
bool AstNode::isClassHandleValue() const {
|
||||
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isNull())
|
||||
|| VN_IS(dtypep(), ClassRefDType);
|
||||
|| (dtypep() && VN_IS(dtypep()->skipRefp(), ClassRefDType));
|
||||
}
|
||||
bool AstNode::isNull() const { return VN_IS(this, Const) && VN_AS(this, Const)->num().isNull(); }
|
||||
bool AstNode::isZero() const {
|
||||
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqZero());
|
||||
}
|
||||
|
||||
+63
-22
@@ -53,7 +53,7 @@ public:
|
||||
// ACCESSORS
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
virtual void dumpSmall(std::ostream& str) const;
|
||||
virtual void dumpSmall(std::ostream& str) const VL_MT_STABLE;
|
||||
bool hasDType() const override { return true; }
|
||||
/// Require VlUnpacked, instead of [] for POD elements.
|
||||
/// A non-POD object is always compound, but some POD elements
|
||||
@@ -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.
|
||||
@@ -126,13 +126,13 @@ public:
|
||||
const char* charIQWN() const {
|
||||
return (isString() ? "N" : isWide() ? "W" : isQuad() ? "Q" : "I");
|
||||
}
|
||||
string cType(const string& name, bool forFunc, bool isRef) const;
|
||||
string cType(const string& name, bool forFunc, bool isRef, bool packed = false) const;
|
||||
// Represents a C++ LiteralType? (can be constexpr)
|
||||
bool isLiteralType() const VL_MT_STABLE;
|
||||
|
||||
private:
|
||||
class CTypeRecursed;
|
||||
CTypeRecursed cTypeRecurse(bool compound) const;
|
||||
CTypeRecursed cTypeRecurse(bool compound, bool packed) const;
|
||||
};
|
||||
class AstNodeArrayDType VL_NOT_FINAL : public AstNodeDType {
|
||||
// Array data type, ie "some_dtype var_name [2:0]"
|
||||
@@ -215,6 +215,7 @@ 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 {
|
||||
@@ -313,7 +314,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 +388,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 +429,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(); }
|
||||
@@ -469,7 +473,7 @@ public:
|
||||
this->elementsp(elementsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstBracketArrayDType;
|
||||
bool similarDType(const AstNodeDType* samep) const override { V3ERROR_NA_RETURN(false); }
|
||||
bool similarDType(const AstNodeDType* samep) const override { return same(samep); }
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE { return childDTypep(); }
|
||||
// METHODS
|
||||
// Will be removed in V3Width, which relies on this
|
||||
@@ -501,7 +505,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; }
|
||||
@@ -510,11 +514,17 @@ public:
|
||||
int widthAlignBytes() const override { return 8; } // Assume
|
||||
int widthTotalBytes() const override { return 8; } // Assume
|
||||
bool isCompound() const override { return true; }
|
||||
static string typeToHold(uint64_t maxItem) {
|
||||
return (maxItem < (1ULL << 8)) ? "CData"
|
||||
: (maxItem < (1ULL << 16)) ? "SData"
|
||||
: (maxItem < (1ULL << 32)) ? "IData"
|
||||
: "QData";
|
||||
static string typeToHold(int width) {
|
||||
if (width <= 8)
|
||||
return "CData";
|
||||
else if (width <= 16)
|
||||
return "SData";
|
||||
else if (width <= VL_IDATASIZE)
|
||||
return "IData";
|
||||
else if (width <= VL_QUADSIZE)
|
||||
return "QData";
|
||||
else
|
||||
return "VlWide<" + std::to_string(VL_WORDS_I(width)) + ">";
|
||||
}
|
||||
};
|
||||
class AstClassRefDType final : public AstNodeDType {
|
||||
@@ -542,6 +552,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; }
|
||||
@@ -710,7 +721,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 {
|
||||
@@ -800,6 +811,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(); }
|
||||
@@ -808,9 +820,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; }
|
||||
@@ -953,6 +965,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
|
||||
@@ -1060,7 +1096,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();
|
||||
@@ -1123,8 +1159,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;
|
||||
@@ -1256,6 +1292,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())));
|
||||
@@ -1351,7 +1392,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 {
|
||||
@@ -1378,7 +1419,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;
|
||||
|
||||
+82
-5
@@ -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
|
||||
@@ -65,6 +66,9 @@ public:
|
||||
|
||||
// Wrap This expression into an AstStmtExpr to denote it occurs in statement position
|
||||
inline AstStmtExpr* makeStmt();
|
||||
// Returns an error message if widthMin() is not correct otherwise returns nullptr like
|
||||
// broken()
|
||||
virtual const char* widthMismatch() const VL_MT_STABLE { return nullptr; }
|
||||
};
|
||||
class AstNodeBiop VL_NOT_FINAL : public AstNodeExpr {
|
||||
// Binary expression
|
||||
@@ -1109,6 +1113,34 @@ public:
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
};
|
||||
class AstCvtPackedToUnpackArray final : public AstNodeExpr {
|
||||
// Cast from packed array to dynamic queue data type
|
||||
// @astgen op1 := fromp : AstNodeExpr
|
||||
public:
|
||||
AstCvtPackedToUnpackArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_CvtPackedToUnpackArray(fl) {
|
||||
this->fromp(fromp);
|
||||
dtypeFrom(dtp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCvtPackedToUnpackArray;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
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; }
|
||||
};
|
||||
class AstDot final : public AstNodeExpr {
|
||||
// A dot separating paths in an AstVarXRef, AstFuncRef or AstTaskRef
|
||||
// These are eliminated in the link stage
|
||||
@@ -2228,7 +2260,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);
|
||||
@@ -2251,13 +2283,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; }
|
||||
@@ -2370,6 +2402,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; }
|
||||
@@ -2416,6 +2449,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; }
|
||||
@@ -2462,6 +2496,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; }
|
||||
@@ -2488,6 +2523,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; }
|
||||
@@ -2610,6 +2646,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; }
|
||||
@@ -2680,6 +2717,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; }
|
||||
@@ -2703,6 +2741,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; }
|
||||
@@ -2773,6 +2812,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; }
|
||||
@@ -2796,6 +2836,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; }
|
||||
@@ -2819,6 +2860,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; }
|
||||
@@ -2842,6 +2884,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; }
|
||||
@@ -2865,6 +2908,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; }
|
||||
@@ -2935,6 +2979,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; }
|
||||
@@ -2958,6 +3003,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; }
|
||||
@@ -3028,6 +3074,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; }
|
||||
@@ -3051,6 +3098,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; }
|
||||
@@ -3073,6 +3121,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; }
|
||||
@@ -3246,6 +3295,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; }
|
||||
@@ -3294,6 +3346,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()) ? "" : "<<";
|
||||
}
|
||||
@@ -3342,6 +3395,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()) ? "" : ">>";
|
||||
}
|
||||
@@ -3394,6 +3448,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; }
|
||||
@@ -3445,6 +3500,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; }
|
||||
@@ -3521,6 +3577,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; }
|
||||
@@ -3637,6 +3694,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; }
|
||||
@@ -3661,6 +3719,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; }
|
||||
@@ -3778,6 +3837,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; }
|
||||
@@ -3824,12 +3884,14 @@ 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; }
|
||||
bool cleanRhs() const override { return false; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
const char* widthMismatch() const override VL_MT_STABLE;
|
||||
};
|
||||
class AstMul final : public AstNodeBiComAsv {
|
||||
public:
|
||||
@@ -3846,6 +3908,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; }
|
||||
@@ -3918,12 +3981,14 @@ 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; }
|
||||
bool cleanRhs() const override { return false; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
const char* widthMismatch() const override VL_MT_STABLE;
|
||||
};
|
||||
class AstXor final : public AstNodeBiComAsv {
|
||||
public:
|
||||
@@ -3940,12 +4005,14 @@ 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; }
|
||||
bool cleanRhs() const override { return false; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
const char* widthMismatch() const override VL_MT_STABLE;
|
||||
};
|
||||
|
||||
// === AstNodeDistBiop ===
|
||||
@@ -4861,6 +4928,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 {
|
||||
@@ -4884,6 +4954,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 {
|
||||
@@ -5016,6 +5089,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; }
|
||||
@@ -5046,6 +5120,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; }
|
||||
@@ -5078,10 +5153,12 @@ 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; }
|
||||
bool sizeMattersLhs() const override { return true; }
|
||||
const char* widthMismatch() const override VL_MT_STABLE;
|
||||
};
|
||||
class AstNullCheck final : public AstNodeUniop {
|
||||
// Return LHS after checking that LHS is non-null
|
||||
|
||||
+78
-32
@@ -76,7 +76,6 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
|
||||
bool m_dpiContext : 1; // DPI import context
|
||||
bool m_dpiOpenChild : 1; // DPI import open array child wrapper
|
||||
bool m_dpiTask : 1; // DPI import task (vs. void function)
|
||||
bool m_dpiTraceInit : 1; // DPI trace_init
|
||||
bool m_isConstructor : 1; // Class constructor
|
||||
bool m_isHideLocal : 1; // Verilog local
|
||||
bool m_isHideProtected : 1; // Verilog protected
|
||||
@@ -87,6 +86,7 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
|
||||
bool m_underGenerate : 1; // Under generate (for warning)
|
||||
bool m_virtual : 1; // Virtual method in class
|
||||
bool m_needProcess : 1; // Needs access to VlProcess of the caller
|
||||
VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends)
|
||||
VLifetime m_lifetime; // Default lifetime of local vars
|
||||
VIsCached m_purity; // Pure state
|
||||
|
||||
@@ -105,7 +105,6 @@ protected:
|
||||
, m_dpiContext{false}
|
||||
, m_dpiOpenChild{false}
|
||||
, m_dpiTask{false}
|
||||
, m_dpiTraceInit{false}
|
||||
, m_isConstructor{false}
|
||||
, m_isHideLocal{false}
|
||||
, m_isHideProtected{false}
|
||||
@@ -161,8 +160,6 @@ public:
|
||||
bool dpiOpenChild() const { return m_dpiOpenChild; }
|
||||
void dpiTask(bool flag) { m_dpiTask = flag; }
|
||||
bool dpiTask() const { return m_dpiTask; }
|
||||
void dpiTraceInit(bool flag) { m_dpiTraceInit = flag; }
|
||||
bool dpiTraceInit() const { return m_dpiTraceInit; }
|
||||
void isConstructor(bool flag) { m_isConstructor = flag; }
|
||||
bool isConstructor() const { return m_isConstructor; }
|
||||
bool isHideLocal() const { return m_isHideLocal; }
|
||||
@@ -183,6 +180,8 @@ public:
|
||||
bool isVirtual() const { return m_virtual; }
|
||||
void setNeedProcess() { m_needProcess = true; }
|
||||
bool needProcess() const { return m_needProcess; }
|
||||
void baseOverride(const VBaseOverride& flag) { m_baseOverride = flag; }
|
||||
VBaseOverride baseOverride() const { return m_baseOverride; }
|
||||
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
|
||||
VLifetime lifetime() const { return m_lifetime; }
|
||||
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
|
||||
@@ -627,7 +626,6 @@ class AstCFunc final : public AstNode {
|
||||
bool m_dpiExportImpl : 1; // DPI export implementation (called from DPI dispatcher via lookup)
|
||||
bool m_dpiImportPrototype : 1; // This is the DPI import prototype (i.e.: provided by user)
|
||||
bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code
|
||||
bool m_dpiTraceInit : 1; // DPI trace_init
|
||||
bool m_needProcess : 1; // Needs access to VlProcess of the caller
|
||||
bool m_recursive : 1; // Recursive or part of recursion
|
||||
public:
|
||||
@@ -657,7 +655,6 @@ public:
|
||||
m_dpiExportImpl = false;
|
||||
m_dpiImportPrototype = false;
|
||||
m_dpiImportWrapper = false;
|
||||
m_dpiTraceInit = false;
|
||||
m_recursive = false;
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCFunc;
|
||||
@@ -731,8 +728,6 @@ public:
|
||||
void dpiImportPrototype(bool flag) { m_dpiImportPrototype = flag; }
|
||||
bool dpiImportWrapper() const { return m_dpiImportWrapper; }
|
||||
void dpiImportWrapper(bool flag) { m_dpiImportWrapper = flag; }
|
||||
void dpiTraceInit(bool flag) { m_dpiTraceInit = flag; }
|
||||
bool dpiTraceInit() const { return m_dpiTraceInit; }
|
||||
bool isCoroutine() const { return m_rtnType == "VlCoroutine"; }
|
||||
void recursive(bool flag) { m_recursive = flag; }
|
||||
bool recursive() const { return m_recursive; }
|
||||
@@ -863,13 +858,42 @@ public:
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
// ACCESSORS
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Cell name
|
||||
bool maybePointedTo() const override { return true; }
|
||||
string origModName() const { return m_origModName; } // * = modp()->origName() before inlining
|
||||
void name(const string& name) override { m_name = name; }
|
||||
void scopep(AstScope* scp) { m_scopep = scp; }
|
||||
AstScope* scopep() const { return m_scopep; }
|
||||
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
|
||||
VTimescale timeunit() const { return m_timeunit; }
|
||||
};
|
||||
|
||||
class AstCellInlineScope final : public AstNode {
|
||||
// A particular scoped usage of a Cell Inline
|
||||
// Parents: Scope
|
||||
// Children: none
|
||||
//
|
||||
// @astgen ptr := m_scopep : Optional[AstScope] // Scope variable is underneath
|
||||
// @astgen ptr := m_cellp : Optional[AstCellInline] // Cell ref
|
||||
const string m_origModName; // Original name of module, ignoring name() changes, for LinkDot
|
||||
public:
|
||||
AstCellInlineScope(FileLine* fl, AstScope* scopep, AstCellInline* cellp)
|
||||
: ASTGEN_SUPER_CellInlineScope(fl)
|
||||
, m_scopep{scopep}
|
||||
, m_cellp{cellp} {
|
||||
UASSERT_OBJ(scopep, fl, "Scope must be non-null");
|
||||
UASSERT_OBJ(cellp, fl, "CellInline must be non-null");
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCellInlineScope;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
// ACCESSORS
|
||||
string name() const override VL_MT_STABLE { return m_cellp->name(); }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
AstScope* scopep() const VL_MT_STABLE { return m_scopep; } // Pointer to scope it's under
|
||||
string origModName() const {
|
||||
return m_cellp->origModName();
|
||||
} // * = modp()->origName() before inlining
|
||||
void scopep(AstScope* nodep) { m_scopep = nodep; }
|
||||
};
|
||||
|
||||
class AstClassExtends final : public AstNode {
|
||||
// class extends class name, or class implements class name
|
||||
// Children: List of AstParseRef for packages/classes
|
||||
@@ -932,6 +956,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:
|
||||
@@ -947,6 +972,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
|
||||
@@ -1115,8 +1143,9 @@ class AstExecGraph final : public AstNode {
|
||||
|
||||
public:
|
||||
explicit AstExecGraph(FileLine* fl, const string& name) VL_MT_DISABLED;
|
||||
ASTGEN_MEMBERS_AstExecGraph;
|
||||
~AstExecGraph() override;
|
||||
ASTGEN_MEMBERS_AstExecGraph;
|
||||
void cloneRelink() override { V3ERROR_NA; }
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(!m_depGraphp);
|
||||
return nullptr;
|
||||
@@ -1170,6 +1199,7 @@ public:
|
||||
explicit AstMTaskBody(FileLine* fl)
|
||||
: ASTGEN_SUPER_MTaskBody(fl) {}
|
||||
ASTGEN_MEMBERS_AstMTaskBody;
|
||||
void cloneRelink() override { V3ERROR_NA; }
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(!m_execMTaskp);
|
||||
return nullptr;
|
||||
@@ -1267,9 +1297,6 @@ class AstNetlist final : public AstNode {
|
||||
VTimescale m_timeunit; // Global time unit
|
||||
VTimescale m_timeprecision; // Global time precision
|
||||
bool m_timescaleSpecified = false; // Input HDL specified timescale
|
||||
uint32_t m_nextFreeMTaskID = 1; // Next unique MTask ID within netlist
|
||||
// starts at 1 so 0 means no MTask ID
|
||||
uint32_t m_nextFreeMTaskProfilingID = 0; // Next unique ID to use for PGO
|
||||
public:
|
||||
AstNetlist();
|
||||
ASTGEN_MEMBERS_AstNetlist;
|
||||
@@ -1310,9 +1337,6 @@ public:
|
||||
void timeprecisionMerge(FileLine*, const VTimescale& value);
|
||||
void timescaleSpecified(bool specified) { m_timescaleSpecified = specified; }
|
||||
bool timescaleSpecified() const { return m_timescaleSpecified; }
|
||||
uint32_t allocNextMTaskID() { return m_nextFreeMTaskID++; }
|
||||
uint32_t allocNextMTaskProfilingID() { return m_nextFreeMTaskProfilingID++; }
|
||||
uint32_t usedMTaskProfilingIDs() const { return m_nextFreeMTaskProfilingID; }
|
||||
};
|
||||
class AstPackageExport final : public AstNode {
|
||||
// A package export declaration
|
||||
@@ -1469,6 +1493,7 @@ class AstScope final : public AstNode {
|
||||
// Children: NODEBLOCK
|
||||
// @astgen op1 := varsp : List[AstVarScope]
|
||||
// @astgen op2 := blocksp : List[AstNode] // Logic blocks/AstActive/AstCFunc
|
||||
// @astgen op3 := inlinesp : List[AstCellInlineScope] // Cell Inlines
|
||||
//
|
||||
// Below scope and cell are nullptr if top scope
|
||||
// @astgen ptr := m_aboveScopep : Optional[AstScope] // Scope above this one in the hierarchy
|
||||
@@ -1514,7 +1539,6 @@ class AstSenItem final : public AstNode {
|
||||
VEdgeType m_edgeType; // Edge type
|
||||
public:
|
||||
class Combo {}; // for constructor type-overload selection
|
||||
class Illegal {}; // for constructor type-overload selection
|
||||
class Static {}; // for constructor type-overload selection
|
||||
class Initial {}; // for constructor type-overload selection
|
||||
class Final {}; // for constructor type-overload selection
|
||||
@@ -1528,9 +1552,6 @@ public:
|
||||
AstSenItem(FileLine* fl, Combo)
|
||||
: ASTGEN_SUPER_SenItem(fl)
|
||||
, m_edgeType{VEdgeType::ET_COMBO} {}
|
||||
AstSenItem(FileLine* fl, Illegal)
|
||||
: ASTGEN_SUPER_SenItem(fl)
|
||||
, m_edgeType{VEdgeType::ET_ILLEGAL} {}
|
||||
AstSenItem(FileLine* fl, Static)
|
||||
: ASTGEN_SUPER_SenItem(fl)
|
||||
, m_edgeType{VEdgeType::ET_STATIC} {}
|
||||
@@ -1562,7 +1583,6 @@ public:
|
||||
bool isStatic() const { return edgeType() == VEdgeType::ET_STATIC; }
|
||||
bool isInitial() const { return edgeType() == VEdgeType::ET_INITIAL; }
|
||||
bool isFinal() const { return edgeType() == VEdgeType::ET_FINAL; }
|
||||
bool isIllegal() const { return edgeType() == VEdgeType::ET_ILLEGAL; }
|
||||
bool isNever() const { return edgeType() == VEdgeType::ET_NEVER; }
|
||||
};
|
||||
class AstSenTree final : public AstNode {
|
||||
@@ -1751,7 +1771,6 @@ class AstVar final : public AstNode {
|
||||
VDirection m_declDirection; // Declared direction input/output etc
|
||||
VLifetime m_lifetime; // Lifetime
|
||||
VVarAttrClocker m_attrClocker;
|
||||
MTaskIdSet m_mtaskIds; // MTaskID's that read or write this var
|
||||
int m_pinNum = 0; // For XML, if non-zero the connection pin number
|
||||
bool m_ansi : 1; // Params or pins declared in the module header, rather than the body
|
||||
bool m_declTyped : 1; // Declared as type (for dedup check)
|
||||
@@ -1794,6 +1813,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
|
||||
|
||||
@@ -1839,6 +1859,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;
|
||||
@@ -1994,6 +2015,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; }
|
||||
@@ -2105,9 +2128,6 @@ public:
|
||||
m_name = name;
|
||||
}
|
||||
static AstVar* scVarRecurse(AstNode* nodep);
|
||||
void addProducingMTaskId(int id) { m_mtaskIds.insert(id); }
|
||||
void addConsumingMTaskId(int id) { m_mtaskIds.insert(id); }
|
||||
const MTaskIdSet& mtaskIds() const { return m_mtaskIds; }
|
||||
void pinNum(int id) { m_pinNum = id; }
|
||||
int pinNum() const { return m_pinNum; }
|
||||
};
|
||||
@@ -2285,6 +2305,7 @@ class AstClass final : public AstNodeModule {
|
||||
// @astgen op4 := extendsp : List[AstClassExtends]
|
||||
// MEMBERS
|
||||
// @astgen ptr := m_classOrPackagep : Optional[AstClassPackage] // Package to be emitted with
|
||||
VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends)
|
||||
bool m_extended = false; // Is extension or extended by other classes
|
||||
bool m_interfaceClass = false; // Interface class
|
||||
bool m_needRNG = false; // Need RNG, uses srandom/randomize
|
||||
@@ -2320,6 +2341,8 @@ public:
|
||||
// Return true if this class is an extension of base class (SLOW)
|
||||
// Accepts nullptrs
|
||||
static bool isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp);
|
||||
void baseOverride(const VBaseOverride& flag) { m_baseOverride = flag; }
|
||||
VBaseOverride baseOverride() const { return m_baseOverride; }
|
||||
// Return the lowest class extended from, or this class
|
||||
AstClass* baseMostClassp();
|
||||
static bool isCacheableChild(const AstNode* nodep);
|
||||
@@ -2419,13 +2442,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 {
|
||||
@@ -2565,6 +2586,31 @@ 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 := levelp : AstNodeExpr
|
||||
// @astgen op3 := itemsp : List[AstNodeExpr]
|
||||
// Type of assertcontrol task; either known from parser or from evaluated
|
||||
// controlTypep expression.
|
||||
VAssertCtlType m_ctlType; // $assert keyword type
|
||||
|
||||
public:
|
||||
AstAssertCtl(FileLine* fl, VAssertCtlType ctlType, AstNodeExpr* levelp = nullptr,
|
||||
AstNodeExpr* itemsp = nullptr);
|
||||
AstAssertCtl(FileLine* fl, AstNodeExpr* controlTypep, AstNodeExpr* assertionTypep = 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}; }
|
||||
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)
|
||||
|
||||
+171
-38
@@ -17,11 +17,11 @@
|
||||
#include "V3PchAstMT.h"
|
||||
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3ExecGraph.h"
|
||||
#include "V3File.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Graph.h"
|
||||
#include "V3Hasher.h"
|
||||
#include "V3PartitionGraph.h" // Just for mtask dumping
|
||||
#include "V3String.h"
|
||||
|
||||
#include "V3Ast__gen_impl.h" // Generated by 'astgen'
|
||||
@@ -319,7 +319,7 @@ AstNodeExpr* AstInsideRange::newAndFromInside(AstNodeExpr* exprp, AstNodeExpr* l
|
||||
AstNodeExpr* const bp = new AstLte{fileline(), exprp->cloneTreePure(true), rhsp};
|
||||
ap->fileline()->modifyWarnOff(V3ErrorCode::UNSIGNED, true);
|
||||
bp->fileline()->modifyWarnOff(V3ErrorCode::CMPCONST, true);
|
||||
return new AstAnd{fileline(), ap, bp};
|
||||
return new AstLogAnd{fileline(), ap, bp};
|
||||
}
|
||||
|
||||
AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) {
|
||||
@@ -738,49 +738,82 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef) const {
|
||||
const CTypeRecursed info = cTypeRecurse(false);
|
||||
string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef, bool packed) const {
|
||||
const CTypeRecursed info = cTypeRecurse(false, packed);
|
||||
return info.render(name, isRef);
|
||||
}
|
||||
|
||||
AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
|
||||
AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packed) const {
|
||||
// Legacy compound argument currently just passed through and unused
|
||||
CTypeRecursed info;
|
||||
|
||||
const AstNodeDType* const dtypep = this->skipRefp();
|
||||
if (const auto* const adtypep = VN_CAST(dtypep, AssocArrayDType)) {
|
||||
const CTypeRecursed key = adtypep->keyDTypep()->cTypeRecurse(true);
|
||||
const CTypeRecursed val = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
const CTypeRecursed key = adtypep->keyDTypep()->cTypeRecurse(true, false);
|
||||
const CTypeRecursed val = adtypep->subDTypep()->cTypeRecurse(true, false);
|
||||
info.m_type = "VlAssocArray<" + key.m_type + ", " + val.m_type + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, CDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
info.m_type = adtypep->name();
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, WildcardArrayDType)) {
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true, false);
|
||||
info.m_type = "VlAssocArray<std::string, " + sub.m_type + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, DynArrayDType)) {
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true, false);
|
||||
info.m_type = "VlQueue<" + sub.m_type + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, QueueDType)) {
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true, false);
|
||||
info.m_type = "VlQueue<" + sub.m_type;
|
||||
// + 1 below as VlQueue uses 0 to mean unlimited, 1 to mean size() max is 1
|
||||
if (adtypep->boundp()) info.m_type += ", " + cvtToStr(adtypep->boundConst() + 1);
|
||||
info.m_type += ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, SampleQueueDType)) {
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true, false);
|
||||
info.m_type = "VlSampleQueue<" + sub.m_type + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, ClassRefDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
info.m_type = "VlClassRef<" + EmitCBase::prefixNameProtect(adtypep) + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, IfaceRefDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
info.m_type = EmitCBase::prefixNameProtect(adtypep->ifaceViaCellp()) + "*";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
if (adtypep->isCompound()) compound = true;
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(compound);
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(compound, false);
|
||||
info.m_type = "VlUnpacked<" + sub.m_type;
|
||||
info.m_type += ", " + cvtToStr(adtypep->declRange().elements());
|
||||
info.m_type += ">";
|
||||
} else if (VN_IS(dtypep, NodeUOrStructDType) && !VN_AS(dtypep, NodeUOrStructDType)->packed()) {
|
||||
const auto* const sdtypep = VN_AS(dtypep, NodeUOrStructDType);
|
||||
} 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);
|
||||
info.m_type = std::move(sub.m_type);
|
||||
info.m_dims = "[" + cvtToStr(adtypep->elementsConst()) + "]" + sub.m_dims;
|
||||
} else if (VN_IS(dtypep, NodeUOrStructDType)
|
||||
&& (!VN_AS(dtypep, NodeUOrStructDType)->packed() || packed)) {
|
||||
const AstNodeUOrStructDType* const sdtypep = VN_AS(dtypep, NodeUOrStructDType);
|
||||
UASSERT_OBJ(!packed || sdtypep->packed(), this,
|
||||
"Unsupported type for packed struct or union");
|
||||
info.m_type = EmitCBase::prefixNameProtect(sdtypep);
|
||||
} else if (const AstBasicDType* const bdtypep = dtypep->basicp()) {
|
||||
// We don't print msb()/lsb() as multidim packed would require recursion,
|
||||
@@ -810,6 +843,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
|
||||
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
|
||||
@@ -823,6 +858,13 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
|
||||
} else if (dtypep->isWide()) {
|
||||
info.m_type = "VlWide<" + cvtToStr(dtypep->widthWords()) + ">" + bitvec;
|
||||
}
|
||||
// CData, SData, IData, QData or VlWide are packed type.
|
||||
const bool packedType = VString::startsWith(info.m_type, "CData")
|
||||
|| VString::startsWith(info.m_type, "SData")
|
||||
|| VString::startsWith(info.m_type, "IData")
|
||||
|| VString::startsWith(info.m_type, "QData")
|
||||
|| VString::startsWith(info.m_type, "VlWide");
|
||||
UASSERT_OBJ(!packed || packedType, this, "Unsupported type for packed struct or union");
|
||||
} else {
|
||||
v3fatalSrc("Unknown data type in var type emitter: " << dtypep->prettyName());
|
||||
}
|
||||
@@ -861,9 +903,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++;
|
||||
@@ -1418,7 +1461,32 @@ void AstAlways::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStr(str, "keyword", keyword().ascii());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
|
||||
AstAssertCtl::AstAssertCtl(FileLine* fl, VAssertCtlType ctlType, AstNodeExpr* levelp,
|
||||
AstNodeExpr* itemsp)
|
||||
: ASTGEN_SUPER_AssertCtl(fl)
|
||||
, m_ctlType{ctlType} {
|
||||
controlTypep(new AstConst{fl, ctlType});
|
||||
if (!levelp) levelp = new AstConst{fl, 0};
|
||||
this->levelp(levelp);
|
||||
addItemsp(itemsp);
|
||||
}
|
||||
AstAssertCtl::AstAssertCtl(FileLine* fl, AstNodeExpr* controlTypep, AstNodeExpr*, AstNodeExpr*,
|
||||
AstNodeExpr* levelp, AstNodeExpr* itemsp)
|
||||
: ASTGEN_SUPER_AssertCtl(fl)
|
||||
, m_ctlType{VAssertCtlType::_TO_BE_EVALUATED} {
|
||||
this->controlTypep(controlTypep);
|
||||
if (!levelp) levelp = new AstConst{fl, 0};
|
||||
this->levelp(levelp);
|
||||
addItemsp(itemsp);
|
||||
}
|
||||
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() << "]";
|
||||
@@ -1439,7 +1507,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) {
|
||||
@@ -1479,12 +1547,20 @@ void AstCell::dumpJson(std::ostream& str) const {
|
||||
void AstCellInline::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " -> " << origModName();
|
||||
str << " [scopep=" << nodeAddr(scopep()) << "]";
|
||||
}
|
||||
void AstCellInline::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStrFunc(str, origModName);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstCellInlineScope::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " -> " << origModName();
|
||||
str << " [scopep=" << nodeAddr(scopep()) << "]";
|
||||
}
|
||||
void AstCellInlineScope::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStrFunc(str, origModName);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
bool AstClass::isCacheableChild(const AstNode* nodep) {
|
||||
return (VN_IS(nodep, Var) || VN_IS(nodep, Constraint) || VN_IS(nodep, EnumItemRef)
|
||||
|| (VN_IS(nodep, NodeFTask) && !VN_AS(nodep, NodeFTask)->isExternProto())
|
||||
@@ -1515,6 +1591,7 @@ void AstClass::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFunc(str, isExtended);
|
||||
dumpJsonBoolFunc(str, isInterfaceClass);
|
||||
dumpJsonBoolFunc(str, isVirtual);
|
||||
if (baseOverride().isAny()) dumpJsonStr(str, "baseOverride", baseOverride().ascii());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstClassExtends::dump(std::ostream& str) const {
|
||||
@@ -1555,6 +1632,7 @@ 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);
|
||||
@@ -1591,6 +1669,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 << " -> ";
|
||||
@@ -1699,7 +1793,7 @@ void AstJumpGo::dump(std::ostream& str) const {
|
||||
if (labelp()) {
|
||||
labelp()->dump(str);
|
||||
} else {
|
||||
str << "%Error:UNLINKED";
|
||||
str << "%E:UNLINKED";
|
||||
}
|
||||
}
|
||||
void AstJumpGo::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
@@ -1714,7 +1808,7 @@ void AstJumpLabel::dump(std::ostream& str) const {
|
||||
if (blockp()) {
|
||||
blockp()->dump(str);
|
||||
} else {
|
||||
str << "%Error:UNLINKED";
|
||||
str << "%E:UNLINKED";
|
||||
}
|
||||
}
|
||||
void AstJumpLabel::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
@@ -1728,7 +1822,8 @@ AstNodeUOrStructDType* AstMemberDType::getChildStructp() const {
|
||||
while (AstNodeArrayDType* const asubdtp = VN_CAST(subdtp, NodeArrayDType)) {
|
||||
subdtp = asubdtp->subDTypep();
|
||||
}
|
||||
return VN_CAST(subdtp, NodeUOrStructDType); // Maybe nullptr
|
||||
// It's possible that `subdtp` is still a ref type, so skip it.
|
||||
return VN_CAST(subdtp->skipRefp(), NodeUOrStructDType); // Maybe nullptr
|
||||
}
|
||||
|
||||
bool AstMemberSel::same(const AstNode* samep) const {
|
||||
@@ -1743,7 +1838,7 @@ void AstMemberSel::dump(std::ostream& str) const {
|
||||
if (varp()) {
|
||||
varp()->dump(str);
|
||||
} else {
|
||||
str << "%Error:UNLINKED";
|
||||
str << "%E:UNLINKED";
|
||||
}
|
||||
}
|
||||
void AstMemberSel::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
@@ -1904,6 +1999,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]";
|
||||
@@ -1916,7 +2023,7 @@ void AstNodeDType::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFunc(str, generic);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstNodeDType::dumpSmall(std::ostream& str) const {
|
||||
void AstNodeDType::dumpSmall(std::ostream& str) const VL_MT_STABLE {
|
||||
str << "(" << (generic() ? "G/" : "") << ((isSigned() && !isDouble()) ? "s" : "")
|
||||
<< (isNosign() ? "n" : "") << (isDouble() ? "d" : "") << (isString() ? "str" : "");
|
||||
if (!isDouble() && !isString()) str << "w" << (widthSized() ? "" : "u") << width();
|
||||
@@ -1943,13 +2050,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
|
||||
@@ -1959,7 +2066,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() {
|
||||
@@ -2063,7 +2170,7 @@ void AstSliceSel::dump(std::ostream& str) const {
|
||||
if (declRange().ranged()) str << " decl" << declRange();
|
||||
}
|
||||
void AstSliceSel::dumpJson(std::ostream& str) const {
|
||||
if (declRange().ranged()) { dumpJsonStr(str, "declRange", cvtToStr(declRange())); }
|
||||
if (declRange().ranged()) dumpJsonStr(str, "declRange", cvtToStr(declRange()));
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstMTaskBody::dump(std::ostream& str) const {
|
||||
@@ -2103,20 +2210,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 + "]";
|
||||
}
|
||||
@@ -2424,6 +2533,7 @@ void AstNodeFTask::dump(std::ostream& str) const {
|
||||
if (dpiOpenChild()) str << " [DPIOPENCHILD]";
|
||||
if (dpiOpenParent()) str << " [DPIOPENPARENT]";
|
||||
if (prototype()) str << " [PROTOTYPE]";
|
||||
if (pureVirtual()) str << " [PUREVIRTUAL]";
|
||||
if (recursive()) str << " [RECURSIVE]";
|
||||
if (taskPublic()) str << " [PUBLIC]";
|
||||
if ((dpiImport() || dpiExport()) && cname() != name()) str << " [c=" << cname() << "]";
|
||||
@@ -2462,6 +2572,7 @@ void AstNodeFTask::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFunc(str, prototype);
|
||||
dumpJsonBoolFunc(str, recursive);
|
||||
dumpJsonBoolFunc(str, taskPublic);
|
||||
if (baseOverride().isAny()) dumpJsonStr(str, "baseOverride", baseOverride().ascii());
|
||||
dumpJsonStrFunc(str, cname);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
@@ -2517,7 +2628,7 @@ void AstCoverInc::dump(std::ostream& str) const {
|
||||
if (declp()) {
|
||||
declp()->dump(str);
|
||||
} else {
|
||||
str << "%Error:UNLINKED";
|
||||
str << "%E:UNLINKED";
|
||||
}
|
||||
}
|
||||
void AstCoverInc::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
@@ -2543,7 +2654,7 @@ void AstTraceInc::dump(std::ostream& str) const {
|
||||
if (declp()) {
|
||||
declp()->dump(str);
|
||||
} else {
|
||||
str << "%Error:UNLINKED";
|
||||
str << "%E:UNLINKED";
|
||||
}
|
||||
}
|
||||
void AstTraceInc::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
@@ -2647,6 +2758,7 @@ void AstCMethodHard::setPurity() {
|
||||
{"commit", false},
|
||||
{"delay", false},
|
||||
{"done", false},
|
||||
{"enqueue", false},
|
||||
{"erase", false},
|
||||
{"evaluate", false},
|
||||
{"evaluation", false},
|
||||
@@ -2659,6 +2771,7 @@ void AstCMethodHard::setPurity() {
|
||||
{"find_last_index", true},
|
||||
{"fire", false},
|
||||
{"first", false},
|
||||
{"hard", false},
|
||||
{"init", false},
|
||||
{"insert", false},
|
||||
{"inside", true},
|
||||
@@ -2698,7 +2811,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());
|
||||
@@ -2757,3 +2871,22 @@ void AstDelay::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFunc(str, isCycleDelay);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
const char* AstAnd::widthMismatch() const VL_MT_STABLE {
|
||||
BROKEN_RTN(lhsp()->widthMin() != rhsp()->widthMin());
|
||||
BROKEN_RTN(lhsp()->widthMin() != widthMin());
|
||||
return nullptr;
|
||||
}
|
||||
const char* AstOr::widthMismatch() const VL_MT_STABLE {
|
||||
BROKEN_RTN(lhsp()->widthMin() != rhsp()->widthMin());
|
||||
BROKEN_RTN(lhsp()->widthMin() != widthMin());
|
||||
return nullptr;
|
||||
}
|
||||
const char* AstXor::widthMismatch() const VL_MT_STABLE {
|
||||
BROKEN_RTN(lhsp()->widthMin() != rhsp()->widthMin());
|
||||
BROKEN_RTN(lhsp()->widthMin() != widthMin());
|
||||
return nullptr;
|
||||
}
|
||||
const char* AstNot::widthMismatch() const VL_MT_STABLE {
|
||||
BROKEN_RTN(lhsp()->widthMin() != widthMin());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -372,7 +372,11 @@ void V3Broken::selfTest() {
|
||||
// Exercise addNewed and deleted for coverage, as otherwise only used with VL_LEAK_CHECKS
|
||||
FileLine* const fl = new FileLine{FileLine::commandLineFilename()};
|
||||
const AstNode* const newp = new AstBegin{fl, "[EditWrapper]", nullptr};
|
||||
// Don't actually do it with VL_LEAK_CHECKS, when new/delete calls these.
|
||||
// Otherwise you call addNewed twice on the same address, which is an error.
|
||||
#ifndef VL_LEAK_CHECKS
|
||||
addNewed(newp);
|
||||
deleted(newp);
|
||||
#endif
|
||||
VL_DO_DANGLING(delete newp, newp);
|
||||
}
|
||||
|
||||
+10
-5
@@ -82,7 +82,7 @@ class CastVisitor final : public VNVisitor {
|
||||
}
|
||||
void ensureCast(AstNodeExpr* nodep) {
|
||||
if (castSize(nodep->backp()) != castSize(nodep) || !nodep->user1()) {
|
||||
insertCast(nodep, castSize(nodep->backp()));
|
||||
if (!nodep->isNull()) insertCast(nodep, castSize(nodep->backp()));
|
||||
}
|
||||
}
|
||||
void ensureLower32Cast(AstCCast* nodep) {
|
||||
@@ -121,14 +121,14 @@ class CastVisitor final : public VNVisitor {
|
||||
// already checked by V3Width and dtypep of a condition operator is a type of their
|
||||
// common base class, so both classes can be safely casted.
|
||||
const AstClassRefDType* const thenClassDtypep
|
||||
= VN_CAST(nodep->thenp()->dtypep(), ClassRefDType);
|
||||
= VN_CAST(nodep->thenp()->dtypep()->skipRefp(), ClassRefDType);
|
||||
const AstClassRefDType* const elseClassDtypep
|
||||
= VN_CAST(nodep->elsep()->dtypep(), ClassRefDType);
|
||||
= VN_CAST(nodep->elsep()->dtypep()->skipRefp(), ClassRefDType);
|
||||
const bool castRequired = thenClassDtypep && elseClassDtypep
|
||||
&& (thenClassDtypep->classp() != elseClassDtypep->classp());
|
||||
if (castRequired) {
|
||||
const AstClass* const commonBaseClassp
|
||||
= VN_AS(nodep->dtypep(), ClassRefDType)->classp();
|
||||
= VN_AS(nodep->dtypep()->skipRefp(), ClassRefDType)->classp();
|
||||
if (thenClassDtypep->classp() != commonBaseClassp) {
|
||||
AstNodeExpr* thenp = nodep->thenp()->unlinkFrBack();
|
||||
nodep->thenp(new AstCCast{thenp->fileline(), thenp, nodep});
|
||||
@@ -201,7 +201,7 @@ class CastVisitor final : public VNVisitor {
|
||||
// Constants are of unknown size if smaller than 33 bits, because
|
||||
// we're too lazy to wrap every constant in the universe in
|
||||
// ((IData)#).
|
||||
nodep->user1(nodep->isQuad() || nodep->isWide());
|
||||
nodep->user1(nodep->isQuad() || nodep->isWide() || nodep->isNull());
|
||||
}
|
||||
|
||||
// Null dereference protection
|
||||
@@ -212,6 +212,11 @@ class CastVisitor final : public VNVisitor {
|
||||
void visit(AstCMethodCall* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureNullChecked(nodep->fromp());
|
||||
nodep->user1(true);
|
||||
}
|
||||
void visit(AstCMethodHard* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(true);
|
||||
}
|
||||
void visit(AstMemberSel* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
|
||||
+43
-8
@@ -26,6 +26,9 @@
|
||||
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
#include <queue>
|
||||
#include <set>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
@@ -46,6 +49,11 @@ class ClassVisitor final : public VNVisitor {
|
||||
const AstNodeFTask* m_ftaskp = nullptr; // Current task
|
||||
std::vector<std::pair<AstNode*, AstScope*>> m_toScopeMoves;
|
||||
std::vector<std::pair<AstNode*, AstNodeModule*>> m_toPackageMoves;
|
||||
std::set<AstTypedef*> m_typedefps; // Contains all typedef nodes
|
||||
std::set<AstNodeUOrStructDType*> m_strDtypeps; // Contains all packed structs and unions
|
||||
// Contains all public packed structs and unions, using a queue to
|
||||
// mark embedded struct / union public by BFS
|
||||
std::queue<AstNodeUOrStructDType*> m_pubStrDtypeps;
|
||||
|
||||
// METHODS
|
||||
|
||||
@@ -150,7 +158,7 @@ class ClassVisitor final : public VNVisitor {
|
||||
// have a pointer to it yet
|
||||
m_toScopeMoves.emplace_back(nodep, m_packageScopep);
|
||||
}
|
||||
if (!m_ftaskp && nodep->lifetime().isStatic()) {
|
||||
if (!m_ftaskp && (nodep->lifetime().isStatic() || nodep->isParam())) {
|
||||
m_toPackageMoves.emplace_back(nodep, m_classPackagep);
|
||||
// We're really moving the VarScope but we might not
|
||||
// have a pointer to it yet
|
||||
@@ -190,12 +198,12 @@ class ClassVisitor final : public VNVisitor {
|
||||
void visit(AstInitial* nodep) override {
|
||||
// But not AstInitialAutomatic, which remains under the class
|
||||
iterateChildren(nodep);
|
||||
if (m_packageScopep) { m_toScopeMoves.emplace_back(nodep, m_packageScopep); }
|
||||
if (m_packageScopep) m_toScopeMoves.emplace_back(nodep, m_packageScopep);
|
||||
}
|
||||
void visit(AstInitialStatic* nodep) override {
|
||||
// But not AstInitialAutomatic, which remains under the class
|
||||
iterateChildren(nodep);
|
||||
if (m_packageScopep) { m_toScopeMoves.emplace_back(nodep, m_packageScopep); }
|
||||
if (m_packageScopep) m_toScopeMoves.emplace_back(nodep, m_packageScopep);
|
||||
}
|
||||
|
||||
void setStructModulep(AstNodeUOrStructDType* const dtypep) {
|
||||
@@ -204,22 +212,27 @@ class ClassVisitor final : public VNVisitor {
|
||||
dtypep->classOrPackagep(m_classPackagep ? m_classPackagep : m_modp);
|
||||
dtypep->name(
|
||||
m_names.get(dtypep->name() + (VN_IS(dtypep, UnionDType) ? "__union" : "__struct")));
|
||||
if (dtypep->packed()) m_strDtypeps.insert(dtypep);
|
||||
|
||||
for (const AstMemberDType* itemp = dtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
AstNodeUOrStructDType* const subp = itemp->getChildStructp();
|
||||
// Recurse only into anonymous unpacked structs inside this definition,
|
||||
// other unpacked structs will be reached from another typedefs
|
||||
if (subp && !subp->packed() && subp->name().empty()) setStructModulep(subp);
|
||||
// Recurse only into anonymous structs inside this definition,
|
||||
// other structs will be reached from another typedefs
|
||||
if (subp && subp->name().empty()) setStructModulep(subp);
|
||||
}
|
||||
}
|
||||
void visit(AstTypedef* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
AstNodeUOrStructDType* const dtypep = VN_CAST(nodep->dtypep(), NodeUOrStructDType);
|
||||
if (dtypep && dtypep->packed()) {
|
||||
m_typedefps.insert(nodep);
|
||||
if (nodep->attrPublic()) m_pubStrDtypeps.push(dtypep);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
if (m_classPackagep) m_classPackagep->addStmtsp(nodep->unlinkFrBack());
|
||||
|
||||
AstNodeUOrStructDType* const dtypep = VN_CAST(nodep->dtypep(), NodeUOrStructDType);
|
||||
if (dtypep && !dtypep->packed()) {
|
||||
if (dtypep) {
|
||||
dtypep->name(nodep->name());
|
||||
setStructModulep(dtypep);
|
||||
}
|
||||
@@ -258,6 +271,28 @@ public:
|
||||
nodep->unlinkFrBack();
|
||||
modp->addStmtsp(nodep);
|
||||
}
|
||||
// BFS to mark public typedefs.
|
||||
std::set<AstNodeUOrStructDType*> pubStrDtypeps;
|
||||
while (!m_pubStrDtypeps.empty()) {
|
||||
AstNodeUOrStructDType* const dtypep = m_pubStrDtypeps.front();
|
||||
m_pubStrDtypeps.pop();
|
||||
if (pubStrDtypeps.insert(dtypep).second) {
|
||||
for (const AstMemberDType* itemp = dtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
if (AstNodeUOrStructDType* const subp = itemp->getChildStructp())
|
||||
m_pubStrDtypeps.push(subp);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (AstTypedef* typedefp : m_typedefps) {
|
||||
AstNodeUOrStructDType* const sdtypep = VN_AS(typedefp->dtypep(), NodeUOrStructDType);
|
||||
if (pubStrDtypeps.count(sdtypep)) typedefp->attrPublic(true);
|
||||
}
|
||||
// Clear package pointer of non-public packed struct / union type, which will never be
|
||||
// exported.
|
||||
for (AstNodeUOrStructDType* sdtypep : m_strDtypeps) {
|
||||
if (!pubStrDtypeps.count(sdtypep)) sdtypep->classOrPackagep(nullptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -224,6 +224,11 @@ class CleanVisitor final : public VNVisitor {
|
||||
iterateChildren(nodep);
|
||||
setClean(nodep, true);
|
||||
}
|
||||
void visit(AstConsPackMember* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureClean(nodep->rhsp());
|
||||
setClean(nodep, true);
|
||||
}
|
||||
void visit(AstSel* nodep) override {
|
||||
operandTriop(nodep);
|
||||
setClean(nodep, nodep->cleanOut());
|
||||
|
||||
@@ -53,6 +53,7 @@ class ConvertWriteRefsToRead final : public VNVisitor {
|
||||
if (nodep->access().isWriteOnly()) {
|
||||
nodep->replaceWith(
|
||||
new AstVarRef{nodep->fileline(), nodep->varScopep(), VAccess::READ});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +67,9 @@ public:
|
||||
// Clock state, as a visitor of each AstNode
|
||||
|
||||
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
|
||||
|
||||
+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 = ", ";
|
||||
|
||||
+1
-1
@@ -286,7 +286,7 @@ public:
|
||||
}
|
||||
void update(const V3ConfigFile& file) {
|
||||
// Copy in all Attributes
|
||||
for (const auto& itr : file.m_lineAttrs) { m_lineAttrs[itr.first] |= itr.second; }
|
||||
for (const auto& itr : file.m_lineAttrs) m_lineAttrs[itr.first] |= itr.second;
|
||||
// Copy in all ignores
|
||||
for (const auto& ignLine : file.m_ignLines) m_ignLines.insert(ignLine);
|
||||
// Update the iterator after the list has changed
|
||||
|
||||
+83
-29
@@ -684,7 +684,7 @@ public:
|
||||
// Reduction ops are transformed in the same way.
|
||||
// &{v[0], v[1]} => 2'b11 == (2'b11 & v)
|
||||
static AstNodeExpr* simplify(AstNodeExpr* nodep, int resultWidth, unsigned externalOps,
|
||||
VDouble0& reduction, VNDeleter& deleterr) {
|
||||
VDouble0& reduction) {
|
||||
UASSERT_OBJ(1 <= resultWidth && resultWidth <= 64, nodep, "resultWidth out of range");
|
||||
|
||||
// Walk tree, gathering all terms referenced in expression
|
||||
@@ -727,7 +727,7 @@ public:
|
||||
}
|
||||
// Set width and widthMin precisely
|
||||
resultp->dtypeChgWidth(resultWidth, 1);
|
||||
for (AstNode* const termp : termps) deleterr.pushDeletep(termp);
|
||||
for (AstNode* const termp : termps) VL_DO_DANGLING(termp->deleteTree(), termp);
|
||||
return resultp;
|
||||
}
|
||||
const ResultTerm result = v->getResultTerm();
|
||||
@@ -803,7 +803,7 @@ public:
|
||||
|
||||
// Only substitute the result if beneficial as determined by operation count
|
||||
if (visitor.m_ops <= resultOps) {
|
||||
for (AstNode* const termp : termps) deleterr.pushDeletep(termp);
|
||||
for (AstNode* const termp : termps) VL_DO_DANGLING(termp->deleteTree(), termp);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1187,11 +1187,9 @@ class ConstVisitor final : public VNVisitor {
|
||||
const AstAnd* const andp = VN_CAST(nodep, And);
|
||||
const int width = nodep->width();
|
||||
if (andp && isConst(andp->lhsp(), 1)) { // 1 & BitOpTree
|
||||
newp = ConstBitOpTreeVisitor::simplify(andp->rhsp(), width, 1, m_statBitOpReduction,
|
||||
deleter());
|
||||
newp = ConstBitOpTreeVisitor::simplify(andp->rhsp(), width, 1, m_statBitOpReduction);
|
||||
} else { // BitOpTree
|
||||
newp = ConstBitOpTreeVisitor::simplify(nodep, width, 0, m_statBitOpReduction,
|
||||
deleter());
|
||||
newp = ConstBitOpTreeVisitor::simplify(nodep, width, 0, m_statBitOpReduction);
|
||||
}
|
||||
|
||||
if (newp) {
|
||||
@@ -1345,7 +1343,8 @@ class ConstVisitor final : public VNVisitor {
|
||||
= new AstSel{nodep->fileline(), ap->unlinkFrBack(), newLsb, nodep->widthConst()};
|
||||
newp->dtypeFrom(nodep);
|
||||
if (debug() >= 9) newp->dumpTree("- SEL(SH)-ou: ");
|
||||
VL_DO_DANGLING(nodep->replaceWith(newp), nodep);
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1557,7 +1556,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.
|
||||
@@ -1822,16 +1823,20 @@ class ConstVisitor final : public VNVisitor {
|
||||
// {llp OP lrp, rlp OP rrp} => {llp, rlp} OP {lrp, rrp}, where OP = AND/OR/XOR
|
||||
AstNodeBiop* const lp = VN_AS(nodep->lhsp(), NodeBiop);
|
||||
AstNodeBiop* const rp = VN_AS(nodep->rhsp(), NodeBiop);
|
||||
AstNodeExpr* const llp = lp->lhsp()->cloneTreePure(false);
|
||||
AstNodeExpr* const lrp = lp->rhsp()->cloneTreePure(false);
|
||||
AstNodeExpr* const rlp = rp->lhsp()->cloneTreePure(false);
|
||||
AstNodeExpr* const rrp = rp->rhsp()->cloneTreePure(false);
|
||||
if (concatMergeable(lp, rp, 0)) {
|
||||
AstConcat* const newlp = new AstConcat{rlp->fileline(), llp, rlp};
|
||||
AstConcat* const newrp = new AstConcat{rrp->fileline(), lrp, rrp};
|
||||
AstNodeExpr* const llp = lp->lhsp();
|
||||
AstNodeExpr* const lrp = lp->rhsp();
|
||||
AstNodeExpr* const rlp = rp->lhsp();
|
||||
AstNodeExpr* const rrp = rp->rhsp();
|
||||
AstConcat* const newlp = new AstConcat{rlp->fileline(), llp->cloneTreePure(false),
|
||||
rlp->cloneTreePure(false)};
|
||||
AstConcat* const newrp = new AstConcat{rrp->fileline(), lrp->cloneTreePure(false),
|
||||
rrp->cloneTreePure(false)};
|
||||
// use the lhs to replace the parent concat
|
||||
lp->lhsp()->replaceWith(newlp);
|
||||
lp->rhsp()->replaceWith(newrp);
|
||||
llp->replaceWith(newlp);
|
||||
VL_DO_DANGLING(pushDeletep(llp), llp);
|
||||
lrp->replaceWith(newrp);
|
||||
VL_DO_DANGLING(pushDeletep(lrp), lrp);
|
||||
lp->dtypeChgWidthSigned(newlp->width(), newlp->width(), VSigning::UNSIGNED);
|
||||
UINFO(5, "merged " << nodep << endl);
|
||||
VL_DO_DANGLING(pushDeletep(rp->unlinkFrBack()), rp);
|
||||
@@ -2170,6 +2175,21 @@ class ConstVisitor final : public VNVisitor {
|
||||
"array to a variable of size greater than 64");
|
||||
}
|
||||
srcp = new AstCvtDynArrayToPacked{srcp->fileline(), srcp, srcDTypep};
|
||||
} 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()};
|
||||
// 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
|
||||
= 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};
|
||||
}
|
||||
nodep->rhsp(srcp);
|
||||
VL_DO_DANGLING(pushDeletep(streamp), streamp);
|
||||
@@ -2190,10 +2210,17 @@ class ConstVisitor final : public VNVisitor {
|
||||
} else {
|
||||
streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(), VSigning::UNSIGNED);
|
||||
}
|
||||
if (dWidth == 0) {
|
||||
streamp = new AstCvtPackedToDynArray{nodep->fileline(), streamp, dstp->dtypep()};
|
||||
} else if (sWidth > dWidth) {
|
||||
streamp = new AstSel{streamp->fileline(), streamp, sWidth - dWidth, dWidth};
|
||||
if (VN_IS(dstp->dtypep(), UnpackArrayDType)) {
|
||||
streamp
|
||||
= new AstCvtPackedToUnpackArray{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()};
|
||||
} else if (sWidth >= dWidth) {
|
||||
streamp = new AstSel{streamp->fileline(), streamp, sWidth - dWidth, dWidth};
|
||||
}
|
||||
}
|
||||
nodep->lhsp(dstp);
|
||||
nodep->rhsp(streamp);
|
||||
@@ -2207,10 +2234,24 @@ class ConstVisitor final : public VNVisitor {
|
||||
AstNodeExpr* srcp = nodep->rhsp()->unlinkFrBack();
|
||||
const int sWidth = srcp->width();
|
||||
const int dWidth = dstp->width();
|
||||
if (dWidth == 0) {
|
||||
srcp = new AstCvtPackedToDynArray{nodep->fileline(), srcp, dstp->dtypep()};
|
||||
} else if (sWidth > dWidth) {
|
||||
srcp = new AstSel{streamp->fileline(), srcp, sWidth - dWidth, dWidth};
|
||||
if (VN_IS(dstp->dtypep(), UnpackArrayDType)) {
|
||||
const int dstBitWidth
|
||||
= dWidth * VN_AS(dstp->dtypep(), UnpackArrayDType)->arrayUnpackedElements();
|
||||
// Handling the case where rhs is wider than lhs. StreamL does not require this
|
||||
// since the combination of the left streaming operation and the implicit
|
||||
// truncation in VL_ASSIGN_UNPACK automatically selects the left-most bits.
|
||||
if (sWidth > dstBitWidth) {
|
||||
srcp
|
||||
= new AstSel{streamp->fileline(), srcp, sWidth - dstBitWidth, dstBitWidth};
|
||||
}
|
||||
srcp = new AstCvtPackedToUnpackArray{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()};
|
||||
} else if (sWidth >= dWidth) {
|
||||
srcp = new AstSel{streamp->fileline(), srcp, sWidth - dWidth, dWidth};
|
||||
}
|
||||
}
|
||||
nodep->lhsp(dstp);
|
||||
nodep->rhsp(srcp);
|
||||
@@ -2230,6 +2271,14 @@ class ConstVisitor final : public VNVisitor {
|
||||
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});
|
||||
streamp->dtypeFrom(lhsDtypep);
|
||||
}
|
||||
} else if (m_doV && replaceAssignMultiSel(nodep)) {
|
||||
return true;
|
||||
@@ -3024,8 +3073,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
|
||||
@@ -3300,6 +3350,9 @@ class ConstVisitor final : public VNVisitor {
|
||||
if (nodep->precondsp()) {
|
||||
nodep->replaceWith(nodep->precondsp());
|
||||
} else {
|
||||
nodep->v3warn(UNUSEDLOOP,
|
||||
"Loop condition is always false; body will never execute");
|
||||
nodep->fileline()->modifyWarnOff(V3ErrorCode::UNUSEDLOOP, true);
|
||||
nodep->unlinkFrBack();
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
@@ -3492,7 +3545,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
|
||||
@@ -3686,7 +3739,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
|
||||
|
||||
+679
-433
File diff suppressed because it is too large
Load Diff
+1
-2
@@ -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);
|
||||
|
||||
+22
-20
@@ -38,20 +38,21 @@ void DfgGraph::addGraph(DfgGraph& other) {
|
||||
m_size += other.m_size;
|
||||
other.m_size = 0;
|
||||
|
||||
const auto moveVertexList = [this](V3List<DfgVertex*>& src, V3List<DfgVertex*>& dst) {
|
||||
if (DfgVertex* vtxp = src.begin()) {
|
||||
vtxp->m_verticesEnt.moveAppend(src, dst, vtxp);
|
||||
do {
|
||||
vtxp->m_userCnt = 0;
|
||||
vtxp->m_graphp = this;
|
||||
vtxp = vtxp->verticesNext();
|
||||
} while (vtxp);
|
||||
}
|
||||
};
|
||||
|
||||
moveVertexList(other.m_varVertices, m_varVertices);
|
||||
moveVertexList(other.m_constVertices, m_constVertices);
|
||||
moveVertexList(other.m_opVertices, m_opVertices);
|
||||
for (DfgVertexVar& vtx : other.m_varVertices) {
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = this;
|
||||
}
|
||||
m_varVertices.splice(m_varVertices.end(), other.m_varVertices);
|
||||
for (DfgConst& vtx : other.m_constVertices) {
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = this;
|
||||
}
|
||||
m_constVertices.splice(m_constVertices.end(), other.m_constVertices);
|
||||
for (DfgVertex& vtx : other.m_opVertices) {
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = this;
|
||||
}
|
||||
m_opVertices.splice(m_opVertices.end(), other.m_opVertices);
|
||||
}
|
||||
|
||||
static const string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&vtx) + '"'; }
|
||||
@@ -74,6 +75,8 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
||||
} else if (varVtxp->hasExtRefs()) {
|
||||
os << ", shape=box, style=filled, fillcolor=firebrick2"; // Red
|
||||
} else if (varVtxp->hasModRefs()) {
|
||||
os << ", shape=box, style=filled, fillcolor=darkorange1"; // Orange
|
||||
} else if (varVtxp->hasDfgRefs()) {
|
||||
os << ", shape=box, style=filled, fillcolor=gold2"; // Yellow
|
||||
} else if (varVtxp->keep()) {
|
||||
os << ", shape=box, style=filled, fillcolor=grey";
|
||||
@@ -98,6 +101,8 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
||||
} else if (arrVtxp->hasExtRefs()) {
|
||||
os << ", shape=box3d, style=filled, fillcolor=firebrick2"; // Red
|
||||
} else if (arrVtxp->hasModRefs()) {
|
||||
os << ", shape=box3d, style=filled, fillcolor=darkorange1"; // Orange
|
||||
} else if (arrVtxp->hasDfgRefs()) {
|
||||
os << ", shape=box3d, style=filled, fillcolor=gold2"; // Yellow
|
||||
} else if (arrVtxp->keep()) {
|
||||
os << ", shape=box3d, style=filled, fillcolor=grey";
|
||||
@@ -229,7 +234,7 @@ static void dumpDotUpstreamConeFromVertex(std::ostream& os, const DfgVertex& vtx
|
||||
// Emit all DfgVarPacked vertices that have external references driven by this vertex
|
||||
vtx.forEachSink([&](const DfgVertex& dst) {
|
||||
if (const DfgVarPacked* const varVtxp = dst.cast<DfgVarPacked>()) {
|
||||
if (varVtxp->hasRefs()) dumpDotVertexAndSourceEdges(os, dst);
|
||||
if (varVtxp->hasNonLocalRefs()) dumpDotVertexAndSourceEdges(os, dst);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -263,7 +268,7 @@ void DfgGraph::dumpDotAllVarConesPrefixed(const string& label) const {
|
||||
// Check if this vertex drives a variable referenced outside the DFG.
|
||||
const DfgVarPacked* const sinkp
|
||||
= vtx.findSink<DfgVarPacked>([](const DfgVarPacked& sink) { //
|
||||
return sink.hasRefs();
|
||||
return sink.hasNonLocalRefs();
|
||||
});
|
||||
|
||||
// We only dump cones driving an externally referenced variable
|
||||
@@ -341,10 +346,7 @@ DfgVertex::DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType*
|
||||
dfg.addVertex(*this);
|
||||
}
|
||||
|
||||
DfgVertex::~DfgVertex() {
|
||||
// TODO: It would be best to intern these via AstTypeTable to save the effort
|
||||
if (VN_IS(m_dtypep, UnpackArrayDType)) VL_DO_DANGLING(delete m_dtypep, m_dtypep);
|
||||
}
|
||||
DfgVertex::~DfgVertex() {}
|
||||
|
||||
bool DfgVertex::selfEquals(const DfgVertex& that) const { return true; }
|
||||
|
||||
|
||||
+333
-354
@@ -55,6 +55,8 @@
|
||||
#endif
|
||||
|
||||
class DfgEdge;
|
||||
class DfgVertex;
|
||||
class DfgGraph;
|
||||
class DfgVisitor;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -89,140 +91,6 @@ constexpr bool operator==(VDfgType lhs, VDfgType::en rhs) { return lhs.m_e == rh
|
||||
constexpr bool operator==(VDfgType::en lhs, VDfgType rhs) { return lhs == rhs.m_e; }
|
||||
inline std::ostream& operator<<(std::ostream& os, const VDfgType& t) { return os << t.ascii(); }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Dataflow graph
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class DfgGraph final {
|
||||
friend class DfgVertex;
|
||||
|
||||
// TYPES
|
||||
|
||||
// RAII handle for DfgVertex user data
|
||||
class UserDataInUse final {
|
||||
DfgGraph* m_graphp; // The referenced graph
|
||||
|
||||
public:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
UserDataInUse(DfgGraph* graphp)
|
||||
: m_graphp{graphp} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
UserDataInUse(UserDataInUse&& that) {
|
||||
UASSERT(that.m_graphp, "Moving from empty");
|
||||
m_graphp = std::exchange(that.m_graphp, nullptr);
|
||||
}
|
||||
VL_UNCOPYABLE(UserDataInUse);
|
||||
UserDataInUse& operator=(UserDataInUse&& that) {
|
||||
UASSERT(that.m_graphp, "Moving from empty");
|
||||
m_graphp = std::exchange(that.m_graphp, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~UserDataInUse() {
|
||||
if (m_graphp) m_graphp->m_userCurrent = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// MEMBERS
|
||||
|
||||
// Variables and constants make up a significant proportion of vertices (40-50% was observed
|
||||
// in large designs), and they can often be treated specially in algorithms, which in turn
|
||||
// enables significant Verilation performance gains, so we keep these in separate lists for
|
||||
// direct access.
|
||||
V3List<DfgVertex*> m_varVertices; // The variable vertices in the graph
|
||||
V3List<DfgVertex*> m_constVertices; // The constant vertices in the graph
|
||||
V3List<DfgVertex*> m_opVertices; // The operation vertices in the graph
|
||||
|
||||
size_t m_size = 0; // Number of vertices in the graph
|
||||
uint32_t m_userCurrent = 0; // Vertex user data generation number currently in use
|
||||
uint32_t m_userCnt = 0; // Vertex user data generation counter
|
||||
// Parent of the graph (i.e.: the module containing the logic represented by this graph).
|
||||
AstModule* const m_modulep;
|
||||
const string m_name; // Name of graph (for debugging)
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
explicit DfgGraph(AstModule& module, const string& name = "") VL_MT_DISABLED;
|
||||
~DfgGraph() VL_MT_DISABLED;
|
||||
VL_UNCOPYABLE(DfgGraph);
|
||||
|
||||
// METHODS
|
||||
public:
|
||||
// Add DfgVertex to this graph (assumes not yet contained).
|
||||
inline void addVertex(DfgVertex& vtx);
|
||||
// Remove DfgVertex form this graph (assumes it is contained).
|
||||
inline void removeVertex(DfgVertex& vtx);
|
||||
// Number of vertices in this graph
|
||||
size_t size() const { return m_size; }
|
||||
// Parent module
|
||||
AstModule* modulep() const { return m_modulep; }
|
||||
// Name of this graph
|
||||
const string& name() const { return m_name; }
|
||||
|
||||
// Reset Vertex user data
|
||||
UserDataInUse userDataInUse() {
|
||||
UASSERT(!m_userCurrent, "Conflicting use of DfgVertex user data");
|
||||
++m_userCnt;
|
||||
UASSERT(m_userCnt, "'m_userCnt' overflow");
|
||||
m_userCurrent = m_userCnt;
|
||||
return UserDataInUse{this};
|
||||
}
|
||||
|
||||
// Access to vertex lists for faster iteration in important contexts
|
||||
inline DfgVertexVar* varVerticesBeginp() const;
|
||||
inline DfgVertexVar* varVerticesRbeginp() const;
|
||||
inline DfgConst* constVerticesBeginp() const;
|
||||
inline DfgConst* constVerticesRbeginp() const;
|
||||
inline DfgVertex* opVerticesBeginp() const;
|
||||
inline DfgVertex* opVerticesRbeginp() const;
|
||||
|
||||
// Calls given function 'f' for each vertex in the graph. It is safe to manipulate any vertices
|
||||
// in the graph, or to delete/unlink the vertex passed to 'f' during iteration. It is however
|
||||
// not safe to delete/unlink any vertex in the same graph other than the one passed to 'f'.
|
||||
inline void forEachVertex(std::function<void(DfgVertex&)> f);
|
||||
|
||||
// 'const' variant of 'forEachVertex'. No mutation allowed.
|
||||
inline void forEachVertex(std::function<void(const DfgVertex&)> f) const;
|
||||
|
||||
// Add contents of other graph to this graph. Leaves other graph empty.
|
||||
void addGraph(DfgGraph& other) VL_MT_DISABLED;
|
||||
|
||||
// Split this graph into individual components (unique sub-graphs with no edges between them).
|
||||
// Also removes any vertices that are not weakly connected to any variable.
|
||||
// Leaves 'this' graph empty.
|
||||
std::vector<std::unique_ptr<DfgGraph>> splitIntoComponents(std::string label) VL_MT_DISABLED;
|
||||
|
||||
// Extract cyclic sub-graphs from 'this' graph. Cyclic sub-graphs are those that contain at
|
||||
// least one strongly connected component (SCC) plus any other vertices that feed or sink from
|
||||
// the SCCs, up to a variable boundary. This means that the returned graphs are guaranteed to
|
||||
// be cyclic, but they are not guaranteed to be strongly connected (however, they are always
|
||||
// at least weakly connected). Trivial SCCs that are acyclic (i.e.: vertices that are not part
|
||||
// of a cycle) are left in 'this' graph. This means that at the end 'this' graph is guaranteed
|
||||
// to be a DAG (acyclic). 'this' will not necessarily be a connected graph at the end, even if
|
||||
// it was originally connected.
|
||||
std::vector<std::unique_ptr<DfgGraph>>
|
||||
extractCyclicComponents(std::string label) VL_MT_DISABLED;
|
||||
|
||||
// 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
|
||||
// the name of the graph which is included in the output.
|
||||
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,
|
||||
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
|
||||
// included in the file names and the output. This is useful for very large graphs that are
|
||||
// otherwise difficult to browse visually due to their size.
|
||||
void dumpDotAllVarConesPrefixed(const string& label = "") const VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Dataflow graph edge
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -264,7 +132,8 @@ class DfgVertex VL_NOT_FINAL {
|
||||
using UserDataStorage = void*; // Storage allocated for user data
|
||||
|
||||
// STATE
|
||||
V3ListEnt<DfgVertex*> m_verticesEnt; // V3List handle of this vertex, kept under the DfgGraph
|
||||
V3ListLinks<DfgVertex> m_links; // V3List links
|
||||
|
||||
protected:
|
||||
DfgEdge* m_sinksp = nullptr; // List of sinks of this vertex
|
||||
FileLine* const m_filelinep; // Source location
|
||||
@@ -280,6 +149,14 @@ protected:
|
||||
public:
|
||||
virtual ~DfgVertex() VL_MT_DISABLED;
|
||||
|
||||
private:
|
||||
V3ListLinks<DfgVertex>& links() { return m_links; }
|
||||
|
||||
public:
|
||||
// List type that can store Vertex (which must be a DfgVertex) instances via m_links
|
||||
template <typename Vertex>
|
||||
using List = V3List<DfgVertex, &DfgVertex::links, Vertex>;
|
||||
|
||||
// METHODS
|
||||
private:
|
||||
// Visitor accept method
|
||||
@@ -330,10 +207,11 @@ public:
|
||||
UDEBUGONLY(UASSERT_OBJ(isSupportedDType(nodep->dtypep()), nodep, "Unsupported dtype"););
|
||||
// For simplicity, all packed types are represented with a fixed type
|
||||
if (AstUnpackArrayDType* const typep = VN_CAST(nodep->dtypep(), UnpackArrayDType)) {
|
||||
// TODO: these need interning via AstTypeTable otherwise they leak
|
||||
return new AstUnpackArrayDType{typep->fileline(),
|
||||
dtypeForWidth(typep->subDTypep()->width()),
|
||||
typep->rangep()->cloneTree(false)};
|
||||
AstNodeDType* const dtypep = new AstUnpackArrayDType{
|
||||
typep->fileline(), dtypeForWidth(typep->subDTypep()->width()),
|
||||
typep->rangep()->cloneTree(false)};
|
||||
v3Global.rootp()->typeTablep()->addTypesp(dtypep);
|
||||
return dtypep;
|
||||
}
|
||||
return dtypeForWidth(nodep->width());
|
||||
}
|
||||
@@ -348,54 +226,15 @@ public:
|
||||
|
||||
// Retrieve user data, constructing it fresh on first try.
|
||||
template <typename T>
|
||||
T& user() {
|
||||
static_assert(sizeof(T) <= sizeof(UserDataStorage),
|
||||
"Size of user data type 'T' is too large for allocated storage");
|
||||
static_assert(alignof(T) <= alignof(UserDataStorage),
|
||||
"Alignment of user data type 'T' is larger than allocated storage");
|
||||
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
|
||||
const uint32_t userCurrent = m_graphp->m_userCurrent;
|
||||
UDEBUGONLY(UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving"););
|
||||
if (m_userCnt != userCurrent) {
|
||||
m_userCnt = userCurrent;
|
||||
// cppcheck-has-bug-suppress uninitvar
|
||||
VL_ATTR_UNUSED T* const resultp = new (storagep) T{};
|
||||
UDEBUGONLY(UASSERT_OBJ(resultp == storagep, this, "Something is odd"););
|
||||
}
|
||||
return *storagep;
|
||||
}
|
||||
inline T& user();
|
||||
|
||||
// Retrieve user data, must be current.
|
||||
template <typename T>
|
||||
T& getUser() {
|
||||
static_assert(sizeof(T) <= sizeof(UserDataStorage),
|
||||
"Size of user data type 'T' is too large for allocated storage");
|
||||
static_assert(alignof(T) <= alignof(UserDataStorage),
|
||||
"Alignment of user data type 'T' is larger than allocated storage");
|
||||
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
|
||||
#if VL_DEBUG
|
||||
const uint32_t userCurrent = m_graphp->m_userCurrent;
|
||||
UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving");
|
||||
UASSERT_OBJ(m_userCnt == userCurrent, this, "DfgVertex user data is stale");
|
||||
#endif
|
||||
return *storagep;
|
||||
}
|
||||
inline T& getUser();
|
||||
|
||||
// Set user data, becomes current.
|
||||
template <typename T>
|
||||
typename std::enable_if<sizeof(T) <= sizeof(void*), void>::type setUser(T value) {
|
||||
static_assert(sizeof(T) <= sizeof(UserDataStorage),
|
||||
"Size of user data type 'T' is too large for allocated storage");
|
||||
static_assert(alignof(T) <= alignof(UserDataStorage),
|
||||
"Alignment of user data type 'T' is larger than allocated storage");
|
||||
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
|
||||
const uint32_t userCurrent = m_graphp->m_userCurrent;
|
||||
#if VL_DEBUG
|
||||
UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving");
|
||||
#endif
|
||||
m_userCnt = userCurrent;
|
||||
*storagep = value;
|
||||
}
|
||||
inline void setUser(T value);
|
||||
|
||||
// Width of result
|
||||
uint32_t width() const {
|
||||
@@ -448,10 +287,6 @@ public:
|
||||
// Relink all sinks to be driven from the given new source
|
||||
void replaceWith(DfgVertex* newSourcep) VL_MT_DISABLED;
|
||||
|
||||
// Access to vertex list for faster iteration in important contexts
|
||||
DfgVertex* verticesNext() const { return m_verticesEnt.nextp(); }
|
||||
DfgVertex* verticesPrev() const { return m_verticesEnt.prevp(); }
|
||||
|
||||
// Calls given function 'f' for each source vertex of this vertex
|
||||
// Unconnected source edges are not iterated.
|
||||
inline void forEachSource(std::function<void(DfgVertex&)> f) VL_MT_DISABLED;
|
||||
@@ -504,9 +339,6 @@ public:
|
||||
// Is this a DfgConst that is all ones
|
||||
inline bool isOnes() const VL_MT_DISABLED;
|
||||
|
||||
// Should this vertex be inlined when rendering to Ast, or be stored to a temporary
|
||||
inline bool inlined() const VL_MT_DISABLED;
|
||||
|
||||
// Methods that allow DfgVertex to participate in error reporting/messaging
|
||||
void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
|
||||
m_filelinep->v3errorEnd(str);
|
||||
@@ -569,9 +401,6 @@ public:
|
||||
virtual const string srcName(size_t idx) const = 0;
|
||||
};
|
||||
|
||||
// Specializations of privateTypeTest
|
||||
#include "V3Dfg__gen_type_tests.h" // From ./astgen
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Dfg vertex visitor
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -585,152 +414,6 @@ public:
|
||||
#include "V3Dfg__gen_visitor_decls.h" // From ./astgen
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Inline method definitions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void DfgGraph::addVertex(DfgVertex& vtx) {
|
||||
// Note: changes here need to be replicated in DfgGraph::addGraph
|
||||
++m_size;
|
||||
if (vtx.is<DfgConst>()) {
|
||||
vtx.m_verticesEnt.pushBack(m_constVertices, &vtx);
|
||||
} else if (vtx.is<DfgVertexVar>()) {
|
||||
vtx.m_verticesEnt.pushBack(m_varVertices, &vtx);
|
||||
} else {
|
||||
vtx.m_verticesEnt.pushBack(m_opVertices, &vtx);
|
||||
}
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = this;
|
||||
}
|
||||
|
||||
void DfgGraph::removeVertex(DfgVertex& vtx) {
|
||||
// Note: changes here need to be replicated in DfgGraph::addGraph
|
||||
--m_size;
|
||||
if (vtx.is<DfgConst>()) {
|
||||
vtx.m_verticesEnt.unlink(m_constVertices, &vtx);
|
||||
} else if (vtx.is<DfgVertexVar>()) {
|
||||
vtx.m_verticesEnt.unlink(m_varVertices, &vtx);
|
||||
} else {
|
||||
vtx.m_verticesEnt.unlink(m_opVertices, &vtx);
|
||||
}
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = nullptr;
|
||||
}
|
||||
|
||||
void DfgGraph::forEachVertex(std::function<void(DfgVertex&)> f) {
|
||||
for (DfgVertex *vtxp = m_varVertices.begin(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
f(*vtxp);
|
||||
}
|
||||
for (DfgVertex *vtxp = m_constVertices.begin(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
f(*vtxp);
|
||||
}
|
||||
for (DfgVertex *vtxp = m_opVertices.begin(), *nextp; vtxp; vtxp = nextp) {
|
||||
nextp = vtxp->verticesNext();
|
||||
f(*vtxp);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgGraph::forEachVertex(std::function<void(const DfgVertex&)> f) const {
|
||||
for (const DfgVertex* vtxp = m_varVertices.begin(); vtxp; vtxp = vtxp->verticesNext()) {
|
||||
f(*vtxp);
|
||||
}
|
||||
for (const DfgVertex* vtxp = m_constVertices.begin(); vtxp; vtxp = vtxp->verticesNext()) {
|
||||
f(*vtxp);
|
||||
}
|
||||
for (const DfgVertex* vtxp = m_opVertices.begin(); vtxp; vtxp = vtxp->verticesNext()) {
|
||||
f(*vtxp);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSource(std::function<void(DfgVertex&)> f) {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) {
|
||||
if (DfgVertex* const sourcep = edgesp[i].m_sourcep) f(*sourcep);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSource(std::function<void(const DfgVertex&)> f) const {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) {
|
||||
if (DfgVertex* const sourcep = edgesp[i].m_sourcep) f(*sourcep);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSink(std::function<void(DfgVertex&)> f) {
|
||||
for (const DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->m_nextp;
|
||||
f(*edgep->m_sinkp);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSink(std::function<void(const DfgVertex&)> f) const {
|
||||
for (const DfgEdge* edgep = m_sinksp; edgep; edgep = edgep->m_nextp) f(*edgep->m_sinkp);
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSourceEdge(std::function<void(DfgEdge&, size_t)> f) {
|
||||
const auto pair = sourceEdges();
|
||||
DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) f(edgesp[i], i);
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSourceEdge(std::function<void(const DfgEdge&, size_t)> f) const {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) f(edgesp[i], i);
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSinkEdge(std::function<void(DfgEdge&)> f) {
|
||||
for (DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->m_nextp;
|
||||
f(*edgep);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSinkEdge(std::function<void(const DfgEdge&)> f) const {
|
||||
for (DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->m_nextp;
|
||||
f(*edgep);
|
||||
}
|
||||
}
|
||||
|
||||
const DfgEdge* DfgVertex::findSourceEdge(std::function<bool(const DfgEdge&, size_t)> p) const {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) {
|
||||
const DfgEdge& edge = edgesp[i];
|
||||
if (p(edge, i)) return &edge;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Vertex>
|
||||
Vertex* DfgVertex::findSink(std::function<bool(const Vertex&)> p) const {
|
||||
static_assert(std::is_base_of<DfgVertex, Vertex>::value,
|
||||
"'Vertex' must be subclass of 'DfgVertex'");
|
||||
for (DfgEdge* edgep = m_sinksp; edgep; edgep = edgep->m_nextp) {
|
||||
if (Vertex* const sinkp = edgep->m_sinkp->cast<Vertex>()) {
|
||||
if (p(*sinkp)) return sinkp;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Vertex>
|
||||
Vertex* DfgVertex::findSink() const {
|
||||
static_assert(!std::is_same<DfgVertex, Vertex>::value,
|
||||
"'Vertex' must be proper subclass of 'DfgVertex'");
|
||||
return findSink<Vertex>([](const Vertex&) { return true; });
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// DfgVertex sub-types follow
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -905,20 +588,281 @@ public:
|
||||
// The rest of the DfgVertex subclasses are generated by 'astgen' from AstNodeExpr nodes
|
||||
#include "V3Dfg__gen_auto_classes.h"
|
||||
|
||||
DfgVertexVar* DfgGraph::varVerticesBeginp() const {
|
||||
return static_cast<DfgVertexVar*>(m_varVertices.begin());
|
||||
//------------------------------------------------------------------------------
|
||||
// Dataflow graph
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class DfgGraph final {
|
||||
friend class DfgVertex;
|
||||
|
||||
// TYPES
|
||||
|
||||
// RAII handle for DfgVertex user data
|
||||
class UserDataInUse final {
|
||||
DfgGraph* m_graphp; // The referenced graph
|
||||
|
||||
public:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
UserDataInUse(DfgGraph* graphp)
|
||||
: m_graphp{graphp} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
UserDataInUse(UserDataInUse&& that) {
|
||||
UASSERT(that.m_graphp, "Moving from empty");
|
||||
m_graphp = std::exchange(that.m_graphp, nullptr);
|
||||
}
|
||||
VL_UNCOPYABLE(UserDataInUse);
|
||||
UserDataInUse& operator=(UserDataInUse&& that) {
|
||||
UASSERT(that.m_graphp, "Moving from empty");
|
||||
m_graphp = std::exchange(that.m_graphp, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~UserDataInUse() {
|
||||
if (m_graphp) m_graphp->m_userCurrent = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// MEMBERS
|
||||
|
||||
// Variables and constants make up a significant proportion of vertices (40-50% was observed
|
||||
// in large designs), and they can often be treated specially in algorithms, which in turn
|
||||
// enables significant Verilation performance gains, so we keep these in separate lists for
|
||||
// direct access.
|
||||
DfgVertex::List<DfgVertexVar> m_varVertices; // The variable vertices in the graph
|
||||
DfgVertex::List<DfgConst> m_constVertices; // The constant vertices in the graph
|
||||
DfgVertex::List<DfgVertex> m_opVertices; // The operation vertices in the graph
|
||||
|
||||
size_t m_size = 0; // Number of vertices in the graph
|
||||
uint32_t m_userCurrent = 0; // Vertex user data generation number currently in use
|
||||
uint32_t m_userCnt = 0; // Vertex user data generation counter
|
||||
// Parent of the graph (i.e.: the module containing the logic represented by this graph).
|
||||
AstModule* const m_modulep;
|
||||
const string m_name; // Name of graph (for debugging)
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
explicit DfgGraph(AstModule& module, const string& name = "") VL_MT_DISABLED;
|
||||
~DfgGraph() VL_MT_DISABLED;
|
||||
VL_UNCOPYABLE(DfgGraph);
|
||||
|
||||
// METHODS
|
||||
public:
|
||||
// Add DfgVertex to this graph (assumes not yet contained).
|
||||
inline void addVertex(DfgVertex& vtx);
|
||||
// Remove DfgVertex form this graph (assumes it is contained).
|
||||
inline void removeVertex(DfgVertex& vtx);
|
||||
// Number of vertices in this graph
|
||||
size_t size() const { return m_size; }
|
||||
// Parent module
|
||||
AstModule* modulep() const { return m_modulep; }
|
||||
// Name of this graph
|
||||
const string& name() const { return m_name; }
|
||||
|
||||
// Reset Vertex user data
|
||||
UserDataInUse userDataInUse() {
|
||||
UASSERT(!m_userCurrent, "Conflicting use of DfgVertex user data");
|
||||
++m_userCnt;
|
||||
UASSERT(m_userCnt, "'m_userCnt' overflow");
|
||||
m_userCurrent = m_userCnt;
|
||||
return UserDataInUse{this};
|
||||
}
|
||||
|
||||
// Access to vertex lists
|
||||
DfgVertex::List<DfgVertexVar>& varVertices() { return m_varVertices; }
|
||||
const DfgVertex::List<DfgVertexVar>& varVertices() const { return m_varVertices; }
|
||||
DfgVertex::List<DfgConst>& constVertices() { return m_constVertices; }
|
||||
const DfgVertex::List<DfgConst>& constVertices() const { return m_constVertices; }
|
||||
DfgVertex::List<DfgVertex>& opVertices() { return m_opVertices; }
|
||||
const DfgVertex::List<DfgVertex>& opVertices() const { return m_opVertices; }
|
||||
|
||||
// Calls given function 'f' for each vertex in the graph. It is safe to manipulate any vertices
|
||||
// in the graph, or to delete/unlink the vertex passed to 'f' during iteration. It is however
|
||||
// not safe to delete/unlink any vertex in the same graph other than the one passed to 'f'.
|
||||
inline void forEachVertex(std::function<void(DfgVertex&)> f);
|
||||
|
||||
// 'const' variant of 'forEachVertex'. No mutation allowed.
|
||||
inline void forEachVertex(std::function<void(const DfgVertex&)> f) const;
|
||||
|
||||
// Add contents of other graph to this graph. Leaves other graph empty.
|
||||
void addGraph(DfgGraph& other) VL_MT_DISABLED;
|
||||
|
||||
// Split this graph into individual components (unique sub-graphs with no edges between them).
|
||||
// Also removes any vertices that are not weakly connected to any variable.
|
||||
// Leaves 'this' graph empty.
|
||||
std::vector<std::unique_ptr<DfgGraph>> splitIntoComponents(std::string label) VL_MT_DISABLED;
|
||||
|
||||
// Extract cyclic sub-graphs from 'this' graph. Cyclic sub-graphs are those that contain at
|
||||
// least one strongly connected component (SCC) plus any other vertices that feed or sink from
|
||||
// the SCCs, up to a variable boundary. This means that the returned graphs are guaranteed to
|
||||
// be cyclic, but they are not guaranteed to be strongly connected (however, they are always
|
||||
// at least weakly connected). Trivial SCCs that are acyclic (i.e.: vertices that are not part
|
||||
// of a cycle) are left in 'this' graph. This means that at the end 'this' graph is guaranteed
|
||||
// to be a DAG (acyclic). 'this' will not necessarily be a connected graph at the end, even if
|
||||
// it was originally connected.
|
||||
std::vector<std::unique_ptr<DfgGraph>>
|
||||
extractCyclicComponents(std::string label) VL_MT_DISABLED;
|
||||
|
||||
// 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
|
||||
// the name of the graph which is included in the output.
|
||||
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,
|
||||
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
|
||||
// included in the file names and the output. This is useful for very large graphs that are
|
||||
// otherwise difficult to browse visually due to their size.
|
||||
void dumpDotAllVarConesPrefixed(const string& label = "") const VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
// Specializations of privateTypeTest
|
||||
#include "V3Dfg__gen_type_tests.h" // From ./astgen
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Inline method definitions - for DfgVertex
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <typename T>
|
||||
T& DfgVertex::user() {
|
||||
static_assert(sizeof(T) <= sizeof(UserDataStorage),
|
||||
"Size of user data type 'T' is too large for allocated storage");
|
||||
static_assert(alignof(T) <= alignof(UserDataStorage),
|
||||
"Alignment of user data type 'T' is larger than allocated storage");
|
||||
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
|
||||
const uint32_t userCurrent = m_graphp->m_userCurrent;
|
||||
UDEBUGONLY(UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving"););
|
||||
if (m_userCnt != userCurrent) {
|
||||
m_userCnt = userCurrent;
|
||||
// cppcheck-has-bug-suppress uninitvar
|
||||
VL_ATTR_UNUSED T* const resultp = new (storagep) T{};
|
||||
UDEBUGONLY(UASSERT_OBJ(resultp == storagep, this, "Something is odd"););
|
||||
}
|
||||
return *storagep;
|
||||
}
|
||||
DfgVertexVar* DfgGraph::varVerticesRbeginp() const {
|
||||
return static_cast<DfgVertexVar*>(m_varVertices.rbegin());
|
||||
|
||||
template <typename T>
|
||||
T& DfgVertex::getUser() {
|
||||
static_assert(sizeof(T) <= sizeof(UserDataStorage),
|
||||
"Size of user data type 'T' is too large for allocated storage");
|
||||
static_assert(alignof(T) <= alignof(UserDataStorage),
|
||||
"Alignment of user data type 'T' is larger than allocated storage");
|
||||
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
|
||||
#if VL_DEBUG
|
||||
const uint32_t userCurrent = m_graphp->m_userCurrent;
|
||||
UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving");
|
||||
UASSERT_OBJ(m_userCnt == userCurrent, this, "DfgVertex user data is stale");
|
||||
#endif
|
||||
return *storagep;
|
||||
}
|
||||
DfgConst* DfgGraph::constVerticesBeginp() const {
|
||||
return static_cast<DfgConst*>(m_constVertices.begin());
|
||||
|
||||
template <typename T>
|
||||
void DfgVertex::setUser(T value) {
|
||||
static_assert(sizeof(T) <= sizeof(UserDataStorage),
|
||||
"Size of user data type 'T' is too large for allocated storage");
|
||||
static_assert(alignof(T) <= alignof(UserDataStorage),
|
||||
"Alignment of user data type 'T' is larger than allocated storage");
|
||||
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
|
||||
const uint32_t userCurrent = m_graphp->m_userCurrent;
|
||||
#if VL_DEBUG
|
||||
UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving");
|
||||
#endif
|
||||
m_userCnt = userCurrent;
|
||||
*storagep = value;
|
||||
}
|
||||
DfgConst* DfgGraph::constVerticesRbeginp() const {
|
||||
return static_cast<DfgConst*>(m_constVertices.rbegin());
|
||||
|
||||
void DfgVertex::forEachSource(std::function<void(DfgVertex&)> f) {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) {
|
||||
if (DfgVertex* const sourcep = edgesp[i].m_sourcep) f(*sourcep);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSource(std::function<void(const DfgVertex&)> f) const {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) {
|
||||
if (DfgVertex* const sourcep = edgesp[i].m_sourcep) f(*sourcep);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSink(std::function<void(DfgVertex&)> f) {
|
||||
for (const DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->m_nextp;
|
||||
f(*edgep->m_sinkp);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSink(std::function<void(const DfgVertex&)> f) const {
|
||||
for (const DfgEdge* edgep = m_sinksp; edgep; edgep = edgep->m_nextp) f(*edgep->m_sinkp);
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSourceEdge(std::function<void(DfgEdge&, size_t)> f) {
|
||||
const auto pair = sourceEdges();
|
||||
DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) f(edgesp[i], i);
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSourceEdge(std::function<void(const DfgEdge&, size_t)> f) const {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) f(edgesp[i], i);
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSinkEdge(std::function<void(DfgEdge&)> f) {
|
||||
for (DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->m_nextp;
|
||||
f(*edgep);
|
||||
}
|
||||
}
|
||||
|
||||
void DfgVertex::forEachSinkEdge(std::function<void(const DfgEdge&)> f) const {
|
||||
for (DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->m_nextp;
|
||||
f(*edgep);
|
||||
}
|
||||
}
|
||||
|
||||
const DfgEdge* DfgVertex::findSourceEdge(std::function<bool(const DfgEdge&, size_t)> p) const {
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
for (size_t i = 0; i < arity; ++i) {
|
||||
const DfgEdge& edge = edgesp[i];
|
||||
if (p(edge, i)) return &edge;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Vertex>
|
||||
Vertex* DfgVertex::findSink(std::function<bool(const Vertex&)> p) const {
|
||||
static_assert(std::is_base_of<DfgVertex, Vertex>::value,
|
||||
"'Vertex' must be subclass of 'DfgVertex'");
|
||||
for (DfgEdge* edgep = m_sinksp; edgep; edgep = edgep->m_nextp) {
|
||||
if (Vertex* const sinkp = edgep->m_sinkp->cast<Vertex>()) {
|
||||
if (p(*sinkp)) return sinkp;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Vertex>
|
||||
Vertex* DfgVertex::findSink() const {
|
||||
static_assert(!std::is_same<DfgVertex, Vertex>::value,
|
||||
"'Vertex' must be proper subclass of 'DfgVertex'");
|
||||
return findSink<Vertex>([](const Vertex&) { return true; });
|
||||
}
|
||||
DfgVertex* DfgGraph::opVerticesBeginp() const { return m_opVertices.begin(); }
|
||||
DfgVertex* DfgGraph::opVerticesRbeginp() const { return m_opVertices.rbegin(); }
|
||||
|
||||
bool DfgVertex::isZero() const {
|
||||
if (const DfgConst* const constp = cast<DfgConst>()) return constp->isZero();
|
||||
@@ -930,13 +874,48 @@ bool DfgVertex::isOnes() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DfgVertex::inlined() const {
|
||||
// Inline vertices that drive only a single node, or are special
|
||||
if (!hasMultipleSinks()) return true;
|
||||
if (is<DfgConst>()) return true;
|
||||
if (is<DfgVertexVar>()) return true;
|
||||
if (const DfgArraySel* const selp = cast<DfgArraySel>()) return selp->bitp()->is<DfgConst>();
|
||||
return false;
|
||||
//------------------------------------------------------------------------------
|
||||
// Inline method definitions - for DfgGraph
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void DfgGraph::addVertex(DfgVertex& vtx) {
|
||||
// Note: changes here need to be replicated in DfgGraph::addGraph
|
||||
++m_size;
|
||||
if (DfgConst* const cVtxp = vtx.cast<DfgConst>()) {
|
||||
m_constVertices.linkBack(cVtxp);
|
||||
} else if (DfgVertexVar* const vVtxp = vtx.cast<DfgVertexVar>()) {
|
||||
m_varVertices.linkBack(vVtxp);
|
||||
} else {
|
||||
m_opVertices.linkBack(&vtx);
|
||||
}
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = this;
|
||||
}
|
||||
|
||||
void DfgGraph::removeVertex(DfgVertex& vtx) {
|
||||
// Note: changes here need to be replicated in DfgGraph::addGraph
|
||||
--m_size;
|
||||
if (DfgConst* const cVtxp = vtx.cast<DfgConst>()) {
|
||||
m_constVertices.unlink(cVtxp);
|
||||
} else if (DfgVertexVar* const vVtxp = vtx.cast<DfgVertexVar>()) {
|
||||
m_varVertices.unlink(vVtxp);
|
||||
} else {
|
||||
m_opVertices.unlink(&vtx);
|
||||
}
|
||||
vtx.m_userCnt = 0;
|
||||
vtx.m_graphp = nullptr;
|
||||
}
|
||||
|
||||
void DfgGraph::forEachVertex(std::function<void(DfgVertex&)> f) {
|
||||
for (DfgVertexVar* const vtxp : m_varVertices.unlinkable()) f(*vtxp);
|
||||
for (DfgConst* const vtxp : m_constVertices.unlinkable()) f(*vtxp);
|
||||
for (DfgVertex* const vtxp : m_opVertices.unlinkable()) f(*vtxp);
|
||||
}
|
||||
|
||||
void DfgGraph::forEachVertex(std::function<void(const DfgVertex&)> f) const {
|
||||
for (const DfgVertexVar& vtx : m_varVertices) f(vtx);
|
||||
for (const DfgConst& vtx : m_constVertices) f(vtx);
|
||||
for (const DfgVertex& vtx : m_opVertices) f(vtx);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user