Compare commits

..
104 Commits
Author SHA1 Message Date
Wilson Snyder cd693ce02b Version bump 2024-06-15 08:43:45 -04:00
Wilson Snyder 2641bc98aa Update include/gtkwave from upstream 2024-06-15 08:14:43 -04:00
Wilson Snyder ad2862ce3f Fix DPI import of null C-string (#5179). 2024-06-14 22:50:54 -04:00
Bartłomiej Chmiel 4695967185 Fix signed types emitted in hierarchical Verilation (#5178)
Signed-off-by: Bartłomiej Chmiel <[email protected]>
2024-06-14 08:07:32 -04:00
Wilson Snyder f96e99542c Internals: Fix cppcheck warnings. No functional change. 2024-06-13 21:29:03 -04:00
Wilson Snyder 7de193c2e9 Commentary: Changes update 2024-06-13 18:04:57 -04:00
Arkadiusz Kozdra 442c9bc316 Support parsing and otherwise ignoring inline constraints (#5126) 2024-06-13 08:38:20 -04:00
Arkadiusz Kozdra 3203019408 Fix coroutines without awaits to have a co_return (#4208) (#5175)
After the V3Timing refactoring the V3SchedTiming phase could apparently
move away all awaits from a coroutine without adding a co_return
statement.

Signed-off-by: Arkadiusz Kozdra <[email protected]>
2024-06-12 15:13:52 -04:00
Geza Lore bc853e260b Support StructSel in unpacked array assignments (#5176) 2024-06-12 17:07:33 +01:00
github action d6e5703298 Apply 'make format' 2024-06-12 11:44:29 +00:00
Alex Solomatnikov a9e50327fd Fix hierarchical compilation with nested -F (#5114) (#5124) 2024-06-12 07:42:52 -04:00
Wilson Snyder 6f0a36318e Tests: Fix test stability (#5167 update). 2024-06-11 20:55:01 -04:00
Wilson Snyder 221fecad0b Commentary 2024-06-11 19:47:53 -04:00
Wilson Snyder fd1e4d9e45 Add assertion on reusing VerilatedContext (#5167). 2024-06-11 19:38:58 -04:00
Wilson Snyder 920ed5ca3d Internals: Avoid unordered_map/set. No functional change intended. 2024-06-11 18:52:09 -04:00
Arthur Rosa 2537431273 Fix table optimization when applied on real data type (#5172) (#5173) 2024-06-11 13:26:11 -04:00
Bartłomiej Chmiel 13114f2efe Fix SystemC BITS_PER_DIGIT in VL_ASSIGN_SBW (#5170)
The BITS_PER_DIGIT macro value differs between SystemC versions,
thus referencing it directly is required.
2024-06-10 20:45:49 -04:00
Arkadiusz Kozdra e9f59e44a5 Fix non-constant replication in concats (#5171)
Signed-off-by: Arkadiusz Kozdra <[email protected]>
2024-06-10 11:21:43 -04:00
Arkadiusz Kozdra d4c3e35f97 Support $psprintf system function (#4314) (#5169)
`$psprintf` is a non-standard system function present in some other
simulators, and has been rejected for standardization by IEEE because
of being basically the same as `$sformatf`.

To encourage users to fix their codebase, a warning is emitted by
default, but it gets otherwise interpreted as `$sformatf` as early as
during lexing.

Signed-off-by: Arkadiusz Kozdra <[email protected]>

* wording/formatting

Signed-off-by: Arkadiusz Kozdra <[email protected]>

---------

Signed-off-by: Arkadiusz Kozdra <[email protected]>
2024-06-10 08:38:26 -04:00
Wilson Snyder 02dd33b60e Commentary: Changes update 2024-06-09 17:10:53 -04:00
Geza Lore 2bc883f3b3 Extend out-of-range select (#5159) (#5164) 2024-06-09 17:05:14 -04:00
Geza Lore 29db25b70e Reset radix in width warnings (#5166) 2024-06-09 14:39:17 +01:00
Geza Lore d6bc0c712a Disable ASLR for more tests. (#5165)
These show spurious errors with some toolchains due to an address sanitizer bug.
2024-06-09 14:32:31 +01:00
github action 962efa3691 Apply 'make format' 2024-06-09 02:45:26 +00:00
Valentin Atepalikhin 4babba16d6 Support 2D dynamic array initialization (#4700) (#5122)
* Support 2D dynamic array initialization (#4700)
- new[] on sub arrays (as per original issue)
- Built-in methods for sub-arrays
- Initialization and literals assignmensts
- Dynamic array as an element for other arrays and queues
2024-06-08 22:44:45 -04:00
Wilson Snyder 5363d89870 Tests: Disable msbuild executable running (#5163) 2024-06-08 19:37:31 -04:00
Wilson Snyder 44acb08ea9 Tests: Commentary - enable msbuild debug 2024-06-08 19:36:50 -04:00
Yutetsu TAKATSUKASA 6584b4d426 Fix assertion failure in V3Gate (#5101) 2024-06-08 08:37:01 -04:00
Paul Bowen-Huggett 9ff06c1664 Fix 'experimental/coroutine' file not found on macOS (#5030) (#5031) (#5151)
libcxx has removed the experimental/coroutine include file in favor of
the C++20-standard coroutine include. If the latter is available we
use it otherwise falling back to the existing experimental version (in
which case we also disable the deprecated-experimental-coroutine warning).

(See also https://reviews.llvm.org/D108697.)
2024-06-08 08:33:50 -04:00
github action 1537c9badc Apply 'make format' 2024-06-08 03:05:01 +00:00
Wilson Snyder 708f875b31 Fix MSVC warnings. No functional change intended. 2024-06-07 22:52:56 -04:00
Kaleb Barrett 0d3cb86a44 Fix VerilatedVpiPutHolder class (#5156) 2024-06-07 22:25:33 -04:00
Wilson Snyder 5c2b9d55b2 Fix MSVC C4805 == type mismatch warning 2024-06-07 22:14:32 -04:00
Arkadiusz Kozdra 1dbf1be3e6 Support inout clocking items (#5160) 2024-06-07 08:30:58 -04:00
Wilson SnyderandDercury 3f886f7c61 Fix select value too wide (#5148) (#5153).
Co-authored-by: Dercury <[email protected]>
2024-06-07 08:27:03 -04:00
Wilson Snyder 7c9fa8647c Fix string to be more standard (#5082) (#5083). 2024-05-31 21:51:12 -04:00
Wilson Snyder dbf68a99e8 Tests: SHow expected values in t_typenames 2024-05-30 22:54:40 -04:00
Andrew NolteandAndrew Nolte 53c2e416f4 Tests: Extend t_typenames (#5083)
Co-authored-by: Andrew Nolte <[email protected]>
2024-05-30 22:32:14 -04:00
github action e834b5be2d Apply 'make format' 2024-05-29 20:56:45 +00:00
Kaleb Barrett bd7689f59a Fix overflow of string on VPI reads (#5145) (#5146) 2024-05-29 16:55:46 -04:00
Kaleb Barrett 3496eb80a5 Improve VerilatedVpiPutHolder storage requirements (#5144) 2024-05-29 08:39:08 -04:00
Ryszard Rozak 839c3bf444 Commentary: Fix docs of verilator_coverage (#5149)
Signed-off-by: Ryszard Rozak <[email protected]>
2024-05-29 08:11:35 -04:00
Geza Lore d4b3583307 Apply DFG regularization to cyclic graphs (#5142)
The Dfg2Ast conversion assumes the 'regularize' pass was run, but we
failed to run it on cyclic sub-graphs. Do so now.

Fixes #5130.
2024-05-26 12:01:30 +01:00
Wilson Snyder ee130cb20d Tests: Skip if no constraint solver 2024-05-23 22:25:14 -04:00
github action 552a146f9c Apply 'make format' 2024-05-22 11:54:22 +00:00
Pawel Jewstafjew 913679f261 Fix output C++ type error on change detect of I/O arrays (#5125) (#5131)
operand order reversed for AstCMethodHard "neq"
interface between C-style arrays and VlUnpacked
overloads added to VlUnpacked::neq(), VlUnpacked::assign()
VlUnpacked::operator=() added

Fixes  #5125
2024-05-22 12:53:28 +01:00
Ethan Sifferman d9078df650 Fix 4-state value support for $readmem (#5070) (#5078) 2024-05-21 17:27:32 -04:00
Wilson Snyder f84592af49 Fix x-valued parameters with --x-assign unique (#5129). 2024-05-21 08:07:57 -04:00
Wilson Snyder 5f85c89425 Commentary: Changes update 2024-05-18 17:48:52 -04:00
Wilson Snyder 94f789b677 Update issue template 2024-05-18 17:41:36 -04:00
Arkadiusz Kozdra 739be2f782 Support constrained randomization with external solvers (#4947) 2024-05-17 10:38:34 -04:00
Krzysztof Bieganski 25b9a16bc7 Fix references to ports in forks (#5123)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2024-05-17 07:38:36 -04:00
Arkadiusz Kozdra 9a8e68928d Internals: Move getenvStr to verilatedos. (#5118)
* Internals: Move getenvStr to verilatedos. No functional change intended.
* Fix POS34-C. Do not call putenv() with a pointer to an automatic variable as the argument.

Signed-off-by: Arkadiusz Kozdra <[email protected]>
2024-05-13 10:23:51 -04:00
Todd Strader 19cccd170e Fix vpiInertialDelay for memories (#5113) 2024-05-10 18:49:43 -04:00
Wilson Snyder d99c8f5d44 Fix redundant AstExtend breaks DFG (#5112) 2024-05-10 18:48:09 -04:00
Geza Lore cf111d2e1f Do not create aliases for forced port signals (#5105)
+ don't remove forced signals in V3Const and Dfg

Fixes #5062
2024-05-10 18:19:51 +01:00
Arkadiusz Kozdra 45eb5b8a5c Fix method calls parsing in constraints (#5110) 2024-05-10 06:37:19 -04:00
Yinan Xu ce5cad17a8 Add increasing of stack size when possible (#5071) (#5104) 2024-05-08 22:40:42 -04:00
Wilson Snyder ed01befc25 Fix tracing interface functions (#5108). 2024-05-08 22:23:53 -04:00
Wilson Snyder 69a2bfee68 Commentary: Changes update 2024-05-08 08:41:56 -04:00
Wilson Snyder 1cc37ad6ce Internals: Add missing VL_DO_DANGLING. No functional change intended. 2024-05-08 08:36:24 -04:00
2a9f29912c Add parameterless assert control system tasks (#5010)
Signed-off-by: Bartłomiej Chmiel <[email protected]>
Co-authored-by: Ryszard Rozak <[email protected]>
Co-authored-by: Wilson Snyder <[email protected]>
Co-authored-by: Arkadiusz Kozdra <[email protected]>
2024-05-08 08:31:34 -04:00
Wilson Snyder 298e0f24d1 Add traceCapable indication to model header (#5053). 2024-05-03 20:18:06 -04:00
Geza Lore 80b08b71aa Support NBAs to arrays inside loops (#5092)
For NBAs that might execute a dynamic number of times in a single
evaluation (specifically: those that assign to array elements inside
loops), we introduce a new run-time VlNBACommitQueue data-structure
(currently a vector), which stores all pending updates and the necessary
info to reconstruct the LHS reference of the AstAssignDly at run-time.

All variables needing a commit queue has their corresponding unique
commit queue.

All NBAs to a variable that requires a commit queue go through the
commit queue. This is necessary to preserve update order in sequential
code, e.g.:
 a[7] <= 10
 for (int i = 1 ; i < 10; ++i) a[i] <= i;
 a[2] <= 10
needs to end with array elements 1..9 being 1, 10, 3, 4, 5, 6, 7, 8, 9.

This enables supporting common forms of NBAs to arrays on the left hand
side of <= in non-suspendable/non-fork code. (Suspendable/fork
implementation is unclear to me so I left it unchanged, see #5084).

Any NBA that does not need a commit queue (i.e.: those that were
supported before), use the same scheme as before, and this patch should
have no effect on the generated code for those NBAs.
2024-05-03 07:45:49 -04:00
Vito Gamberini 64ba569f0d Fix MacOS missing <type_traits> header (#5096) (#5097) 2024-05-02 21:38:36 -04:00
Wilson Snyder 3cb4033c97 Fix width extension of unpacked array select (#5095). 2024-05-02 20:41:39 -04:00
github action 6509bac59e Apply 'make format' 2024-05-02 23:03:39 +00:00
Wilson Snyder ec45a77d93 Fix macro expansion in strings per 1800-2023 (#5094). 2024-05-02 19:02:28 -04:00
Wilson Snyder 8044833c74 Fix $typename on array.min and others (#5049). 2024-05-01 20:07:13 -04:00
Geza Lore 3f89bdcfac Defer conversion of set flag based AssignDlys (#5091)
No functional change. Postpone the conversion of all AstAssignDlys that
use the 'VdlySet' scheme for array LHSs until after the complete
traversal of the netlist. The next patch takes advantage of this by
using some extra information also gathered through the traversal to
change the conversion.

AstAssignDlys inside suspendable or fork are not deferred and are
processed identical to the previous version.

There are some TODOs in this patch that are fixed in the next patch.

Output code perturbed due to variable ordering.

MULTIDRIVEN message ordering perturbed due to processing order change.
2024-05-02 00:24:00 +01:00
Todd Strader c99364b81a Support vpiInertialDelay (#5087) 2024-05-01 18:56:50 -04:00
Geza Lore d841a791e6 Refactor V3Delayed to enable algorithmic extension (#5090)
No functional change.

This patch is just cleanup with some non-functional changes to enable
the next patch. Most importantly createDlyOnSet, which implements NBAs
for arrays, has a new streamlined implementation that does the same
thing. Some output code is perturbed due to statement/local variable
insertion order.

Also renamed Vdlyvfoo to VdlyFoo for easier readability of the generated
code.
2024-05-01 23:06:25 +01:00
Geza Lore b5b937e2f2 Fix missed optimization in V3Delayed (#5089)
Checking the wrong node meant we never actually pushed constant
bit-select indices into the delayed update, as was the intention, but
always generated a temporary instead.
2024-05-01 16:22:56 +01:00
Todd Strader 4a41f69293 Internals: Remove C-style cast in VPI_HANDLE (#5088) 2024-05-01 10:11:44 -04:00
Wilson Snyder 8fd038f88e Add --localize-max-size option and optimization (#5072). 2024-04-30 19:46:54 -04:00
Wilson Snyder 4982f63da2 Update coverage for last commit 2024-04-30 19:34:41 -04:00
Wilson Snyder 71bc60fb91 Add error on zero width select (#5028). 2024-04-30 18:38:37 -04:00
Wilson Snyder 0da7ecf753 Commentary: Changes update 2024-04-30 07:37:06 -04:00
Andrea Calabrese 25ca1af8b3 Internals: Fix missing const (#4899) 2024-04-29 22:57:56 -04:00
Wilson Snyder 5d54fa8e6f Fix missing parameters with comma to be errors (#4979) (#5012). 2024-04-29 22:41:16 -04:00
Christopher Taylor e8c5b9faad Fix missing flex include path variable (#4970) (#4971) 2024-04-29 22:01:09 -04:00
Aleksander Kiryk 8e0301c287 Fix bound queue printing (#5032) 2024-04-29 21:52:06 -04:00
Iztok Jeras 3f625fc359 Tests: Add unsupported streaming LHS tests (#4302) 2024-04-29 21:35:19 -04:00
Geza Lore 72b96d5069 tests: disable ASLR for t_trace_ub_misaligned_address (#5075)
This works around an address-sanitizer bug hit with some GCC versions
2024-04-29 15:38:00 +01:00
Bartłomiej Chmiel d1b45b8d32 Fix attempted to destroy locked thread pool error (#5040) 2024-04-29 07:59:24 -04:00
Wilson Snyder 5601056ed0 Tests: Check for bad event methods 2024-04-28 13:10:25 -04:00
Wilson Snyder 8ed269c77f Make 'disable isn't underneath a begin' into UNSUPPORTED error (#4699). 2024-04-27 21:30:40 -04:00
Geza Lore 27b7e70218 Fix DFG assertion with SystemC (#5076)
Fixes #5050
2024-04-27 13:41:10 +01:00
Geza Lore 97680656d3 Remove unknown '--' form cmake -E (#5073)
After #5063, with cmake 3.22.1, I get test failures, saying:
    cmake -E env: unknown option '--'
The '--' is necessary so removing.
2024-04-27 11:09:59 +01:00
Michael Bikovitsky 4202ec2ab2 Fix CMake builds to export VERILATOR_ROOT (#5063) 2024-04-26 19:29:10 -04:00
Wilson Snyder 4f3a816fb0 Fix false ASSIGNIN on functions with explicit port map (#5069). 2024-04-26 19:26:21 -04:00
Todd Strader 25fd8ef5c0 Add VPI eval needed tracking (#5065) 2024-04-25 09:07:31 -04:00
Gökçe Aydos a9664b8d16 Remove $(info) which cannot be silenced by -MAKEFLAGS '--quiet' (#5059) 2024-04-20 08:48:16 -05:00
Wilson Snyder 26a5729514 Add CITATION.cff (#5057) (#5058). 2024-04-19 20:33:11 -04:00
Arkadiusz Kozdra 5b839699ac Support empty queue as dynarray default value (#5055)
Signed-off-by: Arkadiusz Kozdra <[email protected]>
2024-04-18 11:53:23 -05:00
Wilson Snyder 1315aa31ed Commentary 2024-04-13 08:18:45 -04:00
Wilson Snyder 5d8da6b4ac Fix width extension on delays (#5045). 2024-04-13 08:16:59 -04:00
Wilson Snyder aac2563a07 Commentary: Changes update 2024-04-13 08:03:57 -04:00
Wilson Snyder 8e44487354 Tests: update style 2024-04-13 08:02:25 -04:00
Wilson Snyder 1012c054e6 Fix $system with string argument (#5042). 2024-04-11 17:31:14 -04:00
Paul Wright a8b5738b44 Support __en/__out signals on top level inout ports (#4812) (#4856) 2024-04-11 09:02:58 -04:00
Wilson Snyder 72845167e6 Commenary: Fix README badge paragraph 2024-04-10 17:02:07 -04:00
Krzysztof Bieganski 7ca2d6470a Fix consecutive zero-delays (#5038)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2024-04-05 16:48:47 -04:00
Wilson Snyder 33e999e01a devel release 2024-04-05 06:23:30 -04:00
328 changed files with 10041 additions and 2429 deletions
+2
View File
@@ -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)
+27
View File
@@ -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
View File
@@ -15,7 +15,7 @@
cmake_minimum_required(VERSION 3.15)
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
project(Verilator
VERSION 5.024
VERSION 5.026
HOMEPAGE_URL https://verilator.org
LANGUAGES CXX
)
+68
View File
@@ -8,6 +8,73 @@ The changes in each Verilator version are described below. The
contributors that suggested a given feature are shown in []. Thanks!
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
==========================
@@ -180,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
+1
View File
@@ -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. ####
+15 -14
View File
@@ -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
+2
View File
@@ -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
+2 -2
View File
@@ -154,9 +154,9 @@ verilator_coverage - Verilator coverage analyzer
verilator_coverage --annotate <obj>
verilator_coverage -write merged.dat -read <datafiles>...
verilator_coverage -write merged.dat <datafiles>...
verilator_coverage -write-info merged.info -read <datafiles>...
verilator_coverage -write-info merged.info <datafiles>...
Verilator_coverage processes Verilated model-generated coverage reports.
+4 -4
View File
@@ -92,8 +92,8 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
sudo apt-get update ||
sudo apt-get update
# libfl-dev needed for internal coverage's test runs
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq ||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq z3 ||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq z3
# Required for test_regress/t/t_dist_attributes.pl
if [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
sudo apt-get install python3-clang mold ||
@@ -106,10 +106,10 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
elif [ "$CI_OS_NAME" = "osx" ]; then
brew update
# brew cask install gtkwave # fst2vcd hangs at launch, so don't bother
brew install ccache perl jq
brew install ccache perl jq z3
elif [ "$CI_OS_NAME" = "freebsd" ]; then
# fst2vcd fails with "Could not open '<input file>', exiting."
sudo pkg install -y ccache gmake perl5 python3 jq
sudo pkg install -y ccache gmake perl5 python3 jq z3
else
fatal "Unknown os: '$CI_OS_NAME'"
fi
+13
View File
@@ -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
View File
@@ -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
+1
View File
@@ -43,6 +43,7 @@ RUN apt-get update \
perl \
python3 \
wget \
z3 \
zlib1g \
zlib1g-dev \
&& apt-get clean \
+24 -2
View File
@@ -5,12 +5,12 @@
# General Public License Version 3 or the Perl Artistic License Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
# When releasing, also update header of Changes file, and CmakeLists.txt,
# When releasing, also update header of Changes file, and CMakeLists.txt,
# and commit using "devel release" or "Version bump" message
# Then 'make maintainer-dist'
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[5.024 2024-04-05],
AC_INIT([Verilator],[5.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 "
+10
View File
@@ -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
@@ -51,6 +56,7 @@ G-A. Kamendje
Garrett Smith
Geza Lore
Gianfranco Costamagna
Gijs Burghoorn
Glen Gibb
Gökçe Aydos
Graham Rushton
@@ -127,6 +133,7 @@ Marshal Qiao
Martin Schmidt
Martin Stadler
Matthew Ballance
Michael Bikovitsky
Michael Killough
Michaël Lefebvre
Michal Czyz
@@ -141,8 +148,10 @@ Nathan Myers
Nolan Poe
Oleh Maksymenko
Patrick Stewart
Paul Bowen-Huggett
Paul Swirhun
Paul Wright
Pawel Jewstafjew
Pawel Sagan
Pengcheng Xu
Peter Debacker
@@ -185,6 +194,7 @@ Tudor Timi
Tymoteusz Blazejczyk
Udi Finkelstein
Unai Martinez-Corral
Valentin Atepalikhin
Varun Koyyalagunta
Vassilis Papaefstathiou
Veripool API Bot
+1 -1
View File
@@ -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
+10
View File
@@ -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:
+6
View File
@@ -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
+15 -3
View File
@@ -817,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.
@@ -985,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
@@ -1732,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`.
+2 -2
View File
@@ -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
+14 -2
View File
@@ -152,6 +152,17 @@ To make use of Verilator FST tracing you will want `GTKwave
required at Verilator build time.
Install Z3
^^^^^^^^^^
In order to use constrained randomization the `Z3 Theorem Prover
<https://github.com/z3prover/z3#readme>`__ must be installed, however this is
not required at Verilator build time. There are other compatible SMT solvers,
like CVC5/CVC4, but they are not guaranteed to work. Since different solvers are
faster for different scenarios, the solver to use at run-time can be specified
by the environment variable :option:`VERILATOR_SOLVER`.
.. _Obtain Sources:
Obtain Sources
@@ -193,8 +204,9 @@ Eventual Installation Options
Before configuring the build, you must decide how you're going to
eventually install Verilator onto your system. Verilator will be compiling
the current value of the environment variables :option:`VERILATOR_ROOT`,
:option:`SYSTEMC_INCLUDE`, and :option:`SYSTEMC_LIBDIR` as defaults into
the executable, so they must be correct before configuring.
:option:`VERILATOR_SOLVER`, :option:`SYSTEMC_INCLUDE`, and
:option:`SYSTEMC_LIBDIR` as defaults into the executable, so they must be
correct before configuring.
These are the installation options:
+30 -9
View File
@@ -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
@@ -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
+89
View File
@@ -1031,6 +1031,91 @@ the evaluation process records a bitmask of variables that might have
changed; if clear, checking those signals for changes may be skipped.
Constrained randomization
-------------------------
Because general constrained randomization is a co-NP-hard problem, not all
cases are implemented in Verilator, and an external specialized SMT solver is
used for any non-obvious ones.
The ``randomize()`` method spawns an SMT solver in a sub-process. Then the
solver gets a setup query, then the definition of variables, then all the
constraints (SMT assertions) about the variables. Since the solver has no
information about the class' PRNG state, if the problem is satisfiable,
the solution space is further constrained by adding extra random constraints,
and querying the values satisfying the problem statement.
The constraint is currently constructed as fixing a simple xor of randomly
chosen bits of the variables being randomized.
The runtime classes used for handling the randomization are defined in
``verilated_random.h`` and ``verilated_random.cpp``.
``VlSubprocess``
~~~~~~~~~~~~~~~~
Subprocess handle, responsible for keeping track of the resources like child
PID, read and write file descriptors, and presenting them as a C++ iostream.
``VlRandomizer``
~~~~~~~~~~~~~~~~
Randomizer class, responsible for keeping track of variables and constraints,
and communicating with the solver subprocess.
The solver gets the constraints in `SMT-LIB2
<https://smtlib.cs.uiowa.edu/>`__ textual format in the following syntax:
::
(set-info :smt-lib-version 2.0)
(set-option :produce-models true)
(set-logic QF_BV)
(declare-fun v () (_ BitVec 16))
(declare-fun w () (_ BitVec 64))
(declare-fun x () (_ BitVec 48))
(declare-fun z () (_ BitVec 24))
(declare-fun t () (_ BitVec 23))
(assert (or (= v #x0003) (= v #x0008)))
(assert (= w #x0000000000000009))
(assert (or (or (= x #x000000000001) (= x #x000000000002)) (or (= x #x000000000004) (= x #x000000000009))))
(assert (bvult ((_ zero_extend 8) z) #x00000015))
(assert (bvugt ((_ zero_extend 8) z) #x0000000d))
(check-sat)
The solver responds with either ``sat`` or ``unsat``. Then the initial solution
is queried with:
::
(get-value (v w x z t ))
The solver then responds with e.g.:
::
((v #x0008)
(w #x0000000000000005)
(x #x000000000002)
(z #x000010)
(t #b00000000000000000000000))
And then a follow-up query (or a series thereof) is asked, and the solver gets
reset, so that it can be reused by subsequent randomization attempts:
::
(assert (= (bvxor (bvxor <...> (bvxor ((_ extract 21 21) z) ((_ extract 39 39) x)) ((_ extract 5 5) w)) <...> ((_ extract 10 10) w)) #b0))
(check-sat)
(get-value)
...
(reset)
Coding Conventions
==================
@@ -2125,6 +2210,10 @@ VERILATOR_ROOT
Standard path to Verilator distribution root; see primary Verilator
documentation.
VERILATOR_SOLVER
SMT solver command for constrained randomization; see primary Verilator
documentation.
VERILATOR_TESTS_SITE
Used with ``--site``, a colon-separated list of directories with tests to
be added to testlist.
+9
View File
@@ -40,6 +40,7 @@ Brej
Briquet
Brownlee
Buckenmaier
Burghoorn
Bybell
CLang
CMake
@@ -131,6 +132,7 @@ Gernot
Gerst
Gielda
Gigerl
Gijs
Gileadi
Giri
Gisselquist
@@ -164,6 +166,7 @@ Homebrew
Hornung
Hossell
Hsu
Huggett
Hutt
Hyperthreading
Ibrahim
@@ -842,6 +845,7 @@ parallelized
param
parameterization
parameterized
parameterless
params
parens
pawel
@@ -885,6 +889,7 @@ profcfunc
profiler
prototyptes
ps
psprintf
pthread
ptr
pulldown
@@ -968,6 +973,7 @@ submakes
submodule
submodules
substring
suspendable
sv
svBitVal
svBitVecVal
@@ -978,6 +984,7 @@ svLogicVal
svdpi
svgGetTimeUnit
swrite
synthesizeable
sys
systemc
tenghtt
@@ -996,6 +1003,7 @@ toolchain
topcell
toplevel
toupper
traceCapable
traceEverOn
tran
treei
@@ -1060,6 +1068,7 @@ vluint
vpi
vpiConstType
vpiDefName
vpiInertialDelay
vpiInstance
vpiLeftRange
vpiModule
+1 -1
View File
@@ -47,12 +47,12 @@ int main(int argc, char** argv) {
// Simulate until $finish
while (!contextp->gotFinish()) {
contextp->timeInc(1);
top->clk = ~top->clk & 0x1;
top->eval();
#if VM_TRACE
if (tfp) tfp->dump(contextp->time());
#endif
contextp->timeInc(1);
}
// Final model cleanup
+2 -1
View File
@@ -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; }
+87 -52
View File
@@ -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;
@@ -1171,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;
@@ -1749,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
}
@@ -2037,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;
@@ -2062,50 +2075,46 @@ bool VlReadMem::get(QData& addrr, std::string& valuer) {
// Parse line
if (c == '\n') {
++m_linenum;
ignore_to_eol = false;
reading_addr = false;
ignoreToEol = false;
readingAddress = false;
} else if (c == '\t' || c == ' ' || c == '\r' || c == '\f') {
reading_addr = false;
readingAddress = false;
}
// Skip // comments and detect /* comments
else if (ignore_to_cmt && lastc == '*' && c == '/') {
ignore_to_cmt = false;
reading_addr = false;
} else if (!ignore_to_eol && !ignore_to_cmt) {
if (lastc == '/' && c == '*') {
ignore_to_cmt = true;
} else if (lastc == '/' && c == '/') {
ignore_to_eol = true;
else if (ignoreToComment && lastCh == '*' && c == '/') {
ignoreToComment = false;
readingAddress = false;
} else if (!ignoreToEol && !ignoreToComment) {
if (lastCh == '/' && c == '*') {
ignoreToComment = true;
} else if (lastCh == '/' && c == '/') {
ignoreToEol = true;
} else if (c == '/') { // Part of /* or //
} else if (c == '#') {
ignore_to_eol = true;
ignoreToEol = true;
} else if (c == '@') {
reading_addr = true;
readingAddress = true;
m_anyAddr = true;
m_addr = 0;
}
// Check for hex or binary digits as file format requests
else if (std::isxdigit(c) || (!reading_addr && (c == 'x' || c == 'X'))) {
} else if (readingAddress && chIs2StateHex) {
c = std::tolower(c);
const int value
= (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c - 'a' + 10)) : (c - '0'));
if (reading_addr) {
// Decode @ addresses
m_addr = (m_addr << 4) + value;
} else {
indata = true;
valuer += static_cast<char>(c);
// printf(" Value width=%d @%x = %c\n", width, m_addr, c);
if (VL_UNLIKELY(value > 1 && !m_hex)) {
VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
"$readmemb (binary) file contains hex characters");
}
const int addressValue = (c >= 'a') ? (c - 'a' + 10) : (c - '0');
m_addr = (m_addr << 4) + addressValue;
} else if (readingAddress && chIs4StateHex) {
VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
"$readmem address contains 4-state characters");
} else if (chIs4StateHex) {
inData = true;
valuer += static_cast<char>(c);
if (VL_UNLIKELY(!m_hex && !chIs4StateBin)) {
VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
"$readmemb (binary) file contains hex characters");
}
} else {
VL_FATAL_MT(m_filename.c_str(), m_linenum, "", "$readmem file syntax error");
}
}
lastc = c;
lastCh = c;
}
if (VL_UNLIKELY(m_end != ~0ULL && m_addr <= m_end && !m_anyAddr)) {
@@ -2114,7 +2123,7 @@ bool VlReadMem::get(QData& addrr, std::string& valuer) {
}
addrr = m_addr;
return indata; // EOF
return inData; // EOF
}
void VlReadMem::setData(void* valuep, const std::string& rhs) {
const QData shift = m_hex ? 4ULL : 1ULL;
@@ -2122,8 +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;
@@ -2457,6 +2467,7 @@ VerilatedContext::VerilatedContext()
m_ns.m_coverageFilename = "coverage.dat";
m_ns.m_profExecFilename = "profile_exec.dat";
m_ns.m_profVltFilename = "profile.vlt";
m_ns.m_solverProgram = VlOs::getenvStr("VERILATOR_SOLVER", VL_SOLVER_DEFAULT);
m_fdps.resize(31);
std::fill(m_fdps.begin(), m_fdps.end(), static_cast<FILE*>(nullptr));
m_fdFreeMct.resize(30);
@@ -2567,6 +2578,14 @@ std::string VerilatedContext::profVltFilename() const VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
return m_ns.m_profVltFilename;
}
void VerilatedContext::solverProgram(const std::string& flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_ns.m_solverProgram = flag;
}
std::string VerilatedContext::solverProgram() const VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
return m_ns.m_solverProgram;
}
void VerilatedContext::quiet(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_s.m_quiet = flag;
@@ -2645,8 +2664,16 @@ void VerilatedContext::addModel(VerilatedModel* modelp) {
m_ns.m_cpuTimeStart.start();
m_ns.m_wallTimeStart.start();
}
threadPoolp(); // Ensure thread pool is created, so m_threads cannot change any more
// We look for time passing, as opposed to post-eval(), as embedded
// models might get added inside initial blocks.
if (VL_UNLIKELY(time()))
VL_FATAL_MT(
"", 0, "",
"Adding model when time is non-zero. ... Suggest check time(), or for restarting"
" model use a new VerilatedContext");
threadPoolp(); // Ensure thread pool is created, so m_threads cannot change any more
m_threadsInModels += modelp->threads();
if (VL_UNLIKELY(modelp->threads() > m_threads)) {
std::ostringstream msg;
@@ -2882,7 +2909,7 @@ void VerilatedContext::statsPrintSummary() VL_MT_UNSAFE {
VL_PRINTF("- Verilator: %s at %s; walltime %0.3f s; speed %s/s\n", endwhy.c_str(),
simtime.c_str(), walltime, simtimePerf.c_str());
const double modelMB = VlOs::memUsageBytes() / 1024.0 / 1024.0;
VL_PRINTF("- Verilator: cpu %0.3f s on %d threads; alloced %0.0f MB\n", cputime,
VL_PRINTF("- Verilator: cpu %0.3f s on %u threads; alloced %0.0f MB\n", cputime,
threadsInModels(), modelMB);
}
@@ -2933,7 +2960,6 @@ void VerilatedContext::trace(VerilatedTraceBaseC* tfp, int levels, int options)
VL_FATAL_MT("", 0, "",
"Testbench C call to 'VerilatedContext::trace()' must not be called"
" after 'VerilatedTrace*::open()'\n");
return;
}
{
// Legacy usage may call {modela}->trace(...) then {modelb}->trace(...)
@@ -3126,12 +3152,21 @@ void Verilated::stackCheck(QData needSize) VL_MT_UNSAFE {
if (haveSize == RLIM_INFINITY) haveSize = 0;
}
// VL_PRINTF_MT("-Info: stackCheck(%" PRIu64 ") have %" PRIu64 "\n", needSize, haveSize);
// Check for 1.5x need, but suggest 2x so small model increase won't cause warning
// if the user follows the suggestions
if (VL_UNLIKELY(haveSize && needSize && haveSize < (needSize + needSize / 2))) {
VL_PRINTF_MT("%%Warning: System has stack size %" PRIu64 " kb"
" which may be too small; suggest 'ulimit -s %" PRIu64 "' or larger\n",
haveSize / 1024, (needSize * 2) / 1024);
// Check and request for 1.5x need. This is automated so the user doesn't need to do anything.
QData requestSize = needSize + needSize / 2;
if (VL_UNLIKELY(haveSize && needSize && haveSize < requestSize)) {
// Try to increase the stack limit to the requested size
rlim.rlim_cur = requestSize;
if (
#ifdef _VL_TEST_RLIMIT_FAIL
true ||
#endif
setrlimit(RLIMIT_STACK, &rlim)) {
VL_PRINTF_MT("%%Warning: System has stack size %" PRIu64 " kb"
" which may be too small; failed to request more"
" using 'ulimit -s %" PRIu64 "'\n",
haveSize / 1024, requestSize);
}
}
#else
(void)needSize; // Unused argument
+5
View File
@@ -396,6 +396,7 @@ protected:
std::string m_coverageFilename; // +coverage+file filename
std::string m_profExecFilename; // +prof+exec+file filename
std::string m_profVltFilename; // +prof+vlt filename
std::string m_solverProgram; // SMT solver program
VlOs::DeltaCpuTime m_cpuTimeStart{false}; // CPU time, starts when create first model
VlOs::DeltaWallTime m_wallTimeStart{false}; // Wall time, starts when create first model
std::vector<traceBaseModelCb_t> m_traceBaseModelCbs; // Callbacks to traceRegisterModel
@@ -624,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;
-1
View File
@@ -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 $@; \
+14 -7
View File
@@ -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
@@ -504,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); \
+424
View File
@@ -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
+120
View File
@@ -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
+9 -2
View File
@@ -67,9 +67,16 @@ void VlDelayScheduler::resume() {
}
if (!m_zeroDelayed.empty()) {
for (auto&& handle : m_zeroDelayed) handle.resume();
m_zeroDelayed.clear();
// First, we need to move the coroutines out of the queue, as a resumed coroutine can
// suspend on #0 again, adding itself to the queue, which can result in reallocating the
// queue mid-iteration.
// We swap with the m_zeroDlyResumed field to keep the allocated buffer.
m_zeroDlyResumed.swap(m_zeroDelayed);
for (auto&& handle : m_zeroDlyResumed) handle.resume();
m_zeroDlyResumed.clear();
resumed = true;
// We are now in the Active region, so any coroutines added to m_zeroDelayed in the
// meantime will have to wait until the next Inactive region.
}
if (!resumed) {
+18 -11
View File
@@ -32,14 +32,18 @@
// clang-format off
// Some preprocessor magic to support both Clang and GCC coroutines with both libc++ and libstdc++
#if defined _LIBCPP_VERSION // libc++
# if __clang_major__ > 13 // Clang > 13 warns that coroutine types in std::experimental are deprecated
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-experimental-coroutine"
# if defined(__has_include) && !__has_include(<coroutine>) && __has_include(<experimental/coroutine>)
# if __clang_major__ > 13 // Clang > 13 warns that coroutine types in std::experimental are deprecated
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-experimental-coroutine"
# endif
# include <experimental/coroutine>
namespace std {
using namespace experimental; // Bring std::experimental into the std namespace
}
# else
# include <coroutine>
# endif
# include <experimental/coroutine>
namespace std {
using namespace experimental; // Bring std::experimental into the std namespace
}
#else
# if defined __clang__ && defined __GLIBCXX__ && !defined __cpp_impl_coroutine
# define __cpp_impl_coroutine 1 // Clang doesn't define this, but it's needed for libstdc++
@@ -165,6 +169,9 @@ class VlDelayScheduler final {
VerilatedContext& m_context;
VlDelayedCoroutineQueue m_queue; // Coroutines to be restored at a certain simulation time
std::vector<VlCoroutineHandle> m_zeroDelayed; // Coroutines waiting for #0
std::vector<VlCoroutineHandle> m_zeroDlyResumed; // Coroutines that waited for #0 and are
// to be resumed. Kept as a field to avoid
// reallocation.
public:
// CONSTRUCTORS
@@ -193,9 +200,9 @@ public:
VlProcessRef process; // Data of the suspended process, null if not needed
VlDelayedCoroutineQueue& queue;
std::vector<VlCoroutineHandle>& queueZeroDelay;
uint64_t delay;
VlDelayPhase phase;
VlFileLineDebug fileline;
const uint64_t delay;
const VlDelayPhase phase;
const VlFileLineDebug fileline;
bool await_ready() const { return false; } // Always suspend
void await_suspend(std::coroutine_handle<> coro) {
@@ -208,7 +215,7 @@ public:
void await_resume() const {}
};
VlDelayPhase phase = (delay == 0) ? VlDelayPhase::INACTIVE : VlDelayPhase::ACTIVE;
const VlDelayPhase phase = (delay == 0) ? VlDelayPhase::INACTIVE : VlDelayPhase::ACTIVE;
#ifdef VL_DEBUG
if (phase == VlDelayPhase::INACTIVE) {
VL_WARN_MT(filename, lineno, VL_UNKNOWN,
+4 -3
View File
@@ -30,8 +30,9 @@
#include <condition_variable>
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <type_traits>
#include <map>
#include <set>
#include <vector>
#include <deque>
@@ -294,7 +295,7 @@ private:
uint64_t m_timeLastDump = 0; // Last time we did a dump
bool m_didSomeDump = false; // Did at least one dump (i.e.: m_timeLastDump is valid)
VerilatedContext* m_contextp = nullptr; // The context used by the traced models
std::unordered_set<const VerilatedModel*> m_models; // The collection of models being traced
std::set<const VerilatedModel*> m_models; // The collection of models being traced
void addCallbackRecord(std::vector<CallbackRecord>& cbVec, CallbackRecord&& cbRec)
VL_MT_SAFE_EXCLUDES(m_mutex);
+4 -3
View File
@@ -633,12 +633,13 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addModel(VerilatedModel* modelp)
// Validate
if (!newModel) { // LCOV_EXCL_START
VL_FATAL_MT(__FILE__, __LINE__, "",
"The same model has already been added to this trace file");
VL_FATAL_MT(
__FILE__, __LINE__, "",
"The same model has already been added to this trace file or VerilatedContext");
}
if (VL_UNCOVERABLE(m_contextp && contextp != m_contextp)) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"A trace file instance can only handle models from the same context");
"A trace file instance can only handle models from the same VerilatedContext");
}
if (VL_UNCOVERABLE(m_didSomeDump)) {
VL_FATAL_MT(__FILE__, __LINE__, "",
+204 -5
View File
@@ -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
@@ -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();
}
@@ -1309,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 {
@@ -1386,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);
@@ -1499,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 !=
@@ -1511,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
+222 -81
View File
@@ -31,11 +31,14 @@
#include "verilated.h"
#include "verilated_imp.h"
#include "vltstd/vpi_user.h"
#include <cstdarg>
#include <cstdio>
#include <list>
#include <map>
#include <set>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>
@@ -630,6 +633,146 @@ public:
void invalidate() { m_id = 0; }
};
class VerilatedVpiPutHolder final {
VerilatedVpioVar m_var;
s_vpi_value m_value;
union Storage {
char init = 0; // to ensure trivial constructor
std::string str;
std::vector<s_vpi_vecval> vec;
~Storage() noexcept {/* handled by VerilatedVpiPutHolder */};
} m_storage{};
public:
VerilatedVpiPutHolder(const VerilatedVpioVar* vop, p_vpi_value valuep)
: m_var{vop} {
m_value.format = valuep->format;
switch (valuep->format) {
case vpiBinStrVal: // FALLTHRU
case vpiOctStrVal: // FALLTHRU
case vpiDecStrVal: // FALLTHRU
case vpiHexStrVal: // FALLTHRU
case vpiStringVal: {
new (&m_storage.str) std::string{valuep->value.str};
m_value.value.str = const_cast<char*>(m_storage.str.c_str());
break;
}
case vpiScalarVal: {
m_value.value.scalar = valuep->value.scalar;
break;
}
case vpiIntVal: {
m_value.value.integer = valuep->value.integer;
break;
}
case vpiRealVal: {
m_value.value.real = valuep->value.real;
break;
}
case vpiVectorVal: {
size_t words = 0;
switch (vop->varp()->vltype()) {
case VLVT_UINT8:
case VLVT_UINT16:
case VLVT_UINT32: {
words = 1;
break;
}
case VLVT_UINT64: {
words = 2;
break;
}
case VLVT_WDATA: {
words = VL_WORDS_I(vop->varp()->packed().elements());
break;
}
default: break;
}
new (&m_storage.vec)
std::vector<s_vpi_vecval>{valuep->value.vector, &valuep->value.vector[words]};
m_value.value.vector = m_storage.vec.data();
break;
}
}
}
VerilatedVpiPutHolder(VerilatedVpiPutHolder const& o)
: m_var{o.m_var}
, m_value{o.m_value} {
switch (m_value.format) {
case vpiBinStrVal: // FALLTHRU
case vpiOctStrVal: // FALLTHRU
case vpiDecStrVal: // FALLTHRU
case vpiHexStrVal: // FALLTHRU
case vpiStringVal: {
new (&m_storage.str) std::string{o.m_storage.str};
break;
}
case vpiVectorVal: {
new (&m_storage.vec) std::vector<s_vpi_vecval>{o.m_storage.vec};
break;
}
}
}
VerilatedVpiPutHolder(VerilatedVpiPutHolder&& o) noexcept
: m_var{std::move(o.m_var)}
, m_value{std::move(o.m_value)} {
switch (m_value.format) {
case vpiBinStrVal: // FALLTHRU
case vpiOctStrVal: // FALLTHRU
case vpiDecStrVal: // FALLTHRU
case vpiHexStrVal: // FALLTHRU
case vpiStringVal: {
new (&m_storage.str) std::string{std::move(o.m_storage.str)};
break;
}
case vpiVectorVal: {
new (&m_storage.vec) std::vector<s_vpi_vecval>{std::move(o.m_storage.vec)};
break;
}
}
}
~VerilatedVpiPutHolder() noexcept {
switch (m_value.format) {
case vpiBinStrVal: // FALLTHRU
case vpiOctStrVal: // FALLTHRU
case vpiDecStrVal: // FALLTHRU
case vpiHexStrVal: // FALLTHRU
case vpiStringVal: m_storage.str.~basic_string(); break;
case vpiVectorVal: m_storage.vec.~vector(); break;
}
}
VerilatedVpioVar* varp() { return &m_var; }
p_vpi_value valuep() { return &m_value; }
static bool canInertialDelay(p_vpi_value valuep) {
switch (valuep->format) {
case vpiBinStrVal: // FALLTHRU
case vpiOctStrVal: // FALLTHRU
case vpiDecStrVal: // FALLTHRU
case vpiHexStrVal: // FALLTHRU
case vpiStringVal: {
if (VL_UNLIKELY(!valuep->value.str)) return false;
break;
}
case vpiScalarVal: // FALLTHRU
case vpiIntVal: // FALLTHRU
case vpiRealVal: break;
case vpiVectorVal: {
if (VL_UNLIKELY(!valuep->value.vector)) return false;
break;
}
default: {
return false;
}
}
return true;
}
};
struct VerilatedVpiTimedCbsCmp final {
// Ordering sets keyed by time, then callback unique id
bool operator()(const std::pair<QData, uint64_t>& a,
@@ -652,9 +795,11 @@ class VerilatedVpiImp final {
std::array<VpioCbList, CB_ENUM_MAX_VALUE> m_cbCurrentLists;
VpioFutureCbs m_futureCbs; // Time based callbacks for future timestamps
VpioFutureCbs m_nextCbs; // cbNextSimTime callbacks
std::list<VerilatedVpiPutHolder> m_inertialPuts; // Pending vpi puts due to vpiInertialDelay
VerilatedVpiError* m_errorInfop = nullptr; // Container for vpi error info
VerilatedAssertOneThread m_assertOne; // Assert only called from single thread
uint64_t m_nextCallbackId = 1; // Id to identify callback
bool m_evalNeeded = false; // Model has had signals updated via vpi_put_value()
static VerilatedVpiImp& s() { // Singleton
static VerilatedVpiImp s_s;
@@ -776,7 +921,7 @@ public:
assertOneCheck();
VpioCbList& cbObjList = s().m_cbCurrentLists[cbValueChange];
bool called = false;
std::unordered_set<VerilatedVpioVar*> update; // set of objects to update after callbacks
std::set<VerilatedVpioVar*> update; // set of objects to update after callbacks
if (cbObjList.empty()) return called;
const auto last = std::prev(cbObjList.end()); // prevent looping over newly added elements
for (auto it = cbObjList.begin(); true;) {
@@ -813,6 +958,17 @@ public:
}
static void dumpCbs() VL_MT_UNSAFE_ONE;
static VerilatedVpiError* error_info() VL_MT_UNSAFE_ONE; // getter for vpi error info
static 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();
}
};
//======================================================================
@@ -915,6 +1071,11 @@ PLI_INT32 VerilatedVpioReasonCb::dovpi_remove_cb() {
return 1;
}
void VerilatedVpi::clearEvalNeeded() VL_MT_UNSAFE_ONE { VerilatedVpiImp::evalNeeded(false); }
bool VerilatedVpi::evalNeeded() VL_MT_UNSAFE_ONE { return VerilatedVpiImp::evalNeeded(); }
void VerilatedVpi::doInertialPuts() VL_MT_UNSAFE_ONE { VerilatedVpiImp::doInertialPuts(); }
//======================================================================
// VerilatedVpiImp implementation
@@ -2194,13 +2355,28 @@ bool vl_check_format(const VerilatedVar* varp, const p_vpi_value valuep, const c
return status;
}
static void vl_strprintf(std::string& buffer, char const* fmt, ...) {
va_list args, args_copy;
va_start(args, fmt);
buffer.clear();
// Make copy of args since we may need to call VL_VSNPRINTF more than once
va_copy(args_copy, args);
// Try VL_VSNPRINTF in existing buffer
const int result
= VL_VSNPRINTF(const_cast<char*>(buffer.data()), buffer.capacity(), fmt, args_copy);
va_end(args_copy);
const int required = result + 1; // Returned size doesn't include NUL terminator
// If there wasn't enough space, reallocate and try again
if (buffer.capacity() < required) {
buffer.reserve(required * 2);
VL_VSNPRINTF(const_cast<char*>(buffer.data()), buffer.capacity(), fmt, args);
}
va_end(args);
}
void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
const char* fullname) {
if (!vl_check_format(varp, valuep, fullname, true)) return;
// Maximum required size is for binary string, one byte per bit plus null termination
static thread_local char t_outStr[VL_VALUE_STRING_MAX_WORDS * VL_EDATASIZE + 1];
// cppcheck-suppress variableScope
static const thread_local int t_outStrSz = sizeof(t_outStr) - 1;
// string data type is dynamic and may vary in size during simulation
static thread_local std::string t_outDynamicStr;
// We used to presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal
@@ -2244,43 +2420,21 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
return;
}
} else if (valuep->format == vpiBinStrVal) {
valuep->value.str = t_outStr;
int bits = varp->packed().elements();
t_outDynamicStr.resize(bits);
const CData* datap = (reinterpret_cast<CData*>(varDatap));
int i;
if (bits > t_outStrSz) {
// limit maximum size of output to size of buffer to prevent overrun.
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
VL_VALUE_STRING_MAX_WORDS, bits);
bits = t_outStrSz;
}
for (i = 0; i < bits; ++i) {
for (size_t i = 0; i < bits; ++i) {
const char val = (datap[i >> 3] >> (i & 7)) & 1;
t_outStr[bits - i - 1] = val ? '1' : '0';
t_outDynamicStr[bits - i - 1] = val ? '1' : '0';
}
t_outStr[i] = '\0';
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
return;
} else if (valuep->format == vpiOctStrVal) {
valuep->value.str = t_outStr;
int chars = (varp->packed().elements() + 2) / 3;
t_outDynamicStr.resize(chars);
const int bytes = VL_BYTES_I(varp->packed().elements());
const CData* datap = (reinterpret_cast<CData*>(varDatap));
int i;
if (chars > t_outStrSz) {
// limit maximum size of output to size of buffer to prevent overrun.
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
VL_VALUE_STRING_MAX_WORDS, chars);
chars = t_outStrSz;
}
for (i = 0; i < chars; ++i) {
for (size_t i = 0; i < chars; ++i) {
const div_t idx = div(i * 3, 8);
int val = datap[idx.quot];
if ((idx.quot + 1) < bytes) {
@@ -2299,46 +2453,31 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
val &= (1 << rem) - 1;
}
}
t_outStr[chars - i - 1] = '0' + (val & 7);
t_outDynamicStr[chars - i - 1] = '0' + (val & 7);
}
t_outStr[i] = '\0';
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
return;
} else if (valuep->format == vpiDecStrVal) {
valuep->value.str = t_outStr;
// outStrSz does not include nullptr termination so add one
if (varp->vltype() == VLVT_UINT8) {
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%hhu",
static_cast<unsigned char>(*(reinterpret_cast<CData*>(varDatap))));
return;
vl_strprintf(t_outDynamicStr, "%hhu",
static_cast<unsigned char>(*(reinterpret_cast<CData*>(varDatap))));
} else if (varp->vltype() == VLVT_UINT16) {
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%hu",
static_cast<unsigned short>(*(reinterpret_cast<SData*>(varDatap))));
return;
vl_strprintf(t_outDynamicStr, "%hu",
static_cast<unsigned short>(*(reinterpret_cast<SData*>(varDatap))));
} else if (varp->vltype() == VLVT_UINT32) {
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%u",
static_cast<unsigned int>(*(reinterpret_cast<IData*>(varDatap))));
return;
vl_strprintf(t_outDynamicStr, "%u",
static_cast<unsigned int>(*(reinterpret_cast<IData*>(varDatap))));
} else if (varp->vltype() == VLVT_UINT64) {
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%llu",
static_cast<unsigned long long>(*(reinterpret_cast<QData*>(varDatap))));
return;
vl_strprintf(t_outDynamicStr, "%llu",
static_cast<unsigned long long>(*(reinterpret_cast<QData*>(varDatap))));
}
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
return;
} else if (valuep->format == vpiHexStrVal) {
valuep->value.str = t_outStr;
int chars = (varp->packed().elements() + 3) >> 2;
t_outDynamicStr.resize(chars);
const CData* datap = (reinterpret_cast<CData*>(varDatap));
int i;
if (chars > t_outStrSz) {
// limit maximum size of output to size of buffer to prevent overrun.
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
VL_VALUE_STRING_MAX_WORDS, chars);
chars = t_outStrSz;
}
for (i = 0; i < chars; ++i) {
for (size_t i = 0; i < chars; ++i) {
char val = (datap[i >> 1] >> ((i & 1) << 2)) & 15;
if (i == (chars - 1)) {
// most significant char, mask off nonexistent bits when vector
@@ -2349,9 +2488,9 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
val &= (1 << rem) - 1;
}
}
t_outStr[chars - i - 1] = "0123456789abcdef"[static_cast<int>(val)];
t_outDynamicStr[chars - i - 1] = "0123456789abcdef"[static_cast<int>(val)];
}
t_outStr[i] = '\0';
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
return;
} else if (valuep->format == vpiStringVal) {
if (varp->vltype() == VLVT_STRING) {
@@ -2364,26 +2503,15 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
return;
}
} else {
valuep->value.str = t_outStr;
int bytes = VL_BYTES_I(varp->packed().elements());
t_outDynamicStr.resize(bytes);
const CData* datap = (reinterpret_cast<CData*>(varDatap));
int i;
if (bytes > t_outStrSz) {
// limit maximum size of output to size of buffer to prevent overrun.
VL_VPI_WARNING_(__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than "
"required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format),
fullname, t_outStrSz, VL_VALUE_STRING_MAX_WORDS, bytes);
bytes = t_outStrSz;
}
for (i = 0; i < bytes; ++i) {
for (size_t i = 0; i < bytes; ++i) {
const char val = datap[bytes - i - 1];
// other simulators replace [leading?] zero chars with spaces, replicate here.
t_outStr[i] = val ? val : ' ';
t_outDynamicStr[i] = val ? val : ' ';
}
t_outStr[i] = '\0';
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
return;
}
} else if (valuep->format == vpiIntVal) {
@@ -2432,7 +2560,7 @@ void vpi_get_value(vpiHandle object, p_vpi_value valuep) {
}
vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_p*/,
PLI_INT32 /*flags*/) {
PLI_INT32 flags) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_put_value %p %p\n", object, valuep););
VerilatedVpiImp::assertOneCheck();
VL_VPI_ERROR_RESET_();
@@ -2440,11 +2568,24 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
VL_VPI_WARNING_(__FILE__, __LINE__, "Ignoring vpi_put_value with nullptr value pointer");
return nullptr;
}
PLI_INT32 delay_mode = flags & 0xfff;
if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) {
if (delay_mode == vpiInertialDelay) {
if (!VerilatedVpiPutHolder::canInertialDelay(valuep)) {
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Unsupported p_vpi_value as requested for '%s' with vpiInertialDelay",
__func__, vop->fullname());
return nullptr;
}
VerilatedVpiImp::inertialDelay(vop, valuep);
return object;
}
VL_DEBUG_IF_PLI(
VL_DBG_MSGF("- vpi: vpi_put_value name=%s fmt=%d vali=%d\n", vop->fullname(),
valuep->format, valuep->value.integer);
VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n", vop->varp()->datap(), vop->varDatap()););
VerilatedVpiImp::evalNeeded(true);
if (VL_UNLIKELY(!vop->varp()->isPublicRW())) {
VL_VPI_WARNING_(__FILE__, __LINE__,
+7
View File
@@ -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;
+5
View File
@@ -614,8 +614,13 @@ static inline double VL_ROUND(double n) {
//=========================================================================
// Time and performance
#include <string>
namespace VlOs {
/// Get environment variable
extern std::string getenvStr(const std::string& envvar,
const std::string& defaultValue) VL_MT_SAFE;
extern uint64_t memUsageBytes() VL_MT_SAFE; ///< Return memory usage in bytes, or 0 if unknown
// Internal: Record CPU time, starting point on construction, and current delta from that
+26
View File
@@ -99,5 +99,31 @@ uint64_t memUsageBytes() VL_MT_SAFE {
#endif
}
//=========================================================================
// VlOs::getenvStr implementation
std::string getenvStr(const std::string& envvar, const std::string& defaultValue) VL_MT_SAFE {
std::string ret;
#if defined(_MSC_VER)
// Note: MinGW does not offer _dupenv_s
const char* envvalue = nullptr;
_dupenv_s((char**)&envvalue, nullptr, envvar.c_str());
if (envvalue != nullptr) {
const std::string result{envvalue};
free((void*)envvalue);
ret = result;
} else {
ret = defaultValue;
}
#else
if (const char* const envvalue = getenv(envvar.c_str())) {
ret = envvalue;
} else {
ret = defaultValue;
}
#endif
return ret;
}
//=========================================================================
} //namespace VlOs
+1
View File
@@ -480,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}
+8
View File
@@ -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)
+2 -2
View File
@@ -68,7 +68,7 @@ class ActiveTopVisitor final : public VNVisitor {
UINFO(4, " ACTIVE " << nodep << endl);
// Remove duplicate clocks and such; sensesp() may change!
V3Const::constifyExpensiveEdit(nodep);
AstSenTree* const sensesp = nodep->sensesp();
AstSenTree* sensesp = nodep->sensesp();
UASSERT_OBJ(sensesp, nodep, "nullptr");
if (sensesp->sensesp() && sensesp->sensesp()->isNever()) {
// Never executing. Kill it.
@@ -106,7 +106,7 @@ class ActiveTopVisitor final : public VNVisitor {
// There may be other references to same sense tree,
// we'll be removing all references when we get to them,
// but don't dangle our pointer yet!
pushDeletep(sensesp);
VL_DO_DANGLING(pushDeletep(sensesp), sensesp);
}
nodep->sensesp(wantp);
}
+49 -1
View File
@@ -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());
+12 -4
View File
@@ -158,6 +158,13 @@ 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)) {
@@ -168,9 +175,7 @@ private:
AstConst* const skewp = VN_AS(nodep->skewp(), Const);
if (skewp->num().isNegative()) skewp->v3error("Skew cannot be negative");
AstNodeExpr* const exprp = nodep->exprp();
// Get a ref to the sampled/driven variable
AstVar* const varp = nodep->varp()->unlinkFrBack();
m_clockingp->addVarsp(varp);
m_clockingp->addVarsp(varp->unlinkFrBack());
varp->user1p(nodep);
if (nodep->direction() == VDirection::OUTPUT) {
AstVarRef* const skewedRefp = new AstVarRef{flp, varp, VAccess::READ};
@@ -224,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>);
@@ -250,7 +256,7 @@ private:
} else {
nodep->v3fatal("Invalid direction");
}
pushDeletep(nodep->unlinkFrBack());
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
}
void visit(AstDelay* nodep) override {
// Only cycle delays are relevant in this stage; also only process once
@@ -290,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{
+71 -19
View File
@@ -253,17 +253,17 @@ public:
, m_final{false}
, m_initial{false} {}
class Extends {};
VBaseOverride(Extends)
explicit VBaseOverride(Extends)
: m_extends{true}
, m_final{false}
, m_initial{false} {}
class Final {};
VBaseOverride(Final)
explicit VBaseOverride(Final)
: m_extends{false}
, m_final{true}
, m_initial{false} {}
class Initial {};
VBaseOverride(Initial)
explicit VBaseOverride(Initial)
: m_extends{false}
, m_final{false}
, m_initial{true} {}
@@ -559,6 +559,7 @@ public:
DYNAMIC_TRIGGER_SCHEDULER,
FORK_SYNC,
PROCESS_REFERENCE,
RANDOM_GENERATOR,
// Unsigned and two state; fundamental types
UINT32,
UINT64,
@@ -592,6 +593,7 @@ public:
"VlDynamicTriggerScheduler",
"VlFork",
"VlProcessRef",
"VlRandomizer",
"IData",
"QData",
"LOGIC_IMPLICIT",
@@ -599,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() {
@@ -652,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;
@@ -691,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; }
@@ -903,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,
@@ -1102,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 };
+48 -13
View File
@@ -89,8 +89,8 @@ public:
virtual AstNodeDType* subDTypep() const VL_MT_SAFE { return nullptr; }
virtual bool isFourstate() const;
// Ideally an IEEE $typename
virtual string prettyDTypeName() const { return prettyTypeName(); }
string prettyDTypeNameQ() const { return "'" + prettyDTypeName() + "'"; }
virtual string prettyDTypeName(bool) const { return prettyTypeName(); }
string prettyDTypeNameQ() const { return "'" + prettyDTypeName(false) + "'"; }
//
// Changing the width may confuse the data type resolution, so must clear
// TypeTable cache after use.
@@ -215,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(); }
@@ -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; }
@@ -548,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; }
@@ -716,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 {
@@ -806,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(); }
@@ -814,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; }
@@ -959,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
@@ -1066,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();
@@ -1129,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;
@@ -1262,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())));
@@ -1357,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 {
@@ -1384,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;
+47 -5
View File
@@ -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
@@ -2259,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);
@@ -2282,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; }
@@ -2401,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; }
@@ -2447,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; }
@@ -2493,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; }
@@ -2519,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; }
@@ -2641,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; }
@@ -2711,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; }
@@ -2734,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; }
@@ -2804,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; }
@@ -2827,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; }
@@ -2850,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; }
@@ -2873,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; }
@@ -2896,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; }
@@ -2966,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; }
@@ -2989,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; }
@@ -3059,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; }
@@ -3082,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; }
@@ -3104,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; }
@@ -3277,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; }
@@ -3325,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()) ? "" : "<<";
}
@@ -3373,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()) ? "" : ">>";
}
@@ -3425,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; }
@@ -3476,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; }
@@ -3552,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; }
@@ -3668,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; }
@@ -3692,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; }
@@ -3809,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; }
@@ -3855,6 +3884,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f& %r)"; }
string emitC() override { return "VL_AND_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvand %l %r)"; }
string emitSimpleOperator() override { return "&"; }
bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
bool cleanLhs() const override { return false; }
@@ -3878,6 +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; }
@@ -3950,6 +3981,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f| %r)"; }
string emitC() override { return "VL_OR_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvor %l %r)"; }
string emitSimpleOperator() override { return "|"; }
bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
bool cleanLhs() const override { return false; }
@@ -3973,6 +4005,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f^ %r)"; }
string emitC() override { return "VL_XOR_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvxor %l %r)"; }
string emitSimpleOperator() override { return "^"; }
bool cleanOut() const override { return false; } // Lclean && Rclean
bool cleanLhs() const override { return false; }
@@ -4895,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 {
@@ -4918,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 {
@@ -5050,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; }
@@ -5080,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; }
@@ -5112,6 +5153,7 @@ public:
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opNot(lhs); }
string emitVerilog() override { return "%f(~ %l)"; }
string emitC() override { return "VL_NOT_%lq(%lW, %P, %li)"; }
string emitSMT() const override { return "(bvnot %l)"; }
string emitSimpleOperator() override { return "~"; }
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return false; }
+37 -6
View File
@@ -956,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:
@@ -971,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
@@ -1809,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
@@ -1854,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;
@@ -2009,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; }
@@ -2434,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 {
@@ -2580,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)
+93 -15
View File
@@ -788,6 +788,22 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe
info.m_type = "VlUnpacked<" + sub.m_type;
info.m_type += ", " + cvtToStr(adtypep->declRange().elements());
info.m_type += ">";
} else if (const auto* const adtypep = VN_CAST(dtypep, NBACommitQueueDType)) {
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
compound = true;
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(compound, false);
AstNodeDType* eDTypep = adtypep->subDTypep();
unsigned rank = 0;
while (AstUnpackArrayDType* const uaDTypep = VN_CAST(eDTypep, UnpackArrayDType)) {
eDTypep = uaDTypep->subDTypep()->skipRefp();
++rank;
}
info.m_type = "VlNBACommitQueue<";
info.m_type += sub.m_type;
info.m_type += adtypep->partial() ? ", true" : ", false";
info.m_type += ", " + eDTypep->cTypeRecurse(compound, false).m_type;
info.m_type += ", " + std::to_string(rank);
info.m_type += ">";
} else if (packed && (VN_IS(dtypep, PackArrayDType))) {
const AstPackArrayDType* const adtypep = VN_CAST(dtypep, PackArrayDType);
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(false, true);
@@ -827,6 +843,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe
info.m_type = "VlForkSync";
} else if (bdtypep->isProcessRef()) {
info.m_type = "VlProcessRef";
} else if (bdtypep->isRandomGenerator()) {
info.m_type = "VlRandomizer";
} else if (bdtypep->isEvent()) {
info.m_type = v3Global.assignsEvents() ? "VlAssignableEvent" : "VlEvent";
} else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width
@@ -885,9 +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++;
@@ -1442,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() << "]";
@@ -1463,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) {
@@ -1588,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);
@@ -1624,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 << " -> ";
@@ -1938,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]";
@@ -1977,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
@@ -1993,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() {
@@ -2137,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 + "]";
}
@@ -2683,6 +2758,7 @@ void AstCMethodHard::setPurity() {
{"commit", false},
{"delay", false},
{"done", false},
{"enqueue", false},
{"erase", false},
{"evaluate", false},
{"evaluation", false},
@@ -2695,6 +2771,7 @@ void AstCMethodHard::setPurity() {
{"find_last_index", true},
{"fire", false},
{"first", false},
{"hard", false},
{"init", false},
{"insert", false},
{"inside", true},
@@ -2734,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());
+3 -1
View File
@@ -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 = ", ";
+9 -5
View File
@@ -1556,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.
@@ -3071,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
@@ -3542,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
@@ -3736,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
View File
File diff suppressed because it is too large Load Diff
+1 -2
View File
@@ -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);
+1 -1
View File
@@ -443,7 +443,7 @@ class AstToDfgVisitor final : public VNVisitor {
// Mark variables with external references
if (nodep->isIO() // Ports
|| nodep->user2() // Target of a hierarchical reference
|| nodep->isForceable() // Forceable
|| nodep->isForced() // Forced
) {
getNet(nodep)->setHasExtRefs();
}
+5 -1
View File
@@ -64,6 +64,7 @@ public:
struct Hash final {
size_t operator()(const KeySel& key) const {
// cppcheck-suppress unreadVariable // cppcheck bug
V3Hash hash{vertexHash(key.m_fromp)};
hash += key.m_lsb;
hash += key.m_width;
@@ -83,6 +84,7 @@ class KeyUnary final {
const DfgVertex* const m_source0p;
public:
// cppcheck-suppress noExplicitConstructor
KeyUnary(DfgVertex* source0p)
: m_source0p{source0p} {}
@@ -110,6 +112,7 @@ public:
struct Hash final {
size_t operator()(const KeyBinary& key) const {
// cppcheck-suppress unreadVariable // cppcheck bug
V3Hash hash{vertexHash(key.m_source0p)};
hash += vertexHash(key.m_source1p);
return hash.value();
@@ -137,6 +140,7 @@ public:
struct Hash final {
size_t operator()(const KeyTernary& key) const {
// cppcheck-suppress unreadVariable // cppcheck bug
V3Hash hash{vertexHash(key.m_source0p)};
hash += vertexHash(key.m_source1p);
hash += vertexHash(key.m_source2p);
@@ -348,7 +352,7 @@ class V3DfgCache final {
inline CacheType<Vertex>& cacheForType();
public:
V3DfgCache(DfgGraph& dfg)
explicit V3DfgCache(DfgGraph& dfg)
: m_dfg{dfg} {}
// Find a vertex of type 'Vertex', with the given operands, or create a new one and add it.
+2 -1
View File
@@ -295,7 +295,8 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
// For each cyclic component
for (auto& component : cyclicComponents) {
if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
// TODO: Apply optimizations safe for cyclic graphs
// Converting back to Ast assumes the 'regularize' pass was run, so we must run it
V3DfgPasses::regularize(*component, ctx.m_regularizeContext);
// Add back under the main DFG (we will convert everything back in one go)
dfg->addGraph(*component);
}
+3 -9
View File
@@ -165,17 +165,11 @@ void V3DfgPasses::inlineVars(DfgGraph& dfg) {
const AstVar* const astVarp = driverVarp->varp();
// If driven from a SystemC variable
if (astVarp->isSc()) continue;
// If the variable is forceable
if (astVarp->isForceable()) continue;
// If the variable is forced
if (astVarp->isForced()) continue;
}
varp->forEachSinkEdge([=](DfgEdge& edge) {
// If sink is a SystemC variable, don't inline that sink
if (DfgVertexVar* const sinkVarp = edge.sinkp()->cast<DfgVarPacked>()) {
if (sinkVarp->varp()->isSc()) return;
}
edge.relinkSource(driverp);
});
varp->forEachSinkEdge([=](DfgEdge& edge) { edge.relinkSource(driverp); });
}
}
}
+23 -6
View File
@@ -27,6 +27,7 @@
VL_DEFINE_DEBUG_FUNCTIONS;
std::string V3DfgRegularizeContext::tmpNamePrefix(DfgGraph& dfg) {
// cppcheck-suppress unreadVariable // cppcheck bug
V3Hash hash{dfg.modulep()->name()};
hash += m_label;
std::string name = hash.toString();
@@ -45,11 +46,15 @@ class DfgRegularize final {
// Return canonical variable that can be used to hold the value of this vertex
DfgVarPacked* getCanonicalVariable(DfgVertex& vtx) {
// First gather all existing variables fully written by this vertex
// First gather all existing variables fully written by this vertex. Ignore SystemC
// variables, those cannot act as canonical variables, as they cannot participate in
// expressions or be assigned rvalues.
std::vector<DfgVarPacked*> varVtxps;
vtx.forEachSink([&](DfgVertex& sink) {
if (DfgVarPacked* const varVtxp = sink.cast<DfgVarPacked>()) {
if (varVtxp->isDrivenFullyByDfg()) varVtxps.push_back(varVtxp);
if (varVtxp->isDrivenFullyByDfg() && !varVtxp->varp()->isSc()) {
varVtxps.push_back(varVtxp);
}
}
});
@@ -95,10 +100,22 @@ class DfgRegularize final {
// Ensure intermediate values used multiple times are written to variables
for (DfgVertex& vtx : m_dfg.opVertices()) {
// Operations without multiple sinks need no variables
if (!vtx.hasMultipleSinks()) continue;
// Array selects need no variables, they are just memory references
if (vtx.is<DfgArraySel>()) continue;
const bool needsIntermediateVariable = [&]() {
// Anything that drives an SC variable needs an intermediate,
// as we can only assign simple variables to SC variables at runtime.
const bool hasScSink = vtx.findSink<DfgVertexVar>([](const DfgVertexVar& var) { //
return var.varp()->isSc();
});
if (hasScSink) return true;
// Operations without multiple sinks need no variables
if (!vtx.hasMultipleSinks()) return false;
// Array selects need no variables, they are just memory references
if (vtx.is<DfgArraySel>()) return false;
// Otherwise needs an intermediate variable
return true;
}();
if (!needsIntermediateVariable) continue;
// This is an op which has multiple sinks. Ensure it is assigned to a variable.
DfgVarPacked* const varp = getCanonicalVariable(vtx);
+1 -1
View File
@@ -239,7 +239,7 @@ public:
ASTGEN_MEMBERS_DfgVarPacked;
bool isDrivenFullyByDfg() const {
return arity() == 1 && source(0)->dtypep() == dtypep() && !varp()->isForceable();
return arity() == 1 && source(0)->dtypep() == dtypep() && !varp()->isForced();
}
void addDriver(FileLine* flp, uint32_t lsb, DfgVertex* vtxp) {
+1
View File
@@ -84,6 +84,7 @@ class EmitCConstPool final : public EmitCConstInit {
putns(varp, "extern const ");
putns(varp, varp->dtypep()->cType(nameProtect, false, false));
putns(varp, " = ");
UASSERT_OBJ(varp, varp->valuep(), "Var without value");
iterateConst(varp->valuep());
putns(varp, ";\n");
// Keep track of stats
+2
View File
@@ -722,6 +722,8 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP
return "";
} else if (basicp && basicp->isDynamicTriggerScheduler()) {
return "";
} else if (basicp && basicp->isRandomGenerator()) {
return "";
} else if (basicp) {
const bool zeroit
= (varp->attrFileDescr() // Zero so we don't do file IO if never $fopen
+1
View File
@@ -515,6 +515,7 @@ class EmitCHeader final : public EmitCConstInit {
if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n");
if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n");
if (v3Global.usesTiming()) puts("#include \"verilated_timing.h\"\n");
if (v3Global.useRandomizeMethods()) puts("#include \"verilated_random.h\"\n");
std::set<string> cuse_set;
auto add_to_cuse_set = [&](string s) { cuse_set.insert(s); };
+1
View File
@@ -389,6 +389,7 @@ class EmitCImp final : EmitCFunc {
} else if (varp->isParam()) {
} else if (varp->isStatic() && varp->isConst()) {
} else if (varp->basicp() && varp->basicp()->isTriggerVec()) {
} else if (VN_IS(varp->dtypep(), NBACommitQueueDType)) {
} else {
int vects = 0;
AstNodeDType* elementp = varp->dtypeSkipRefp();
+6
View File
@@ -85,6 +85,9 @@ class CMakeEmitter final {
cmake_set(*of, "VERILATOR_ROOT",
V3OutFormatter::quoteNameControls(V3Options::getenvVERILATOR_ROOT()), "PATH",
"Path to Verilator kit (from $VERILATOR_ROOT)");
cmake_set(*of, "VERILATOR_SOLVER",
V3OutFormatter::quoteNameControls(V3Options::getenvVERILATOR_SOLVER()), "STRING",
"Default SMT solver for constrained randomization (from $VERILATOR_SOLVER)");
*of << "\n### Compiler flags...\n";
@@ -160,6 +163,9 @@ class CMakeEmitter final {
if (v3Global.usesTiming()) {
global.emplace_back("${VERILATOR_ROOT}/include/verilated_timing.cpp");
}
if (v3Global.useRandomizeMethods()) {
global.emplace_back("${VERILATOR_ROOT}/include/verilated_random.cpp");
}
global.emplace_back("${VERILATOR_ROOT}/include/verilated_threads.cpp");
if (v3Global.opt.usesProfiler()) {
global.emplace_back("${VERILATOR_ROOT}/include/verilated_profiler.cpp");
+6
View File
@@ -101,6 +101,12 @@ class EmitCModel final : public EmitCFunc {
puts("\n");
ofp()->putsPrivate(false); // public:
puts("\n// CONSTEXPR CAPABILITIES\n");
puts("// Verilated with --trace?\n");
puts("static constexpr bool traceCapable = "s
+ (v3Global.opt.trace() ? "true;\n"s : "false;\n"s));
// User accessible IO
puts("\n// PORTS\n"
"// The application code writes and reads these signals to\n"
+6
View File
@@ -106,6 +106,8 @@ public:
}
if (v3Global.usesProbDist()) putMakeClassEntry(of, "verilated_probdist.cpp");
if (v3Global.usesTiming()) putMakeClassEntry(of, "verilated_timing.cpp");
if (v3Global.useRandomizeMethods())
putMakeClassEntry(of, "verilated_random.cpp");
putMakeClassEntry(of, "verilated_threads.cpp");
if (v3Global.opt.usesProfiler()) {
putMakeClassEntry(of, "verilated_profiler.cpp");
@@ -187,6 +189,10 @@ public:
of.puts("# User CFLAGS (from -CFLAGS on Verilator command line)\n");
of.puts("VM_USER_CFLAGS = \\\n");
const std::string solver = V3Options::getenvVERILATOR_SOLVER();
if (v3Global.useRandomizeMethods() && solver != "")
of.puts("\t-DVM_SOLVER_DEFAULT='\"" + V3OutFormatter::quoteNameControls(solver)
+ "\"' \\\n");
if (!v3Global.opt.libCreate().empty()) of.puts("\t-fPIC \\\n");
const V3StringList& cFlags = v3Global.opt.cFlags();
for (const string& i : cFlags) of.puts("\t" + i + " \\\n");
+1 -1
View File
@@ -608,8 +608,8 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
puts(";\n");
}
void visit(AstBasicDType* nodep) override {
if (nodep->isSigned()) putfs(nodep, "signed ");
putfs(nodep, nodep->prettyName());
if (nodep->isSigned()) putfs(nodep, " signed");
if (nodep->rangep()) {
puts(" ");
iterateAndNextConstNull(nodep->rangep());
+2 -1
View File
@@ -121,6 +121,7 @@ public:
MULTITOP, // Multiple top level modules
NEWERSTD, // Newer language standard required
NOLATCH, // No latch detected in always_latch block
NONSTD, // Non-standard feature present in other sims
NULLPORT, // Null port detected in module definition
PINCONNECTEMPTY,// Cell pin connected by name with empty reference
PINMISSING, // Cell pin not specified
@@ -204,7 +205,7 @@ public:
"IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE",
"INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE",
"LATCH", "LITENDIAN", "MINTYPMAXDLY", "MISINDENT", "MODDUP",
"MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOLATCH", "NULLPORT", "PINCONNECTEMPTY",
"MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOLATCH", "NONSTD", "NULLPORT", "PINCONNECTEMPTY",
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PREPROCZERO", "PROCASSWIRE",
"PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY",
"SELRANGE", "SHORTREAL", "SIDEEFFECT", "SPLITVAR",
+6 -17
View File
@@ -64,15 +64,6 @@ class ForceConvertVisitor final : public VNVisitor {
m_rdVarp->addNext(m_enVarp);
m_rdVarp->addNext(m_valVarp);
varp->addNextHere(m_rdVarp);
if (varp->isPrimaryIO()) {
varp->v3warn(
E_UNSUPPORTED,
"Unsupported: Force/Release on primary input/output net "
<< varp->prettyNameQ() << "\n"
<< varp->warnMore()
<< "... Suggest assign it to/from a temporary net and force/release that");
}
}
};
@@ -173,13 +164,12 @@ class ForceConvertVisitor final : public VNVisitor {
void visit(AstAssignForce* nodep) override {
// The AstAssignForce node will be removed for sure
VNRelinker relinker;
nodep->unlinkFrBack(&relinker);
pushDeletep(nodep);
FileLine* const flp = nodep->fileline();
AstNodeExpr* const lhsp = nodep->lhsp(); // The LValue we are forcing
AstNodeExpr* const rhsp = nodep->rhsp(); // The value we are forcing it to
VNRelinker relinker;
nodep->unlinkFrBack(&relinker);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
// Set corresponding enable signals to ones
V3Number ones{lhsp, isRangedDType(lhsp) ? lhsp->width() : 1};
@@ -208,13 +198,12 @@ class ForceConvertVisitor final : public VNVisitor {
}
void visit(AstRelease* nodep) override {
FileLine* const flp = nodep->fileline();
AstNodeExpr* const lhsp = nodep->lhsp(); // The LValue we are releasing
// The AstRelease node will be removed for sure
VNRelinker relinker;
nodep->unlinkFrBack(&relinker);
pushDeletep(nodep);
FileLine* const flp = nodep->fileline();
AstNodeExpr* const lhsp = nodep->lhsp(); // The LValue we are releasing
VL_DO_DANGLING(pushDeletep(nodep), nodep);
// Set corresponding enable signals to zero
V3Number zero{lhsp, isRangedDType(lhsp) ? lhsp->width() : 1};
+1
View File
@@ -616,6 +616,7 @@ class ForkVisitor final : public VNVisitor {
if (m_forkDepth && !nodep->varp()->isFuncLocal() && nodep->varp()->isClassMember()) return;
if (m_forkDepth && (m_forkLocalsp.count(nodep->varp()) == 0)
&& nodep->varp()->varType() != VVarType::PORT // Basically static, so it's safe
&& !nodep->varp()->lifetime().isStatic()) {
if (nodep->access().isWriteOrRW()
&& (!nodep->isClassHandleValue() || nodep->user2())) {
+9 -2
View File
@@ -425,8 +425,8 @@ class GateClkDecomp final {
void visit(GateVarVertex* vVtxp, int offset) {
AstVarScope* const vscp = vVtxp->varScp();
// Can't propagate if this variable is forceable
if (vscp->varp()->isForceable()) return;
// Can't propagate if this variable might be forced
if (vscp->varp()->isForced()) return;
// Check that we haven't been here before
if (vscp->user2SetOnce()) return;
@@ -656,6 +656,9 @@ public:
return m_substitutionp;
}
const std::vector<AstVarScope*>& readVscps() const { return m_readVscps; }
bool varAssigned(const AstVarScope* scopep) const {
return m_lhsVarRef && (m_lhsVarRef->varScopep() == scopep);
}
};
//######################################################################
@@ -772,6 +775,8 @@ class GateInline final {
// Was it ok?
if (!okVisitor.isSimple()) continue;
// If the varScope is already removed from logicp, no need to try substitution.
if (!okVisitor.varAssigned(vVtxp->varScp())) continue;
// Does it read multiple source variables?
if (okVisitor.readVscps().size() > 1) {
@@ -822,6 +827,8 @@ class GateInline final {
if (debug() >= 9) dstVtxp->nodep()->dumpTree(" inside: ");
UASSERT_OBJ(logicp != dstVtxp->nodep(), logicp,
"Circular logic should have been rejected by okVisitor");
recordSubstitution(vscp, substp, dstVtxp->nodep());
// If the new replacement referred to a signal,
+2 -4
View File
@@ -98,8 +98,6 @@ static void V3HierWriteCommonInputs(const V3HierBlock* hblockp, std::ostream* of
if (hblockp) topModuleFile = hblockp->vFileIfNecessary();
if (!forCMake) {
if (!topModuleFile.empty()) *of << topModuleFile << "\n";
const V3StringList& vFiles = v3Global.opt.vFiles();
for (const string& i : vFiles) *of << i << "\n";
}
const V3StringSet& libraryFiles = v3Global.opt.libraryFiles();
for (const string& i : libraryFiles) {
@@ -227,7 +225,7 @@ void V3HierBlock::writeCommandArgsFile(bool forCMake) const {
for (const string& opt : commandOpts) *of << opt << "\n";
*of << hierBlockArgs().front() << "\n";
for (const auto& hierblockp : m_children) *of << hierblockp->hierBlockArgs().front() << "\n";
*of << v3Global.opt.allArgsStringForHierBlock(false) << "\n";
*of << v3Global.opt.allArgsStringForHierBlock(false, forCMake) << "\n";
}
string V3HierBlock::commandArgsFileName(bool forCMake) const {
@@ -424,7 +422,7 @@ void V3HierBlockPlan::writeCommandArgsFiles(bool forCMake) const {
}
*of << "--threads " << cvtToStr(v3Global.opt.threads()) << "\n";
*of << (v3Global.opt.systemC() ? "--sc" : "--cc") << "\n";
*of << v3Global.opt.allArgsStringForHierBlock(true) << "\n";
*of << v3Global.opt.allArgsStringForHierBlock(true, forCMake) << "\n";
}
string V3HierBlockPlan::topCommandArgsFileName(bool forCMake) {
+13 -3
View File
@@ -320,8 +320,16 @@ class InlineRelinkVisitor final : public VNVisitor {
// module, so a AstVarRef not AstVarXRef below
exprvarrefp = exprvarrefp->cloneTree(false);
exprvarrefp->access(VAccess::READ);
m_modp->addStmtsp(new AstAssignAlias{
flp, new AstVarRef{flp, nodep, VAccess::WRITE}, exprvarrefp});
AstVarRef* const nodeVarRefp = new AstVarRef{flp, nodep, VAccess::WRITE};
if (nodep->isForced() && nodep->direction() == VDirection::INPUT) {
m_modp->addStmtsp(new AstAssignW{flp, nodeVarRefp, exprvarrefp});
} else if (nodep->isForced() && nodep->direction() == VDirection::OUTPUT) {
exprvarrefp->access(VAccess::WRITE);
nodeVarRefp->access(VAccess::READ);
m_modp->addStmtsp(new AstAssignW{flp, exprvarrefp, nodeVarRefp});
} else {
m_modp->addStmtsp(new AstAssignAlias{flp, nodeVarRefp, exprvarrefp});
}
FileLine* const flbp = exprvarrefp->varp()->fileline();
flp->modifyStateInherit(flbp);
flbp->modifyStateInherit(flp);
@@ -370,7 +378,9 @@ class InlineRelinkVisitor final : public VNVisitor {
if (nodep->varp()->user2p() // It's being converted to an alias.
&& !nodep->varp()->user3()
// Don't constant propagate aliases (we just made)
&& !VN_IS(nodep->backp(), AssignAlias)) {
&& !VN_IS(nodep->backp(), AssignAlias)
// Forced signals do not use aliases
&& !nodep->varp()->isForced()) {
AstVar* const varp = nodep->varp();
if (AstConst* const constp = VN_CAST(varp->user2p(), Const)) {
nodep->replaceWith(constp->cloneTree(false));
+1 -1
View File
@@ -463,7 +463,7 @@ private:
pushDeletep(pinVarp);
} // else pinVarp already unlinked when another instance did this step
nodep->replaceWith(prevPinp);
pushDeletep(nodep);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
}
+1 -4
View File
@@ -53,10 +53,7 @@ public:
~LifeState() {
V3Stats::addStatSum("Optimizations, Lifetime assign deletions", m_statAssnDel);
V3Stats::addStatSum("Optimizations, Lifetime constant prop", m_statAssnCon);
for (AstNode* ip : m_unlinkps) {
ip->unlinkFrBack();
ip->deleteTree();
}
for (AstNode* ip : m_unlinkps) VL_DO_DANGLING(ip->unlinkFrBack()->deleteTree(), ip);
}
// METHODS
void pushUnlinkDeletep(AstNode* nodep) { m_unlinkps.push_back(nodep); }
+1 -1
View File
@@ -273,7 +273,7 @@ class LinkCellsVisitor final : public VNVisitor {
iterateAndNextNull(cellsp);
}
}
pushDeletep(nodep->unlinkFrBack());
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
}
void visit(AstCell* nodep) override {
+78 -10
View File
@@ -1211,6 +1211,23 @@ class LinkDotFindVisitor final : public VNVisitor {
foundp = m_modSymp; // Conflicts with modname?
}
AstVar* const findvarp = foundp ? VN_CAST(foundp->nodep(), Var) : nullptr;
// clocking items can have duplicate names (inout)
if (findvarp && VN_IS(findvarp->backp(), ClockingItem)
&& VN_IS(nodep->backp(), ClockingItem)) {
AstClockingItem* const itemp = VN_AS(nodep->backp(), ClockingItem);
AstClockingItem* const finditemp = VN_AS(findvarp->backp(), ClockingItem);
UINFO(4, "ClockCompl: " << itemp << " ;; " << finditemp << endl);
UINFO(4, "ClockCompV: " << nodep << " ;; " << findvarp << endl);
if (*itemp->exprp()->fileline() == *finditemp->exprp()->fileline()) {
UASSERT_OBJ(finditemp->direction() == VDirection::INPUT
&& itemp->direction() == VDirection::OUTPUT,
itemp, "Input after output?");
// pretend nothing found and rename
foundp = nullptr;
nodep->name("__Voutput_" + nodep->name());
finditemp->outputp(itemp);
}
}
bool ins = false;
if (!foundp) {
ins = true;
@@ -1504,11 +1521,11 @@ class LinkDotFindVisitor final : public VNVisitor {
VL_DO_DANGLING(argp->unlinkFrBackWithNext()->deleteTree(), argp);
}
// Type depends on the method used, let V3Width figure it out later
if (nodep->exprp()) { // Else empty expression and pretend no "with"
if (nodep->exprsp()) { // Else empty expression and pretend no "with"
const auto indexArgRefp = new AstLambdaArgRef{argFl, name + "__DOT__index", true};
const auto valueArgRefp = new AstLambdaArgRef{argFl, name, false};
const auto newp = new AstWith{nodep->fileline(), indexArgRefp, valueArgRefp,
nodep->exprp()->unlinkFrBackWithNext()};
nodep->exprsp()->unlinkFrBackWithNext()};
funcrefp->addPinsp(newp);
}
nodep->replaceWith(funcrefp->unlinkFrBack());
@@ -1528,6 +1545,7 @@ class LinkDotFindVisitor final : public VNVisitor {
// Insert argref's name into symbol table
m_statep->insertSym(m_curSymp, nodep->valueArgRefp()->name(), nodep->valueArgRefp(),
nullptr);
iterateChildren(nodep);
}
}
@@ -2181,6 +2199,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
AstVar* const eventp = new AstVar{
clockingp->fileline(), VVarType::MODULETEMP, clockingp->name(), VFlagChildDType{},
new AstBasicDType{clockingp->fileline(), VBasicDTypeKwd::EVENT}};
eventp->lifetime(VLifetime::STATIC);
clockingp->eventp(eventp);
// Trigger the clocking event in Observed (IEEE 1800-2023 14.13)
clockingp->addNextHere(new AstAlwaysObserved{
@@ -2195,16 +2214,21 @@ class LinkDotResolveVisitor final : public VNVisitor {
return reinterpret_cast<VSymEnt*>(clockingp->eventp()->user1p());
}
bool isParamedClassRefDType(const AstNode* classp) {
while (const AstRefDType* const refp = VN_CAST(classp, RefDType))
classp = refp->subDTypep();
return (VN_IS(classp, ClassRefDType) && VN_AS(classp, ClassRefDType)->paramsp())
|| VN_IS(classp, ParamTypeDType);
}
bool isParamedClassRef(const AstNode* nodep) {
// Is this a parameterized reference to a class, or a reference to class parameter
if (const auto* classRefp = VN_CAST(nodep, ClassOrPackageRef)) {
if (classRefp->paramsp()) return true;
const auto* classp = classRefp->classOrPackageNodep();
while (const auto* typedefp = VN_CAST(classp, Typedef)) classp = typedefp->subDTypep();
return (VN_IS(classp, ClassRefDType) && VN_AS(classp, ClassRefDType)->paramsp())
|| VN_IS(classp, ParamTypeDType);
return isParamedClassRefDType(classp);
}
return false;
return isParamedClassRefDType(nodep);
}
VSymEnt* getThisClassSymp() {
VSymEnt* classSymp = m_ds.m_dotSymp;
@@ -2240,9 +2264,9 @@ class LinkDotResolveVisitor final : public VNVisitor {
<< "... Location of interface class's function\n"
<< interfaceSubp->warnContextSecondary());
}
const auto it = m_ifClassImpNames.find(interfaceSubp->name());
if (!existsInChild && it != m_ifClassImpNames.end()
&& it->second != interfaceSubp) { // Not exact same function from diamond
const auto itn = m_ifClassImpNames.find(interfaceSubp->name());
if (!existsInChild && itn != m_ifClassImpNames.end()
&& itn->second != interfaceSubp) { // Not exact same function from diamond
implementsClassp->v3error(
"Class " << implementsClassp->prettyNameQ() << impOrExtends
<< baseClassp->prettyNameQ()
@@ -2619,7 +2643,16 @@ class LinkDotResolveVisitor final : public VNVisitor {
= VN_AS(m_ds.m_dotp->lhsp(), ClassOrPackageRef);
classOrPackagep = cpackagerefp->classOrPackagep();
UASSERT_OBJ(classOrPackagep, m_ds.m_dotp->lhsp(), "Bad package link");
m_ds.m_dotSymp = m_statep->getNodeSym(classOrPackagep);
if (cpackagerefp->name() == "local::") {
if (m_pinSymp) {
m_ds.m_dotSymp = m_curSymp->fallbackp();
} else {
nodep->v3error("Illegal 'local::' outside 'randomize() with'");
m_ds.m_dotErr = true;
}
} else {
m_ds.m_dotSymp = m_statep->getNodeSym(classOrPackagep);
}
m_ds.m_dotPos = DP_SCOPE;
} else if (m_ds.m_dotPos == DP_SCOPE) {
// {a}.{b}, where {a} maybe a module name
@@ -3078,8 +3111,42 @@ class LinkDotResolveVisitor final : public VNVisitor {
void visit(AstMethodCall* nodep) override {
// Created here so should already be resolved.
VL_RESTORER(m_ds);
VL_RESTORER(m_pinSymp);
{
m_ds.init(m_curSymp);
if (nodep->name() == "randomize" && VN_IS(nodep->pinsp(), With)) {
const AstNodeDType* fromDtp = nodep->fromp()->dtypep();
if (!fromDtp) {
if (const AstNodeVarRef* const varRefp = VN_CAST(nodep->fromp(), NodeVarRef)) {
fromDtp = varRefp->varp()->subDTypep();
} else if (const AstNodeSel* const selp = VN_CAST(nodep->fromp(), NodeSel)) {
if (const AstNodeVarRef* const varRefp
= VN_CAST(selp->fromp(), NodeVarRef)) {
fromDtp = varRefp->varp()->dtypeSkipRefp()->subDTypep();
}
} else if (const AstNodePreSel* const selp
= VN_CAST(nodep->fromp(), NodePreSel)) {
if (const AstNodeVarRef* const varRefp
= VN_CAST(selp->fromp(), NodeVarRef)) {
fromDtp = varRefp->varp()->dtypeSkipRefp()->subDTypep();
}
}
if (!fromDtp)
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: 'randomize() with' on complex expressions");
}
if (m_statep->forPrimary() && isParamedClassRefDType(fromDtp)) {
m_ds.m_unresolvedClass = true;
} else if (fromDtp) {
const AstClassRefDType* const classDtp
= VN_CAST(fromDtp->skipRefp(), ClassRefDType);
if (!classDtp)
nodep->v3error("'randomize() with' on a non-class-instance "
<< fromDtp->prettyNameQ());
else
m_pinSymp = m_statep->getNodeSym(classDtp->classp());
}
}
iterateChildren(nodep);
}
}
@@ -3095,7 +3162,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
if (nodep->user3SetOnce()) return;
UINFO(8, " " << nodep << endl);
UINFO(8, " " << m_ds.ascii() << endl);
{
if (m_ds.m_dotPos != DP_MEMBER || nodep->name() != "randomize") {
// Visit arguments at the beginning.
// They may be visitted even if the current node can't be linked now.
VL_RESTORER(m_ds);
@@ -3439,6 +3506,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
VL_RESTORER(m_curSymp);
{
m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep);
if (m_pinSymp) m_curSymp->importFromClass(m_statep->symsp(), m_pinSymp);
iterateChildren(nodep);
}
m_ds.m_dotSymp = VL_RESTORER_PREV(m_curSymp);
+2 -1
View File
@@ -341,7 +341,8 @@ class LinkJumpVisitor final : public VNVisitor {
}
// if (debug() >= 9) { UINFO(0, "\n"); blockp->dumpTree("- labeli: "); }
if (!blockp) {
nodep->v3error("disable isn't underneath a begin with name: " << nodep->prettyNameQ());
nodep->v3warn(E_UNSUPPORTED,
"disable isn't underneath a begin with name: " << nodep->prettyNameQ());
} else if (AstBegin* const beginp = VN_CAST(blockp, Begin)) {
// Jump to the end of the named block
AstJumpLabel* const labelp = findAddLabel(beginp, false);
+33 -19
View File
@@ -22,6 +22,8 @@
#include "V3LinkLValue.h"
#include "V3Task.h"
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
@@ -33,6 +35,7 @@ class LinkLValueVisitor final : public VNVisitor {
// STATE
bool m_setContinuously = false; // Set that var has some continuous assignment
bool m_setStrengthSpecified = false; // Set that var has assignment with strength specified.
bool m_setForcedByCode = false; // Set that var is the target of an AstAssignForce/AstRelease
VAccess m_setRefLvalue; // Set VarRefs to lvalues for pin assignments
// VISITs
@@ -40,15 +43,20 @@ class LinkLValueVisitor final : public VNVisitor {
void visit(AstNodeVarRef* nodep) override {
// VarRef: LValue its reference
if (m_setRefLvalue != VAccess::NOCHANGE) nodep->access(m_setRefLvalue);
if (nodep->varp()) {
if (nodep->access().isWriteOrRW() && m_setContinuously) {
if (nodep->varp() && nodep->access().isWriteOrRW()) {
if (m_setContinuously) {
nodep->varp()->isContinuously(true);
// Strength may only be specified in continuous assignment,
// so it is needed to check only if m_setContinuously is true
if (m_setStrengthSpecified) nodep->varp()->hasStrengthAssignment(true);
}
if (nodep->access().isWriteOrRW() && !nodep->varp()->isFuncLocal()
&& nodep->varp()->isReadOnly()) {
if (const AstClockingItem* itemp = VN_CAST(nodep->varp()->backp(), ClockingItem)) {
UINFO(5, "ClkOut " << nodep << endl);
if (itemp->outputp()) nodep->varp(itemp->outputp()->varp());
}
if (m_setForcedByCode) {
nodep->varp()->setForcedByCode();
} else if (!nodep->varp()->isFuncLocal() && nodep->varp()->isReadOnly()) {
nodep->v3warn(ASSIGNIN,
"Assigning to input/const variable: " << nodep->prettyNameQ());
}
@@ -77,7 +85,11 @@ class LinkLValueVisitor final : public VNVisitor {
if (AstAssignW* assignwp = VN_CAST(nodep, AssignW)) {
if (assignwp->strengthSpecp()) m_setStrengthSpecified = true;
}
iterateAndNextNull(nodep->lhsp());
{
VL_RESTORER(m_setForcedByCode);
m_setForcedByCode = VN_IS(nodep, AssignForce);
iterateAndNextNull(nodep->lhsp());
}
m_setRefLvalue = VAccess::NOCHANGE;
m_setContinuously = false;
m_setStrengthSpecified = false;
@@ -87,9 +99,11 @@ class LinkLValueVisitor final : public VNVisitor {
void visit(AstRelease* nodep) override {
VL_RESTORER(m_setRefLvalue);
VL_RESTORER(m_setContinuously);
VL_RESTORER(m_setForcedByCode);
{
m_setRefLvalue = VAccess::WRITE;
m_setContinuously = false;
m_setForcedByCode = true;
iterateAndNextNull(nodep->lhsp());
}
}
@@ -279,23 +293,23 @@ class LinkLValueVisitor final : public VNVisitor {
iterateChildren(nodep);
}
void visit(AstNodeFTaskRef* nodep) override {
AstNode* pinp = nodep->pinsp();
const AstNodeFTask* const taskp = nodep->taskp();
// We'll deal with mismatching pins later
if (!taskp) return;
for (AstNode* stmtp = taskp->stmtsp(); stmtp && pinp; stmtp = stmtp->nextp()) {
if (const AstVar* const portp = VN_CAST(stmtp, Var)) {
if (portp->isIO()) {
if (portp->isWritable()) {
m_setRefLvalue = VAccess::WRITE;
iterate(pinp);
m_setRefLvalue = VAccess::NOCHANGE;
} else {
iterate(pinp);
}
// Advance pin
pinp = pinp->nextp();
}
const V3TaskConnects tconnects
= V3Task::taskConnects(nodep, taskp->stmtsp(), nullptr, false);
for (const auto& tconnect : tconnects) {
const AstVar* const portp = tconnect.first;
const AstArg* const argp = tconnect.second;
if (!argp) continue;
AstNodeExpr* const pinp = argp->exprp();
if (!pinp) continue;
if (portp->isWritable()) {
m_setRefLvalue = VAccess::WRITE;
iterate(pinp);
m_setRefLvalue = VAccess::NOCHANGE;
} else {
iterate(pinp);
}
}
}
+1 -1
View File
@@ -874,7 +874,7 @@ class LinkParseVisitor final : public VNVisitor {
itemp->v3fatalSrc("Incorrect direction");
}
}
pushDeletep(itemp->unlinkFrBack());
VL_DO_DANGLING(pushDeletep(itemp->unlinkFrBack()), itemp);
}
iterateChildren(nodep);
}
+5 -5
View File
@@ -81,7 +81,7 @@ class V3List final {
}
VL_ATTR_ALWINLINE
static void prefetch(T_Base* elementp, T_Base* fallbackp) {
static void prefetch(const T_Base* elementp, const T_Base* fallbackp) {
UDEBUGONLY(UASSERT(fallbackp, "Prefetch fallback pointer must be non nullptr"););
// This compiles to a branchless prefetch with cmove, with the address always valid
VL_PREFETCH_RW(elementp ? elementp : fallbackp);
@@ -104,7 +104,7 @@ class V3List final {
T_Base* m_currp; // Currently iterated element, or 'nullptr' for 'end()' iterator
VL_ATTR_ALWINLINE
SimpleItertatorImpl(T_Base* elementp)
explicit SimpleItertatorImpl(T_Base* elementp)
: m_currp{elementp} {}
VL_ATTR_ALWINLINE
@@ -159,7 +159,7 @@ class V3List final {
ListType& m_list; // The proxied list
UnlinkableProxy(ListType& list)
explicit UnlinkableProxy(ListType& list)
: m_list{list} {}
// Unlinkable iterator class template. This only supports enough for range based for loops.
@@ -179,11 +179,11 @@ class V3List final {
T_Base* m_nextp; // Next element after current, or 'nullptr' for 'end()' iterator
VL_ATTR_ALWINLINE
UnlinkableItertatorImpl(T_Base* elementp)
explicit UnlinkableItertatorImpl(T_Base* elementp)
: m_currp{elementp}
, m_nextp{toLinks(m_currp).m_nextp} {}
VL_ATTR_ALWINLINE
UnlinkableItertatorImpl(std::nullptr_t)
explicit UnlinkableItertatorImpl(std::nullptr_t)
: m_currp{nullptr}
, m_nextp{nullptr} {}
+8 -3
View File
@@ -63,9 +63,14 @@ class LocalizeVisitor final : public VNVisitor {
// METHODS
bool isOptimizable(AstVarScope* nodep) {
return !nodep->user1() || // Not marked as not optimizable, or ...
(nodep->varp()->varType() == VVarType::BLOCKTEMP
&& m_accessors(nodep).size() == 1); // .. a block temp used in a single CFunc
// Don't want to malloc/free the backing store all the time
if (VN_IS(nodep->dtypep(), NBACommitQueueDType)) return false;
return ((!nodep->user1() // Not marked as not optimizable, or ...
// .. a block temp used in a single CFunc
|| (nodep->varp()->varType() == VVarType::BLOCKTEMP
&& m_accessors(nodep).size() == 1))
// and under size limit
&& nodep->varp()->dtypep()->widthTotalBytes() <= v3Global.opt.localizeMaxSize());
}
static bool existsNonLeaf(const std::unordered_set<AstCFunc*>& funcps) {
+1 -1
View File
@@ -1891,7 +1891,7 @@ V3Number& V3Number::opSub(const V3Number& lhs, const V3Number& rhs) {
NUM_ASSERT_OP_ARGS2(lhs, rhs);
NUM_ASSERT_LOGIC_ARGS2(lhs, rhs);
if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX();
V3Number negrhs(&rhs, rhs.width());
V3Number negrhs(&rhs, width());
negrhs.opNegate(rhs);
return opAdd(lhs, negrhs);
}
+31 -8
View File
@@ -67,6 +67,7 @@ public:
using DirMap = std::map<const string, std::set<std::string>>; // Directory listing
// STATE
std::list<string> m_lineArgs; // List of command line argument encountered
std::list<string> m_allArgs; // List of every argument encountered
std::list<string> m_incDirUsers; // Include directories (ordered)
std::set<string> m_incDirUserSet; // Include directories (for removing duplicates)
@@ -393,6 +394,8 @@ void V3Options::addVFile(const string& filename) {
}
void V3Options::addForceInc(const string& filename) { m_forceIncs.push_back(filename); }
void V3Options::addLineArg(const string& arg) { m_impp->m_lineArgs.push_back(arg); }
void V3Options::addArg(const string& arg) { m_impp->m_allArgs.push_back(arg); }
string V3Options::allArgsString() const VL_MT_SAFE {
@@ -405,12 +408,12 @@ string V3Options::allArgsString() const VL_MT_SAFE {
}
// Delete some options for Verilation of the hierarchical blocks.
string V3Options::allArgsStringForHierBlock(bool forTop) const {
string V3Options::allArgsStringForHierBlock(bool forTop, bool forCMake) const {
std::set<string> vFiles;
for (const auto& vFile : m_vFiles) vFiles.insert(vFile);
string out;
for (std::list<string>::const_iterator it = m_impp->m_allArgs.begin();
it != m_impp->m_allArgs.end(); ++it) {
for (std::list<string>::const_iterator it = m_impp->m_lineArgs.begin();
it != m_impp->m_lineArgs.end(); ++it) {
int skip = 0;
if (it->length() >= 2 && (*it)[0] == '-' && (*it)[1] == '-') {
skip = 2;
@@ -426,7 +429,7 @@ string V3Options::allArgsStringForHierBlock(bool forTop) const {
continue;
}
} else { // Not an option
if (vFiles.find(*it) != vFiles.end() // Remove HDL
if ((forCMake && vFiles.find(*it) != vFiles.end()) // Remove HDL
|| m_cppFiles.find(*it) != m_cppFiles.end()) { // Remove C++
continue;
}
@@ -493,7 +496,10 @@ string V3Options::fileExists(const string& filename) {
try {
for (const auto& dirEntry : std::filesystem::directory_iterator(dir.c_str()))
setp->insert(dirEntry.path().filename().string());
} catch (std::filesystem::filesystem_error const& ex) { return ""; }
} catch (std::filesystem::filesystem_error const& ex) {
(void)ex;
return "";
}
#else
if (DIR* const dirp = opendir(dir.c_str())) {
while (struct dirent* direntp = readdir(dirp)) setp->insert(direntp->d_name);
@@ -750,6 +756,18 @@ string V3Options::getenvVERILATOR_ROOT() {
return V3Os::filenameCleanup(var);
}
string V3Options::getenvVERILATOR_SOLVER() {
string var = V3Os::getenvStr("VERILATOR_SOLVER", "");
// Treat compiled-in DEFENV string literals as C-strings to enable
// binary patching for relocatable installs (e.g. conda)
string defenv = string{DEFENV_VERILATOR_SOLVER}.c_str();
if (var == "" && defenv != "") {
var = defenv;
V3Os::setenvStr("VERILATOR_SOLVER", var, "Hardcoded at build time");
}
return var;
}
string V3Options::getStdPackagePath() {
return V3Os::filenameJoin(getenvVERILATOR_ROOT(), "include", "verilated_std.sv");
}
@@ -988,6 +1006,9 @@ string V3Options::argString(int argc, char** argv) {
// V3 Options Parsing
void V3Options::parseOpts(FileLine* fl, int argc, char** argv) VL_MT_DISABLED {
// Save command line options
for (int i = 0; i < argc; ++i) { addLineArg(argv[i]); }
// Parse all options
// Initial entry point from Verilator.cpp
parseOptsList(fl, ".", argc, argv);
@@ -1307,6 +1328,9 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
});
DECL_OPTION("-LDFLAGS", CbVal, callStrSetter(&V3Options::addLdLibs));
DECL_OPTION("-l2-name", Set, &m_l2Name);
DECL_OPTION("-no-l2name", CbCall, [this]() { m_l2Name = ""; }).undocumented(); // Historical
DECL_OPTION("-l2name", CbCall, [this]() { m_l2Name = "v"; }).undocumented(); // Historical
const auto setLang = [this, fl](const char* valp) {
const V3LangCode optval{valp};
if (optval.legal()) {
@@ -1323,9 +1347,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
DECL_OPTION("-language", CbVal, setLang);
DECL_OPTION("-lib-create", Set, &m_libCreate);
DECL_OPTION("-lint-only", OnOff, &m_lintOnly);
DECL_OPTION("-l2-name", Set, &m_l2Name);
DECL_OPTION("-no-l2name", CbCall, [this]() { m_l2Name = ""; }).undocumented(); // Historical
DECL_OPTION("-l2name", CbCall, [this]() { m_l2Name = "v"; }).undocumented(); // Historical
DECL_OPTION("-localize-max-size", Set, &m_localizeMaxSize);
DECL_OPTION("-main-top-name", Set, &m_mainTopName);
DECL_OPTION("-MAKEFLAGS", CbVal, callStrSetter(&V3Options::addMakeFlags));
@@ -1388,6 +1410,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
m_pinsScBigUint = flag;
m_pinsBv = 513;
});
DECL_OPTION("-pins-inout-enables", OnOff, &m_pinsInoutEnables);
DECL_OPTION("-pins-uint8", OnOff, &m_pinsUint8);
DECL_OPTION("-pipe-filter", Set, &m_pipeFilter);
DECL_OPTION("-pp-comments", OnOff, &m_ppComments);
+7 -1
View File
@@ -258,6 +258,7 @@ private:
bool m_main = false; // main switch: --main
bool m_outFormatOk = false; // main switch: --cc, --sc or --sp was specified
bool m_pedantic = false; // main switch: --Wpedantic
bool m_pinsInoutEnables = false;// main switch: --pins-inout-enables
bool m_pinsScUint = false; // main switch: --pins-sc-uint
bool m_pinsScBigUint = false; // main switch: --pins-sc-biguint
bool m_pinsUint8 = false; // main switch: --pins-uint8
@@ -308,6 +309,7 @@ private:
int m_instrCountDpi = 200; // main switch: --instr-count-dpi
bool m_jsonEditNums = true; // main switch: --no-json-edit-nums
bool m_jsonIds = true; // main switch: --no-json-ids
int m_localizeMaxSize = 1024; // main switch: --localize-max-size
VOptionBool m_makeDepend; // main switch: -MMD
int m_maxNumWidth = 65536; // main switch: --max-num-width
int m_moduleRecursion = 100; // main switch: --module-recursion-depth
@@ -398,6 +400,7 @@ private:
private:
// METHODS
void addLineArg(const string& arg);
void addArg(const string& arg);
void addDefine(const string& defline, bool allowPlus) VL_MT_DISABLED;
void addFuture(const string& flag);
@@ -509,6 +512,7 @@ public:
bool outFormatOk() const { return m_outFormatOk; }
bool keepTempFiles() const { return (V3Error::debugDefault() != 0); }
bool pedantic() const { return m_pedantic; }
bool pinsInoutEnables() const { return m_pinsInoutEnables; }
bool pinsScUint() const { return m_pinsScUint; }
bool pinsScBigUint() const { return m_pinsScBigUint; }
bool pinsUint8() const { return m_pinsUint8; }
@@ -544,6 +548,7 @@ public:
int ifDepth() const { return m_ifDepth; }
int inlineMult() const { return m_inlineMult; }
int instrCountDpi() const { return m_instrCountDpi; }
int localizeMaxSize() const { return m_localizeMaxSize; }
bool jsonEditNums() const { return m_jsonEditNums; }
bool jsonIds() const { return m_jsonIds; }
VOptionBool makeDepend() const { return m_makeDepend; }
@@ -695,7 +700,7 @@ public:
string allArgsString() const VL_MT_SAFE; ///< Return all passed arguments as simple string
// Return options for child hierarchical blocks when forTop==false, otherwise returns args for
// the top module.
string allArgsStringForHierBlock(bool forTop) const;
string allArgsStringForHierBlock(bool forTop, bool forCMake) const;
void parseOpts(FileLine* fl, int argc, char** argv) VL_MT_DISABLED;
void parseOptsList(FileLine* fl, const string& optdir, int argc, char** argv) VL_MT_DISABLED;
void parseOptsFile(FileLine* fl, const string& filename, bool rel) VL_MT_DISABLED;
@@ -713,6 +718,7 @@ public:
static string getenvSYSTEMC_INCLUDE();
static string getenvSYSTEMC_LIBDIR();
static string getenvVERILATOR_ROOT();
static string getenvVERILATOR_SOLVER();
static string getStdPackagePath();
static string getSupported(const string& var);
static bool systemCSystemWide();
+2 -1
View File
@@ -110,6 +110,7 @@ class OrderMoveDomScope final {
struct Hash final {
size_t operator()(const DomScopeMapKey& key) const {
// cppcheck-suppress unreadVariable // cppcheck bug
V3Hash hash{reinterpret_cast<uint64_t>(key.m_domainp)};
hash += reinterpret_cast<uint64_t>(key.m_scopep);
return hash.value();
@@ -199,7 +200,7 @@ class OrderMoveGraphSerializer final {
public:
// CONSTRUCTOR
OrderMoveGraphSerializer(OrderMoveGraph& moveGraph) {
explicit OrderMoveGraphSerializer(OrderMoveGraph& moveGraph) {
// Set V3GraphVertex::user() to the number of incoming edges (upstream dependencies)
for (V3GraphVertex& vtx : moveGraph.vertices()) {
const uint32_t nDeps = vtx.inEdges().size();
+2 -21
View File
@@ -86,26 +86,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
// Environment
string V3Os::getenvStr(const string& envvar, const string& defaultValue) {
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 VString::escapeStringForPath(ret);
return VString::escapeStringForPath(VlOs::getenvStr(envvar, defaultValue));
}
void V3Os::setenvStr(const string& envvar, const string& value, const string& why) {
@@ -122,7 +103,7 @@ void V3Os::setenvStr(const string& envvar, const string& value, const string& wh
// setenv() replaced by putenv() in Solaris environment. Prototype is different
// putenv() requires NAME=VALUE format
const string vareq = envvar + "=" + value;
putenv(const_cast<char*>(vareq.c_str()));
putenv(strdup(vareq.c_str())); // will leak if setting the same variable again
#endif
}
+1 -1
View File
@@ -140,7 +140,7 @@ AstNodeDType* V3ParseGrammar::createArray(AstNodeDType* basep, AstNodeRange* nra
arrayp = new AstUnpackArrayDType{rangep->fileline(), VFlagChildDType{}, arrayp,
rangep};
} else if (VN_IS(nrangep, UnsizedRange)) {
arrayp = new AstUnsizedArrayDType{nrangep->fileline(), VFlagChildDType{}, arrayp};
arrayp = new AstDynArrayDType{nrangep->fileline(), VFlagChildDType{}, arrayp};
VL_DO_DANGLING(nrangep->deleteTree(), nrangep);
} else if (VN_IS(nrangep, BracketRange)) {
const AstBracketRange* const arangep = VN_AS(nrangep, BracketRange);
+31 -25
View File
@@ -688,43 +688,43 @@ string V3PreProcImp::defineSubst(VDefineRef* refp) {
{ // Parse substitution define using arguments
string argName;
bool quote = false;
bool triquote = false;
bool backslashesc = false; // In \.....{space} block
// Note we go through the loop once more at the nullptr end-of-string
for (const char* cp = value.c_str(); (*cp) || argName != ""; cp = (*cp ? cp + 1 : cp)) {
// UINFO(4, "CH "<<*cp<<" an "<<argName<<endl);
if (!quote && *cp == '\\') {
if (!quote && !triquote && *cp == '\\') {
backslashesc = true;
} else if (std::isspace(*cp)) {
backslashesc = false;
}
// We don't check for quotes; some simulators expand even inside quotes
if (std::isalpha(*cp) || *cp == '_'
|| *cp == '$' // Won't replace system functions, since no $ in argValueByName
|| (argName != "" && (std::isdigit(*cp) || *cp == '$'))) {
argName += *cp;
continue;
}
if (argName != "") {
// Found a possible variable substitution
const auto iter = argValueByName.find(argName);
if (iter != argValueByName.end()) {
// Substitute
const string subst = iter->second;
if (subst == "") {
// Normally `` is removed later, but with no token after, we're otherwise
// stuck, so remove proceeding ``
if (out.size() >= 2 && out.substr(out.size() - 2) == "``") {
out = out.substr(0, out.size() - 2);
if (!quote && !triquote) {
if (std::isalpha(*cp) || *cp == '_'
|| *cp == '$' // Won't replace system functions, since no $ in argValueByName
|| (argName != "" && (std::isdigit(*cp) || *cp == '$'))) {
argName += *cp;
continue;
}
if (argName != "") {
// Found a possible variable substitution
const auto iter = argValueByName.find(argName);
if (iter != argValueByName.end()) {
// Substitute
const string subst = iter->second;
if (subst == "") {
// Normally `` is removed later, but with no token after, we're
// otherwise stuck, so remove proceeding ``
if (out.size() >= 2 && out.substr(out.size() - 2) == "``") {
out = out.substr(0, out.size() - 2);
}
} else {
out += subst;
}
} else {
out += subst;
out += argName;
}
} else {
out += argName;
argName = "";
}
argName = "";
}
if (!quote) {
// Check for `` only after we've detected end-of-argname
if (cp[0] == '`' && cp[1] == '`') {
if (backslashesc) {
@@ -772,6 +772,12 @@ string V3PreProcImp::defineSubst(VDefineRef* refp) {
out += cp[0];
continue;
}
if (cp[0] == '"' && cp[1] == '"' && cp[2] == '"') {
triquote = !triquote;
out += "\"\"\"";
cp += 2;
continue;
}
if (*cp == '"') quote = !quote;
if (*cp) out += *cp;
}
+3 -1
View File
@@ -86,7 +86,7 @@ class PremitVisitor final : public VNVisitor {
// Extract into constant pool.
const bool merge = v3Global.opt.fMergeConstPool();
varp = v3Global.rootp()->constPoolp()->findConst(constp, merge)->varp();
nodep->deleteTree();
VL_DO_DANGLING(nodep->deleteTree(), nodep);
++m_extractedToConstPool;
} else {
// Keep as local temporary.
@@ -183,9 +183,11 @@ class PremitVisitor final : public VNVisitor {
void visit(AstWhile* nodep) override {
UINFO(4, " WHILE " << nodep << endl);
// cppcheck-suppress shadowVariable // Also restored below
START_STATEMENT_OR_RETURN(nodep);
iterateAndNextNull(nodep->precondsp());
{
// cppcheck-suppress shadowVariable // Also restored above
VL_RESTORER(m_inWhileCondp);
m_inWhileCondp = nodep;
iterateAndNextNull(nodep->condp());
+225 -17
View File
@@ -39,24 +39,28 @@ class RandomizeMarkVisitor final : public VNVisitorConst {
// NODE STATE
// Cleared on Netlist
// AstClass::user1() -> bool. Set true to indicate needs randomize processing
// AstConstraintExpr::user1() -> bool. Set true to indicate state-dependent
// AstNodeExpr::user1() -> bool. Set true to indicate constraint expression depending on a
// randomized variable
const VNUser1InUse m_inuser1;
using DerivedSet = std::unordered_set<AstClass*>;
using BaseToDerivedMap = std::unordered_map<AstClass*, DerivedSet>;
using BaseToDerivedMap = std::unordered_map<const AstClass*, DerivedSet>;
BaseToDerivedMap m_baseToDerivedMap; // Mapping from base classes to classes that extend them
AstClass* m_classp = nullptr; // Current class
AstConstraintExpr* m_constraintExprp = nullptr; // Current constraint expression
// METHODS
void markMembers(AstClass* nodep) {
for (auto* classp = nodep; classp;
void markMembers(const AstClass* nodep) {
for (const AstClass* classp = nodep; classp;
classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr) {
for (auto* memberp = classp->stmtsp(); memberp; memberp = memberp->nextp()) {
for (const AstNode* memberp = classp->stmtsp(); memberp; memberp = memberp->nextp()) {
// If member is rand and of class type, mark its class
if (VN_IS(memberp, Var) && VN_AS(memberp, Var)->isRand()) {
if (const auto* const classRefp
if (const AstClassRefDType* const classRefp
= VN_CAST(memberp->dtypep()->skipRefp(), ClassRefDType)) {
auto* const rclassp = classRefp->classp();
AstClass* const rclassp = classRefp->classp();
if (!rclassp->user1()) {
rclassp->user1(true);
markMembers(rclassp);
@@ -67,11 +71,11 @@ class RandomizeMarkVisitor final : public VNVisitorConst {
}
}
}
void markDerived(AstClass* nodep) {
void markDerived(const AstClass* nodep) {
const auto it = m_baseToDerivedMap.find(nodep);
if (it != m_baseToDerivedMap.end()) {
for (auto* classp : it->second) {
if (!classp->user1p()) {
if (!classp->user1()) {
classp->user1(true);
markMembers(classp);
markDerived(classp);
@@ -92,7 +96,7 @@ class RandomizeMarkVisitor final : public VNVisitorConst {
iterateChildrenConst(nodep);
if (nodep->extendsp()) {
// Save pointer to derived class
AstClass* const basep = nodep->extendsp()->classp();
const AstClass* const basep = nodep->extendsp()->classp();
m_baseToDerivedMap[basep].insert(nodep);
}
}
@@ -111,6 +115,22 @@ class RandomizeMarkVisitor final : public VNVisitorConst {
if (nodep->name() != "randomize") return;
if (m_classp) m_classp->user1(true);
}
void visit(AstConstraintExpr* nodep) override {
VL_RESTORER(m_constraintExprp);
m_constraintExprp = nodep;
iterateChildrenConst(nodep);
}
void visit(AstNodeVarRef* nodep) override {
if (!m_constraintExprp) return;
if (!nodep->varp()->isRand()) {
m_constraintExprp->user1(true);
nodep->v3warn(CONSTRAINTIGN, "State-dependent constraint ignored (unsupported)");
return;
}
for (AstNode* backp = nodep; backp != m_constraintExprp && !backp->user1();
backp = backp->backp())
backp->user1(true);
}
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
@@ -123,6 +143,143 @@ public:
~RandomizeMarkVisitor() override = default;
};
//######################################################################
// Visitor that turns constraints into template strings for solvers
class ConstraintExprVisitor final : public VNVisitor {
// NODE STATE
// AstVar::user4() -> bool. Handled in constraints
// AstNodeExpr::user1() -> bool. Depending on a randomized variable
// VNUser4InUse m_inuser4; (Allocated for use in RandomizeVisitor)
AstTask* const m_taskp; // X_setup_constraint() method of the constraint
AstVar* const m_genp; // VlRandomizer variable of the class
bool editFormat(AstNodeExpr* nodep) {
if (nodep->user1()) return false;
// Replace computable expression with SMT constant
VNRelinker handle;
nodep->unlinkFrBack(&handle);
AstSFormatF* const newp = new AstSFormatF{
nodep->fileline(), (nodep->width() & 3) ? "#b%b" : "#x%x", false, nodep};
handle.relink(newp);
return true;
}
void editSMT(AstNodeExpr* nodep, AstNodeExpr* lhsp = nullptr, AstNodeExpr* rhsp = nullptr) {
// Replace incomputable (result-dependent) expression with SMT expression
std::string smtExpr = nodep->emitSMT(); // Might need child width (AstExtend)
UASSERT_OBJ(smtExpr != "", nodep,
"Node needs randomization constraint, but no emitSMT: " << nodep);
if (lhsp) lhsp = VN_AS(iterateSubtreeReturnEdits(lhsp->unlinkFrBack()), NodeExpr);
if (rhsp) rhsp = VN_AS(iterateSubtreeReturnEdits(rhsp->unlinkFrBack()), NodeExpr);
AstNodeExpr* argsp = nullptr;
for (string::iterator pos = smtExpr.begin(); pos != smtExpr.end(); ++pos) {
if (pos[0] == '%') {
++pos;
switch (pos[0]) {
case '%': break;
case 'l':
pos[0] = '@';
UASSERT_OBJ(lhsp, nodep, "emitSMT() references undef node");
argsp = AstNode::addNext(argsp, lhsp);
lhsp = nullptr;
break;
case 'r':
pos[0] = '@';
UASSERT_OBJ(rhsp, nodep, "emitSMT() references undef node");
argsp = AstNode::addNext(argsp, rhsp);
rhsp = nullptr;
break;
default: nodep->v3fatalSrc("Unknown emitSMT format code: %" << pos[0]); break;
}
}
}
UASSERT_OBJ(!lhsp, nodep, "Missing emitSMT %l for " << lhsp);
UASSERT_OBJ(!rhsp, nodep, "Missing emitSMT %r for " << rhsp);
AstSFormatF* const newp = new AstSFormatF{nodep->fileline(), smtExpr, false, argsp};
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
// VISITORS
void visit(AstNodeVarRef* nodep) override {
if (editFormat(nodep)) return;
// In SMT just variable name, but we also ensure write_var for the variable
const std::string smtName = nodep->name(); // Can be anything unique
nodep->replaceWith(new AstSFormatF{nodep->fileline(), smtName, false, nullptr});
AstVar* const varp = nodep->varp();
VL_DO_DANGLING(pushDeletep(nodep), nodep);
if (!varp->user4()) {
varp->user4(true);
AstCMethodHard* const methodp = new AstCMethodHard{
varp->fileline(), new AstVarRef{varp->fileline(), m_genp, VAccess::READWRITE},
"write_var"};
methodp->dtypeSetVoid();
methodp->addPinsp(new AstVarRef{varp->fileline(), varp, VAccess::WRITE});
methodp->addPinsp(new AstConst{varp->dtypep()->fileline(), AstConst::Unsized64{},
(size_t)varp->width()});
AstNodeExpr* const varnamep
= new AstCExpr{varp->fileline(), "\"" + smtName + "\"", varp->width()};
varnamep->dtypep(varp->dtypep());
methodp->addPinsp(varnamep);
m_taskp->addStmtsp(new AstStmtExpr{varp->fileline(), methodp});
}
}
void visit(AstNodeBiop* nodep) override {
if (editFormat(nodep)) return;
editSMT(nodep, nodep->lhsp(), nodep->rhsp());
}
void visit(AstNodeUniop* nodep) override {
if (editFormat(nodep)) return;
editSMT(nodep, nodep->lhsp());
}
void visit(AstReplicate* nodep) override {
// Biop, but RHS is harmful
if (editFormat(nodep)) return;
editSMT(nodep, nodep->srcp());
}
void visit(AstSFormatF* nodep) override {}
void visit(AstConstraintExpr* nodep) override { iterateChildren(nodep); }
void visit(AstCMethodHard* nodep) override {
if (editFormat(nodep)) return;
UASSERT_OBJ(nodep->name() == "size", nodep, "Non-size method call in constraints");
AstNode* fromp = nodep->fromp();
// Warn early while the dtype is still there
fromp->v3warn(E_UNSUPPORTED, "Unsupported: random member variable with type "
<< fromp->dtypep()->prettyDTypeNameQ());
iterateChildren(nodep); // Might change fromp
fromp = nodep->fromp()->unlinkFrBack();
fromp->dtypep(nodep->dtypep());
nodep->replaceWith(fromp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstNodeExpr* nodep) override {
if (editFormat(nodep)) return;
nodep->v3fatalSrc(
"Visit function missing? Constraint function missing for math node: " << nodep);
}
void visit(AstNode* nodep) override {
nodep->v3fatalSrc(
"Visit function missing? Constraint function missing for node: " << nodep);
}
public:
// CONSTRUCTORS
explicit ConstraintExprVisitor(AstConstraintExpr* nodep, AstTask* taskp, AstVar* genp)
: m_taskp(taskp)
, m_genp(genp) {
iterate(nodep);
}
};
//######################################################################
// Visitor that defines a randomize method where needed
@@ -130,10 +287,15 @@ class RandomizeVisitor final : public VNVisitor {
// NODE STATE
// Cleared on Netlist
// AstClass::user1() -> bool. Set true to indicate needs randomize processing
// AstConstraintExpr::user1() -> bool. Set true to indicate state-dependent
// AstEnumDType::user2() -> AstVar*. Pointer to table with enum values
// AstClass::user3() -> AstFunc*. Pointer to randomize() method of a class
// AstVar::user4() -> bool. Handled in constraints
// AstClass::user4() -> AstVar*. Constrained randomizer variable
// VNUser1InUse m_inuser1; (Allocated for use in RandomizeMarkVisitor)
const VNUser2InUse m_inuser2;
const VNUser3InUse m_inuser3;
const VNUser4InUse m_inuser4;
// STATE
VMemberMap m_memberMap; // Member names cached for fast lookup
@@ -147,9 +309,9 @@ class RandomizeVisitor final : public VNVisitor {
AstVar* enumValueTabp(AstEnumDType* nodep) {
if (nodep->user2p()) return VN_AS(nodep->user2p(), Var);
UINFO(9, "Construct Venumvaltab " << nodep << endl);
AstNodeArrayDType* const vardtypep
= new AstUnpackArrayDType{nodep->fileline(), nodep->dtypep(),
new AstRange{nodep->fileline(), nodep->itemCount(), 0}};
AstNodeArrayDType* const vardtypep = new AstUnpackArrayDType{
nodep->fileline(), nodep->dtypep(),
new AstRange{nodep->fileline(), static_cast<int>(nodep->itemCount()), 0}};
AstInitArray* const initp = new AstInitArray{nodep->fileline(), vardtypep, nullptr};
v3Global.rootp()->typeTablep()->addTypesp(vardtypep);
AstVar* const varp
@@ -274,6 +436,12 @@ class RandomizeVisitor final : public VNVisitor {
funcp->addStmtsp(callp->makeStmt());
}
}
AstTask* newSetupConstraintTask(AstClass* nodep, const std::string& name) {
AstTask* const taskp = new AstTask{nodep->fileline(), name + "_setup_constraint", nullptr};
taskp->classMethod(true);
nodep->addMembersp(taskp);
return taskp;
}
// VISITORS
void visit(AstNodeModule* nodep) override {
@@ -315,20 +483,28 @@ class RandomizeVisitor final : public VNVisitor {
beginValp = baseRandCallp;
}
}
if (m_modp->user4p()) {
AstNode* const argsp = new AstVarRef{nodep->fileline(), VN_AS(m_modp->user4p(), Var),
VAccess::READWRITE};
argsp->addNext(new AstText{fl, ".next(__Vm_rng)"});
AstNodeExpr* const solverCallp = new AstCExpr{fl, argsp};
solverCallp->dtypeSetBit();
beginValp = beginValp ? new AstAnd{fl, beginValp, solverCallp} : solverCallp;
}
if (!beginValp) beginValp = new AstConst{fl, AstConst::WidthedValue{}, 32, 1};
funcp->addStmtsp(new AstAssign{fl, new AstVarRef{fl, fvarp, VAccess::WRITE}, beginValp});
for (auto* memberp = nodep->stmtsp(); memberp; memberp = memberp->nextp()) {
for (AstNode* memberp = nodep->stmtsp(); memberp; memberp = memberp->nextp()) {
AstVar* const memberVarp = VN_CAST(memberp, Var);
if (!memberVarp || !memberVarp->isRand()) continue;
if (!memberVarp || !memberVarp->isRand() || memberVarp->user4()) continue;
const AstNodeDType* const dtypep = memberp->dtypep()->skipRefp();
if (VN_IS(dtypep, BasicDType) || VN_IS(dtypep, StructDType)) {
AstVar* const randcVarp = newRandcVarsp(memberVarp);
AstVarRef* const refp = new AstVarRef{fl, memberVarp, VAccess::WRITE};
AstNodeStmt* const stmtp = newRandStmtsp(fl, refp, randcVarp);
funcp->addStmtsp(stmtp);
} else if (const auto* const classRefp = VN_CAST(dtypep, ClassRefDType)) {
} else if (const AstClassRefDType* const classRefp = VN_CAST(dtypep, ClassRefDType)) {
if (classRefp->classp() == nodep) {
memberp->v3warn(
E_UNSUPPORTED,
@@ -358,8 +534,40 @@ class RandomizeVisitor final : public VNVisitor {
nodep->user1(false);
}
void visit(AstConstraint* nodep) override {
nodep->v3warn(CONSTRAINTIGN, "Constraint ignored (unsupported)");
if (!v3Global.opt.xmlOnly()) VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
AstNodeFTask* const newp = VN_AS(m_memberMap.findMember(m_modp, "new"), NodeFTask);
UASSERT_OBJ(newp, m_modp, "No new() in class");
AstTask* const taskp = newSetupConstraintTask(VN_AS(m_modp, Class), nodep->name());
AstTaskRef* const setupTaskRefp
= new AstTaskRef{nodep->fileline(), taskp->name(), nullptr};
setupTaskRefp->taskp(taskp);
newp->addStmtsp(new AstStmtExpr{nodep->fileline(), setupTaskRefp});
AstVar* genp = VN_AS(m_modp->user4p(), Var);
if (!genp) {
genp = new AstVar(nodep->fileline(), VVarType::MEMBER, "constraint",
m_modp->findBasicDType(VBasicDTypeKwd::RANDOM_GENERATOR));
VN_AS(m_modp, Class)->addMembersp(genp);
m_modp->user4p(genp);
}
while (nodep->itemsp()) {
AstConstraintExpr* const condsp = VN_CAST(nodep->itemsp(), ConstraintExpr);
if (!condsp || condsp->user1()) {
nodep->itemsp()->v3warn(CONSTRAINTIGN,
"Constraint expression ignored (unsupported)");
pushDeletep(nodep->itemsp()->unlinkFrBack());
continue;
}
{ ConstraintExprVisitor{condsp->unlinkFrBack(), taskp, genp}; }
// Only hard constraints are now supported
AstCMethodHard* const methodp = new AstCMethodHard{
condsp->fileline(), new AstVarRef{condsp->fileline(), genp, VAccess::READWRITE},
"hard", condsp->exprp()->unlinkFrBack()};
methodp->dtypeSetVoid();
taskp->addStmtsp(new AstStmtExpr{condsp->fileline(), methodp});
VL_DO_DANGLING(condsp->deleteTree(), condsp);
}
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
}
void visit(AstRandCase* nodep) override {
// RANDCASE
+9
View File
@@ -283,6 +283,7 @@ void transformForks(AstNetlist* const netlistp) {
// STATE
bool m_inClass = false; // Are we in a class?
bool m_beginHasAwaits = false; // Does the current begin have awaits?
bool m_awaitMoved = false; // Has the current function lost awaits?
AstFork* m_forkp = nullptr; // Current fork
AstCFunc* m_funcp = nullptr; // Current function
@@ -344,7 +345,13 @@ void transformForks(AstNetlist* const netlistp) {
}
void visit(AstCFunc* nodep) override {
m_funcp = nodep;
m_awaitMoved = false;
iterateChildren(nodep);
if (nodep->isCoroutine() && m_awaitMoved
&& !nodep->stmtsp()->exists([](AstCAwait*) { return true; })) {
// co_return at the end (either that or a co_await is required in a coroutine
nodep->addStmtsp(new AstCStmt{nodep->fileline(), "co_return;\n"});
}
m_funcp = nullptr;
}
void visit(AstVar* nodep) override {
@@ -399,6 +406,8 @@ void transformForks(AstNetlist* const netlistp) {
if (!m_beginHasAwaits) {
// co_return at the end (either that or a co_await is required in a coroutine
newfuncp->addStmtsp(new AstCStmt{nodep->fileline(), "co_return;\n"});
} else {
m_awaitMoved = true;
}
remapLocals(newfuncp, callp);
} else {
+2 -2
View File
@@ -128,11 +128,11 @@ private:
if (writesToVirtIface(nodep)) {
// Convert to always, as we have to assign the trigger var
FileLine* const flp = nodep->fileline();
AstAlwaysPost* const postp = new AstAlwaysPost{flp, nullptr, nullptr};
AstAlwaysPost* const postp = new AstAlwaysPost{flp};
nodep->replaceWith(postp);
postp->addStmtsp(
new AstAssign{flp, nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack()});
nodep->deleteTree();
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
}
void visit(AstNodeIf* nodep) override {
+3 -1
View File
@@ -167,7 +167,9 @@ class SenExprBuilder final {
case VEdgeType::ET_CHANGED:
case VEdgeType::ET_HYBRID: //
if (VN_IS(senp->dtypep()->skipRefp(), UnpackArrayDType)) {
AstCMethodHard* const resultp = new AstCMethodHard{flp, currp(), "neq", prevp()};
// operand order reversed to avoid calling neq() method on non-VlUnpacked type, see
// issue #5125
AstCMethodHard* const resultp = new AstCMethodHard{flp, prevp(), "neq", currp()};
resultp->dtypeSetBit();
return {resultp, true};
}
+1 -1
View File
@@ -200,7 +200,7 @@ class SliceVisitor final : public VNVisitor {
leOffset};
} else if (VN_IS(nodep, ArraySel) || VN_IS(nodep, NodeVarRef) || VN_IS(nodep, NodeSel)
|| VN_IS(nodep, CMethodHard) || VN_IS(nodep, MemberSel)
|| VN_IS(nodep, ExprStmt)) {
|| VN_IS(nodep, ExprStmt) || VN_IS(nodep, StructSel)) {
UINFO(9, " cloneSel(" << elements << "," << elemIdx << ") " << nodep << endl);
const int leOffset = !arrayp->rangep()->ascending()
? arrayp->rangep()->elementsConst() - 1 - elemIdx
+1 -1
View File
@@ -819,7 +819,7 @@ class RemovePlaceholdersVisitor final : public VNVisitor {
}
}
if (emptyOrCommentOnly) {
pushDeletep(nodep->unlinkFrBack());
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
++m_emptyAlways;
}
}
+1 -2
View File
@@ -93,8 +93,7 @@ class SplitAsCleanVisitor final : public VNVisitor {
m_keepStmt = true;
} else {
UINFO(6, " Delete STMT " << nodep << endl);
nodep->unlinkFrBack();
pushDeletep(nodep);
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
}
}
// If something below matches, the upper statement remains too.
+6 -5
View File
@@ -79,7 +79,7 @@ public:
: m_fl{fl} {}
~TableBuilder() {
if (m_initp) m_initp->deleteTree();
if (m_initp) VL_DO_DANGLING(m_initp->deleteTree(), m_initp);
}
void setTableSize(AstNodeDType* elemDType, unsigned size) {
@@ -89,16 +89,17 @@ public:
// Create data type
const int width = elemDType->width();
AstNodeDType* const subDTypep
= elemDType->isString()
= elemDType->isString() || elemDType->isDouble()
? elemDType
: v3Global.rootp()->findBitDType(width, width, VSigning::UNSIGNED);
AstUnpackArrayDType* const tableDTypep = new AstUnpackArrayDType{
m_fl, subDTypep, new AstRange{m_fl, static_cast<int>(size), 0}};
v3Global.rootp()->typeTablep()->addTypesp(tableDTypep);
// Create table initializer (with default value 0)
AstConst* const defaultp = elemDType->isString()
? new AstConst{m_fl, AstConst::String{}, ""}
: new AstConst{m_fl, AstConst::WidthedValue{}, width, 0};
AstConst* const defaultp
= elemDType->isString() ? new AstConst{m_fl, AstConst::String{}, ""}
: elemDType->isDouble() ? new AstConst{m_fl, AstConst::RealDouble{}, 0.0}
: new AstConst{m_fl, AstConst::WidthedValue{}, width, 0};
m_initp = new AstInitArray{m_fl, tableDTypep, defaultp};
}
+32 -17
View File
@@ -305,6 +305,9 @@ struct TaskDpiUtils final {
if (portp->basicp() && portp->basicp()->keyword() == VBasicDTypeKwd::CHANDLE) {
frstmt = "VL_CVT_VP_Q(" + frName;
ket = ")";
} else if (portp->basicp() && portp->basicp()->keyword() == VBasicDTypeKwd::STRING) {
frstmt = "VL_CVT_N_CSTR(" + frName;
ket = ")";
} else if ((portp->basicp() && portp->basicp()->isDpiPrimitive())) {
frstmt = frName;
} else {
@@ -694,7 +697,7 @@ class TaskVisitor final : public VNVisitor {
// differ we may get C compilation problems later
const std::string dpiType = portp->dpiArgType(false, false);
dpiproto += dpiType;
const std::string vType = portp->dtypep()->prettyDTypeName();
const std::string vType = portp->dtypep()->prettyDTypeName(false);
if (!portp->isDpiOpenArray() && dpiType != vType) {
dpiproto += " /* " + vType + " */ ";
}
@@ -1589,14 +1592,13 @@ public:
const char* const V3Task::s_dpiTemporaryVarSuffix = "__Vcvt";
V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp,
V3TaskConnectState* statep) {
V3TaskConnectState* statep, bool makeChanges) {
// Output list will be in order of the port declaration variables (so
// func calls are made right in C)
// Missing pin/expr? We return (pinvar, nullptr)
// Extra pin/expr? We clean it up
UINFO(9, "taskConnects " << nodep << endl);
std::map<const std::string, int> nameToIndex;
std::set<const AstVar*> argWrap; // Which ports are defaulted, forcing arg wrapper creation
V3TaskConnects tconnects;
UASSERT_OBJ(nodep->taskp(), nodep, "unlinked");
@@ -1608,7 +1610,7 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp,
if (portp->isIO()) {
tconnects.emplace_back(portp, static_cast<AstArg*>(nullptr));
nameToIndex.emplace(portp->name(), tpinnum); // For name based connections
tpinnum++;
++tpinnum;
if (portp->attrSFormat()) {
sformatp = portp;
} else if (sformatp) {
@@ -1630,17 +1632,19 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp,
// By name
const auto it = nameToIndex.find(argp->name());
if (it == nameToIndex.end()) {
pinp->v3error("No such argument " << argp->prettyNameQ() << " in function call to "
<< nodep->taskp()->prettyTypeName());
// We'll just delete it; seems less error prone than making a false argument
VL_DO_DANGLING(pinp->unlinkFrBack()->deleteTree(), pinp);
if (makeChanges) {
pinp->v3error("No such argument " << argp->prettyNameQ()
<< " in function call to "
<< nodep->taskp()->prettyTypeName());
// We'll just delete it; seems less error prone than making a false argument
VL_DO_DANGLING(pinp->unlinkFrBack()->deleteTree(), pinp);
}
} else {
if (tconnects[it->second].second) {
if (tconnects[it->second].second && makeChanges) {
pinp->v3error("Duplicate argument " << argp->prettyNameQ()
<< " in function call to "
<< nodep->taskp()->prettyTypeName());
}
argp->name(""); // Can forget name as will add back in pin order
tconnects[it->second].second = argp;
reorganize = true;
}
@@ -1649,8 +1653,8 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp,
if (sformatp) {
tconnects.emplace_back(sformatp, static_cast<AstArg*>(nullptr));
tconnects[ppinnum].second = argp;
tpinnum++;
} else {
++tpinnum;
} else if (makeChanges) {
pinp->v3error("Too many arguments in function call to "
<< nodep->taskp()->prettyTypeName());
// We'll just delete it; seems less error prone than making a false argument
@@ -1660,10 +1664,13 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp,
tconnects[ppinnum].second = argp;
}
}
ppinnum++;
++ppinnum;
}
if (!makeChanges) return tconnects;
// Connect missing ones
std::set<const AstVar*> argWrap; // Which ports are defaulted, forcing arg wrapper creation
for (int i = 0; i < tpinnum; ++i) {
AstVar* const portp = tconnects[i].first;
if (!tconnects[i].second || !tconnects[i].second->exprp()) {
@@ -1723,6 +1730,11 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp,
}
}
for (const auto& tconnect : tconnects) {
AstArg* const argp = tconnect.second;
argp->name(""); // Can forget name as will add back in pin order
}
if (reorganize) {
// To simplify downstream, put argument list back into pure pinnumber ordering
while (nodep->pinsp()) {
@@ -1839,10 +1851,13 @@ AstNodeFTask* V3Task::taskConnectWrapNew(AstNodeFTask* taskp, const string& newn
newPortp->funcLocal(true);
newTaskp->addStmtsp(newPortp);
// Runtime-assign it to the default
AstAssign* const newAssignp = new AstAssign{
valuep->fileline(), new AstVarRef{valuep->fileline(), newPortp, VAccess::WRITE},
valuep->cloneTree(true)};
newTaskp->addStmtsp(newAssignp);
if (!VN_IS(valuep, EmptyQueue)) {
AstAssign* const newAssignp
= new AstAssign{valuep->fileline(),
new AstVarRef{valuep->fileline(), newPortp, VAccess::WRITE},
valuep->cloneTree(true)};
newTaskp->addStmtsp(newAssignp);
}
}
oldNewVars.emplace(portp, newPortp);
const VAccess pinAccess = portp->isWritable() ? VAccess::WRITE : VAccess::READ;

Some files were not shown because too many files have changed in this diff Show More