mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 01:54:01 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd693ce02b | ||
|
|
2641bc98aa | ||
|
|
ad2862ce3f | ||
|
|
4695967185 | ||
|
|
f96e99542c | ||
|
|
7de193c2e9 | ||
|
|
442c9bc316 | ||
|
|
3203019408 | ||
|
|
bc853e260b | ||
|
|
d6e5703298 | ||
|
|
a9e50327fd | ||
|
|
6f0a36318e | ||
|
|
221fecad0b | ||
|
|
fd1e4d9e45 | ||
|
|
920ed5ca3d | ||
|
|
2537431273 | ||
|
|
13114f2efe | ||
|
|
e9f59e44a5 | ||
|
|
d4c3e35f97 | ||
|
|
02dd33b60e | ||
|
|
2bc883f3b3 | ||
|
|
29db25b70e | ||
|
|
d6bc0c712a | ||
|
|
962efa3691 | ||
|
|
4babba16d6 | ||
|
|
5363d89870 | ||
|
|
44acb08ea9 | ||
|
|
6584b4d426 | ||
|
|
9ff06c1664 | ||
|
|
1537c9badc | ||
|
|
708f875b31 | ||
|
|
0d3cb86a44 | ||
|
|
5c2b9d55b2 | ||
|
|
1dbf1be3e6 | ||
|
|
3f886f7c61 | ||
|
|
7c9fa8647c | ||
|
|
dbf68a99e8 | ||
|
|
53c2e416f4 | ||
|
|
e834b5be2d | ||
|
|
bd7689f59a | ||
|
|
3496eb80a5 | ||
|
|
839c3bf444 | ||
|
|
d4b3583307 | ||
|
|
ee130cb20d | ||
|
|
552a146f9c | ||
|
|
913679f261 | ||
|
|
d9078df650 | ||
|
|
f84592af49 | ||
|
|
5f85c89425 | ||
|
|
94f789b677 | ||
|
|
739be2f782 | ||
|
|
25b9a16bc7 | ||
|
|
9a8e68928d | ||
|
|
19cccd170e | ||
|
|
d99c8f5d44 | ||
|
|
cf111d2e1f | ||
|
|
45eb5b8a5c | ||
|
|
ce5cad17a8 | ||
|
|
ed01befc25 | ||
|
|
69a2bfee68 | ||
|
|
1cc37ad6ce | ||
|
|
2a9f29912c | ||
|
|
298e0f24d1 | ||
|
|
80b08b71aa | ||
|
|
64ba569f0d | ||
|
|
3cb4033c97 | ||
|
|
6509bac59e | ||
|
|
ec45a77d93 | ||
|
|
8044833c74 | ||
|
|
3f89bdcfac | ||
|
|
c99364b81a | ||
|
|
d841a791e6 | ||
|
|
b5b937e2f2 | ||
|
|
4a41f69293 | ||
|
|
8fd038f88e | ||
|
|
4982f63da2 | ||
|
|
71bc60fb91 | ||
|
|
0da7ecf753 | ||
|
|
25ca1af8b3 | ||
|
|
5d54fa8e6f | ||
|
|
e8c5b9faad | ||
|
|
8e0301c287 | ||
|
|
3f625fc359 | ||
|
|
72b96d5069 | ||
|
|
d1b45b8d32 | ||
|
|
5601056ed0 | ||
|
|
8ed269c77f | ||
|
|
27b7e70218 | ||
|
|
97680656d3 | ||
|
|
4202ec2ab2 | ||
|
|
4f3a816fb0 | ||
|
|
25fd8ef5c0 | ||
|
|
a9664b8d16 | ||
|
|
26a5729514 | ||
|
|
5b839699ac | ||
|
|
1315aa31ed | ||
|
|
5d8da6b4ac | ||
|
|
aac2563a07 | ||
|
|
8e44487354 | ||
|
|
1012c054e6 | ||
|
|
a8b5738b44 | ||
|
|
72845167e6 | ||
|
|
7ca2d6470a | ||
|
|
33e999e01a |
@@ -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?
|
What OS and distribution are you using?
|
||||||
|
|
||||||
May we assist you in trying to fix this in Verilator yourself?
|
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)
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# See https://citation-file-format.github.io/
|
||||||
|
cff-version: 1.2.0
|
||||||
|
title: Verilator
|
||||||
|
message: >-
|
||||||
|
If you use this software, please cite it using the
|
||||||
|
metadata from this file.
|
||||||
|
type: software
|
||||||
|
authors:
|
||||||
|
- given-names: Wilson
|
||||||
|
family-names: Snyder
|
||||||
|
email: [email protected]
|
||||||
|
affiliation: Veripool
|
||||||
|
- given-names: Paul
|
||||||
|
family-names: Wasson
|
||||||
|
- given-names: Duane
|
||||||
|
family-names: Galbi
|
||||||
|
- name: 'et al'
|
||||||
|
repository-code: 'https://github.com/verilator/verilator'
|
||||||
|
url: 'https://verilator.org'
|
||||||
|
abstract: >-
|
||||||
|
The Verilator package converts Verilog and SystemVerilog hardware
|
||||||
|
description language (HDL) designs into a fast C++ or SystemC model
|
||||||
|
that, after compiling, can be executed. Verilator is not a
|
||||||
|
traditional simulator but a compiler.
|
||||||
|
license:
|
||||||
|
- LGPL-3.0-only
|
||||||
|
- Artistic-2.0
|
||||||
+1
-1
@@ -15,7 +15,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.15)
|
cmake_minimum_required(VERSION 3.15)
|
||||||
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
|
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
|
||||||
project(Verilator
|
project(Verilator
|
||||||
VERSION 5.024
|
VERSION 5.026
|
||||||
HOMEPAGE_URL https://verilator.org
|
HOMEPAGE_URL https://verilator.org
|
||||||
LANGUAGES CXX
|
LANGUAGES CXX
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,6 +8,73 @@ The changes in each Verilator version are described below. The
|
|||||||
contributors that suggested a given feature are shown in []. Thanks!
|
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
|
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 dynamic NBA conditions (#4773). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||||
* Fix `V3Fork` stage to run only if `--timing` is set (#4778). [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 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
|
Verilator 5.018 2023-10-30
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ datarootdir = @datarootdir@
|
|||||||
CFG_WITH_CCWARN = @CFG_WITH_CCWARN@
|
CFG_WITH_CCWARN = @CFG_WITH_CCWARN@
|
||||||
CFG_WITH_DEFENV = @CFG_WITH_DEFENV@
|
CFG_WITH_DEFENV = @CFG_WITH_DEFENV@
|
||||||
CFG_WITH_LONGTESTS = @CFG_WITH_LONGTESTS@
|
CFG_WITH_LONGTESTS = @CFG_WITH_LONGTESTS@
|
||||||
|
CFG_WITH_SOLVER = @CFG_WITH_SOLVER@
|
||||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||||
|
|
||||||
#### End of system configuration section. ####
|
#### End of system configuration section. ####
|
||||||
|
|||||||
+15
-14
@@ -1,21 +1,23 @@
|
|||||||
.. Github doesn't render images unless absolute URL
|
.. Github doesn't render images unless absolute URL
|
||||||
.. Do not know of a conditional tag, "only: github" nor "github display" works
|
.. 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
|
: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
|
: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
|
: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
|
: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
|
: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
|
: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
|
: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
|
: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
|
as `Icarus Verilog`_. Another 2-10x speedup might be gained from
|
||||||
multithreading (yielding 200-1000x total over interpreted simulators).
|
multithreading (yielding 200-1000x total over interpreted simulators).
|
||||||
|
|
||||||
Verilator has typically similar or better performance versus
|
Verilator has typically similar or better performance versus closed-source
|
||||||
closed-source Verilog simulators (e.g., Carbon Design Systems Carbonator,
|
Verilog simulators (e.g., Aldec Riviera-Pro, Cadence Incisive/NC-Verilog,
|
||||||
Modelsim/Questa, Cadence Incisive/NC-Verilog, Synopsys VCS, VTOC, and
|
Mentor ModelSim/Questa, Synopsys VCS, VTOC, and Pragmatic CVer/CVC). But,
|
||||||
Pragmatic CVer/CVC). But, Verilator is open-sourced, so you can spend on
|
Verilator is open-sourced, so you can spend on computes rather than
|
||||||
computes rather than licenses. Thus, Verilator gives you the best
|
licenses. Thus, Verilator gives you the best simulation cycles/dollar.
|
||||||
simulation cycles/dollar.
|
|
||||||
|
|
||||||
|
|
||||||
Installation & Documentation
|
Installation & Documentation
|
||||||
|
|||||||
@@ -387,6 +387,7 @@ detailed descriptions of these arguments.
|
|||||||
--lib-create <name> Create a DPI library
|
--lib-create <name> Create a DPI library
|
||||||
+libext+<ext>+[ext]... Extensions for finding modules
|
+libext+<ext>+[ext]... Extensions for finding modules
|
||||||
--lint-only Lint, but do not make output
|
--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
|
--make <build-tool> Generate scripts for specified build tool
|
||||||
-MAKEFLAGS <flags> Arguments to pass to make during --build
|
-MAKEFLAGS <flags> Arguments to pass to make during --build
|
||||||
--main Generate C++ main() file
|
--main Generate C++ main() file
|
||||||
@@ -406,6 +407,7 @@ detailed descriptions of these arguments.
|
|||||||
--output-split-ctrace <statements> Split tracing functions
|
--output-split-ctrace <statements> Split tracing functions
|
||||||
-P Disable line numbers and blanks with -E
|
-P Disable line numbers and blanks with -E
|
||||||
--pins-bv <bits> Specify types for top-level ports
|
--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-biguint Specify types for top-level ports
|
||||||
--pins-sc-uint Specify types for top-level ports
|
--pins-sc-uint Specify types for top-level ports
|
||||||
--pins-uint8 Specify types for top-level ports
|
--pins-uint8 Specify types for top-level ports
|
||||||
|
|||||||
@@ -154,9 +154,9 @@ verilator_coverage - Verilator coverage analyzer
|
|||||||
|
|
||||||
verilator_coverage --annotate <obj>
|
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.
|
Verilator_coverage processes Verilated model-generated coverage reports.
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -92,8 +92,8 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
|||||||
sudo apt-get update ||
|
sudo apt-get update ||
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
# libfl-dev needed for internal coverage's test runs
|
# 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 z3 ||
|
||||||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq
|
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq z3
|
||||||
# Required for test_regress/t/t_dist_attributes.pl
|
# Required for test_regress/t/t_dist_attributes.pl
|
||||||
if [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
|
if [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
|
||||||
sudo apt-get install python3-clang mold ||
|
sudo apt-get install python3-clang mold ||
|
||||||
@@ -106,10 +106,10 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
|||||||
elif [ "$CI_OS_NAME" = "osx" ]; then
|
elif [ "$CI_OS_NAME" = "osx" ]; then
|
||||||
brew update
|
brew update
|
||||||
# brew cask install gtkwave # fst2vcd hangs at launch, so don't bother
|
# 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
|
elif [ "$CI_OS_NAME" = "freebsd" ]; then
|
||||||
# fst2vcd fails with "Could not open '<input file>', exiting."
|
# 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
|
else
|
||||||
fatal "Unknown os: '$CI_OS_NAME'"
|
fatal "Unknown os: '$CI_OS_NAME'"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -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)) {
|
if (-Not (Test-Path $PWD/../.ccache/win_bison.exe)) {
|
||||||
git clone --depth 1 https://github.com/lexxmark/winflexbison
|
git clone --depth 1 https://github.com/lexxmark/winflexbison
|
||||||
cd winflexbison
|
cd winflexbison
|
||||||
@@ -8,6 +20,7 @@ if (-Not (Test-Path $PWD/../.ccache/win_bison.exe)) {
|
|||||||
cmake --install . --prefix $PWD/../../../.ccache
|
cmake --install . --prefix $PWD/../../../.ccache
|
||||||
cd ../..
|
cd ../..
|
||||||
}
|
}
|
||||||
|
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. --install-prefix $PWD/../install
|
cmake .. --install-prefix $PWD/../install
|
||||||
|
|||||||
+17
-1
@@ -1,3 +1,16 @@
|
|||||||
|
# DESCRIPTION: Verilator: CI Windows Power Shell - Verilate a test
|
||||||
|
#
|
||||||
|
# Copyright 2024 by Wilson Snyder. This program is free software; you
|
||||||
|
# can redistribute it and/or modify it under the terms of either the GNU
|
||||||
|
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||||
|
# Version 2.0.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
|
||||||
|
Set-PSDebug -Trace 1
|
||||||
|
|
||||||
cd install
|
cd install
|
||||||
$Env:VERILATOR_ROOT=$PWD
|
$Env:VERILATOR_ROOT=$PWD
|
||||||
cd examples/cmake_tracing_c
|
cd examples/cmake_tracing_c
|
||||||
@@ -5,6 +18,9 @@ mkdir build
|
|||||||
cd build
|
cd build
|
||||||
cmake ..
|
cmake ..
|
||||||
cmake --build . --config Release -j 3
|
cmake --build . --config Release -j 3
|
||||||
Release/example.exe
|
|
||||||
|
# TODO put this back in, see issue# 5163
|
||||||
|
# Release/example.exe
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
Remove-Item -path build -recurse
|
Remove-Item -path build -recurse
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ RUN apt-get update \
|
|||||||
perl \
|
perl \
|
||||||
python3 \
|
python3 \
|
||||||
wget \
|
wget \
|
||||||
|
z3 \
|
||||||
zlib1g \
|
zlib1g \
|
||||||
zlib1g-dev \
|
zlib1g-dev \
|
||||||
&& apt-get clean \
|
&& apt-get clean \
|
||||||
|
|||||||
+24
-2
@@ -5,12 +5,12 @@
|
|||||||
# General Public License Version 3 or the Perl Artistic License Version 2.0.
|
# General Public License Version 3 or the Perl Artistic License Version 2.0.
|
||||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-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
|
# and commit using "devel release" or "Version bump" message
|
||||||
# Then 'make maintainer-dist'
|
# Then 'make maintainer-dist'
|
||||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||||
#AC_INIT([Verilator],[#.### devel])
|
#AC_INIT([Verilator],[#.### devel])
|
||||||
AC_INIT([Verilator],[5.024 2024-04-05],
|
AC_INIT([Verilator],[5.026 2024-06-15],
|
||||||
[https://verilator.org],
|
[https://verilator.org],
|
||||||
[verilator],[https://verilator.org])
|
[verilator],[https://verilator.org])
|
||||||
|
|
||||||
@@ -134,6 +134,28 @@ AC_ARG_ENABLE([longtests],
|
|||||||
AC_SUBST(CFG_WITH_LONGTESTS)
|
AC_SUBST(CFG_WITH_LONGTESTS)
|
||||||
AC_MSG_RESULT($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)
|
# Compiler flags (ensure they are not empty to avoid configure defaults)
|
||||||
CFLAGS="$CFLAGS "
|
CFLAGS="$CFLAGS "
|
||||||
CPPFLAGS="$CPPFLAGS "
|
CPPFLAGS="$CPPFLAGS "
|
||||||
|
|||||||
@@ -9,20 +9,24 @@ Adrien Le Masle
|
|||||||
Ahmed El-Mahmoudy
|
Ahmed El-Mahmoudy
|
||||||
Aleksander Kiryk
|
Aleksander Kiryk
|
||||||
Alex Chadwick
|
Alex Chadwick
|
||||||
|
Alex Solomatnikov
|
||||||
Àlex Torregrosa
|
Àlex Torregrosa
|
||||||
Aliaksei Chapyzhenka
|
Aliaksei Chapyzhenka
|
||||||
Ameya Vikram Singh
|
Ameya Vikram Singh
|
||||||
|
Andrea Calabrese
|
||||||
Andreas Kuster
|
Andreas Kuster
|
||||||
Andrei Kostovski
|
Andrei Kostovski
|
||||||
Andrew Miloradovsky
|
Andrew Miloradovsky
|
||||||
Andrew Nolte
|
Andrew Nolte
|
||||||
Anthony Donlon
|
Anthony Donlon
|
||||||
Arkadiusz Kozdra
|
Arkadiusz Kozdra
|
||||||
|
Arthur Rosa
|
||||||
Aylon Chaim Porat
|
Aylon Chaim Porat
|
||||||
Bartłomiej Chmiel
|
Bartłomiej Chmiel
|
||||||
Cameron Kirk
|
Cameron Kirk
|
||||||
Chih-Mao Chen
|
Chih-Mao Chen
|
||||||
Chris Randall
|
Chris Randall
|
||||||
|
Christopher Taylor
|
||||||
Chuxuan Wang
|
Chuxuan Wang
|
||||||
Chykon
|
Chykon
|
||||||
Conor McCullough
|
Conor McCullough
|
||||||
@@ -33,6 +37,7 @@ David Ledger
|
|||||||
David Metz
|
David Metz
|
||||||
David Stanford
|
David Stanford
|
||||||
David Turner
|
David Turner
|
||||||
|
Dercury
|
||||||
Don Williamson
|
Don Williamson
|
||||||
Drew Ranck
|
Drew Ranck
|
||||||
Drew Taussig
|
Drew Taussig
|
||||||
@@ -51,6 +56,7 @@ G-A. Kamendje
|
|||||||
Garrett Smith
|
Garrett Smith
|
||||||
Geza Lore
|
Geza Lore
|
||||||
Gianfranco Costamagna
|
Gianfranco Costamagna
|
||||||
|
Gijs Burghoorn
|
||||||
Glen Gibb
|
Glen Gibb
|
||||||
Gökçe Aydos
|
Gökçe Aydos
|
||||||
Graham Rushton
|
Graham Rushton
|
||||||
@@ -127,6 +133,7 @@ Marshal Qiao
|
|||||||
Martin Schmidt
|
Martin Schmidt
|
||||||
Martin Stadler
|
Martin Stadler
|
||||||
Matthew Ballance
|
Matthew Ballance
|
||||||
|
Michael Bikovitsky
|
||||||
Michael Killough
|
Michael Killough
|
||||||
Michaël Lefebvre
|
Michaël Lefebvre
|
||||||
Michal Czyz
|
Michal Czyz
|
||||||
@@ -141,8 +148,10 @@ Nathan Myers
|
|||||||
Nolan Poe
|
Nolan Poe
|
||||||
Oleh Maksymenko
|
Oleh Maksymenko
|
||||||
Patrick Stewart
|
Patrick Stewart
|
||||||
|
Paul Bowen-Huggett
|
||||||
Paul Swirhun
|
Paul Swirhun
|
||||||
Paul Wright
|
Paul Wright
|
||||||
|
Pawel Jewstafjew
|
||||||
Pawel Sagan
|
Pawel Sagan
|
||||||
Pengcheng Xu
|
Pengcheng Xu
|
||||||
Peter Debacker
|
Peter Debacker
|
||||||
@@ -185,6 +194,7 @@ Tudor Timi
|
|||||||
Tymoteusz Blazejczyk
|
Tymoteusz Blazejczyk
|
||||||
Udi Finkelstein
|
Udi Finkelstein
|
||||||
Unai Martinez-Corral
|
Unai Martinez-Corral
|
||||||
|
Valentin Atepalikhin
|
||||||
Varun Koyyalagunta
|
Varun Koyyalagunta
|
||||||
Vassilis Papaefstathiou
|
Vassilis Papaefstathiou
|
||||||
Veripool API Bot
|
Veripool API Bot
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.. comment: generated by t_lint_multidriven_bad
|
.. comment: generated by t_lint_multidriven_bad
|
||||||
.. code-block::
|
.. 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 first driving block
|
||||||
example.v:1:7 ... Location of other driving block
|
example.v:1:7 ... Location of other driving block
|
||||||
|
|||||||
@@ -407,6 +407,16 @@ only a couple of instructions.
|
|||||||
For signal callbacks to work the main loop of the program must call
|
For signal callbacks to work the main loop of the program must call
|
||||||
:code:`VerilatedVpi::callValueCbs()`.
|
: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:
|
.. _VPI Example:
|
||||||
|
|
||||||
|
|||||||
@@ -118,6 +118,12 @@ associated programs.
|
|||||||
|
|
||||||
See :ref:`Installation` for more details.
|
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
|
.. option:: VERILATOR_VALGRIND
|
||||||
|
|
||||||
If set, the command to run when using the :vlopt:`--valgrind` option, such as
|
If set, the command to run when using the :vlopt:`--valgrind` option, such as
|
||||||
|
|||||||
@@ -817,6 +817,11 @@ Summary:
|
|||||||
If the design is not to be completely Verilated, see also the
|
If the design is not to be completely Verilated, see also the
|
||||||
:vlopt:`--bbox-sys` and :vlopt:`--bbox-unsup` options.
|
: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>
|
.. option:: --make <build-tool>
|
||||||
|
|
||||||
Generates a script for the specified 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
|
:option:`/*verilator&32;sc_bv*/` metacomment to select specific ports to
|
||||||
be sc_bv.
|
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
|
.. option:: --pins-sc-uint
|
||||||
|
|
||||||
Specifies SystemC inputs/outputs greater than 2 bits wide should use
|
Specifies SystemC inputs/outputs greater than 2 bits wide should use
|
||||||
@@ -1732,9 +1744,9 @@ Summary:
|
|||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
This option applies only to values explicitly written as X
|
This option applies only to values explicitly written as X in modules
|
||||||
in modules (not classes) in the Verilog source code. Initial values
|
(not classes, nor parameters) in the Verilog source code. Initial
|
||||||
of clocks are set to 0 unless `--x-initial-edge` is
|
values of clocks are set to 0 unless `--x-initial-edge` is
|
||||||
specified. Initial values of all other state holding variables are
|
specified. Initial values of all other state holding variables are
|
||||||
controlled with `--x-initial`.
|
controlled with `--x-initial`.
|
||||||
|
|
||||||
|
|||||||
@@ -37,9 +37,9 @@ verilator_coverage Example Usage
|
|||||||
|
|
||||||
verilator_coverage --annotate <obj>
|
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
|
verilator_coverage Arguments
|
||||||
|
|||||||
+14
-2
@@ -152,6 +152,17 @@ To make use of Verilator FST tracing you will want `GTKwave
|
|||||||
required at Verilator build time.
|
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:
|
||||||
|
|
||||||
Obtain Sources
|
Obtain Sources
|
||||||
@@ -193,8 +204,9 @@ Eventual Installation Options
|
|||||||
Before configuring the build, you must decide how you're going to
|
Before configuring the build, you must decide how you're going to
|
||||||
eventually install Verilator onto your system. Verilator will be compiling
|
eventually install Verilator onto your system. Verilator will be compiling
|
||||||
the current value of the environment variables :option:`VERILATOR_ROOT`,
|
the current value of the environment variables :option:`VERILATOR_ROOT`,
|
||||||
:option:`SYSTEMC_INCLUDE`, and :option:`SYSTEMC_LIBDIR` as defaults into
|
:option:`VERILATOR_SOLVER`, :option:`SYSTEMC_INCLUDE`, and
|
||||||
the executable, so they must be correct before configuring.
|
:option:`SYSTEMC_LIBDIR` as defaults into the executable, so they must be
|
||||||
|
correct before configuring.
|
||||||
|
|
||||||
These are the installation options:
|
These are the installation options:
|
||||||
|
|
||||||
|
|||||||
+30
-9
@@ -210,22 +210,35 @@ List Of Warnings
|
|||||||
|
|
||||||
.. option:: BLKLOOPINIT
|
.. option:: BLKLOOPINIT
|
||||||
|
|
||||||
.. TODO better example
|
Indicates certain constructs where non-blocking assignments to unpacked
|
||||||
|
arrays (memories) are not supported inside loops. These typically appear in
|
||||||
This indicates that the initialization of an array needs to use
|
initialization/reset code:
|
||||||
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
|
|
||||||
|
|
||||||
.. code-block:: sv
|
.. code-block:: sv
|
||||||
|
|
||||||
always @(posedge clk)
|
always @(posedge clk)
|
||||||
if (~reset_l)
|
if (~reset_l)
|
||||||
for (i=0; i<`ARRAY_SIZE; i++)
|
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
|
This message is only seen on large or complicated loops because
|
||||||
Verilator generally unrolls small loops. You may want to try increasing
|
Verilator generally unrolls small loops. You may want to try increasing
|
||||||
@@ -1204,6 +1217,14 @@ List Of Warnings
|
|||||||
:vlopt:`--no-timing` option.
|
: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
|
.. option:: NULLPORT
|
||||||
|
|
||||||
Warns that a null port was detected in the module definition port
|
Warns that a null port was detected in the module definition port
|
||||||
|
|||||||
@@ -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.
|
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
|
Coding Conventions
|
||||||
==================
|
==================
|
||||||
|
|
||||||
@@ -2125,6 +2210,10 @@ VERILATOR_ROOT
|
|||||||
Standard path to Verilator distribution root; see primary Verilator
|
Standard path to Verilator distribution root; see primary Verilator
|
||||||
documentation.
|
documentation.
|
||||||
|
|
||||||
|
VERILATOR_SOLVER
|
||||||
|
SMT solver command for constrained randomization; see primary Verilator
|
||||||
|
documentation.
|
||||||
|
|
||||||
VERILATOR_TESTS_SITE
|
VERILATOR_TESTS_SITE
|
||||||
Used with ``--site``, a colon-separated list of directories with tests to
|
Used with ``--site``, a colon-separated list of directories with tests to
|
||||||
be added to testlist.
|
be added to testlist.
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ Brej
|
|||||||
Briquet
|
Briquet
|
||||||
Brownlee
|
Brownlee
|
||||||
Buckenmaier
|
Buckenmaier
|
||||||
|
Burghoorn
|
||||||
Bybell
|
Bybell
|
||||||
CLang
|
CLang
|
||||||
CMake
|
CMake
|
||||||
@@ -131,6 +132,7 @@ Gernot
|
|||||||
Gerst
|
Gerst
|
||||||
Gielda
|
Gielda
|
||||||
Gigerl
|
Gigerl
|
||||||
|
Gijs
|
||||||
Gileadi
|
Gileadi
|
||||||
Giri
|
Giri
|
||||||
Gisselquist
|
Gisselquist
|
||||||
@@ -164,6 +166,7 @@ Homebrew
|
|||||||
Hornung
|
Hornung
|
||||||
Hossell
|
Hossell
|
||||||
Hsu
|
Hsu
|
||||||
|
Huggett
|
||||||
Hutt
|
Hutt
|
||||||
Hyperthreading
|
Hyperthreading
|
||||||
Ibrahim
|
Ibrahim
|
||||||
@@ -842,6 +845,7 @@ parallelized
|
|||||||
param
|
param
|
||||||
parameterization
|
parameterization
|
||||||
parameterized
|
parameterized
|
||||||
|
parameterless
|
||||||
params
|
params
|
||||||
parens
|
parens
|
||||||
pawel
|
pawel
|
||||||
@@ -885,6 +889,7 @@ profcfunc
|
|||||||
profiler
|
profiler
|
||||||
prototyptes
|
prototyptes
|
||||||
ps
|
ps
|
||||||
|
psprintf
|
||||||
pthread
|
pthread
|
||||||
ptr
|
ptr
|
||||||
pulldown
|
pulldown
|
||||||
@@ -968,6 +973,7 @@ submakes
|
|||||||
submodule
|
submodule
|
||||||
submodules
|
submodules
|
||||||
substring
|
substring
|
||||||
|
suspendable
|
||||||
sv
|
sv
|
||||||
svBitVal
|
svBitVal
|
||||||
svBitVecVal
|
svBitVecVal
|
||||||
@@ -978,6 +984,7 @@ svLogicVal
|
|||||||
svdpi
|
svdpi
|
||||||
svgGetTimeUnit
|
svgGetTimeUnit
|
||||||
swrite
|
swrite
|
||||||
|
synthesizeable
|
||||||
sys
|
sys
|
||||||
systemc
|
systemc
|
||||||
tenghtt
|
tenghtt
|
||||||
@@ -996,6 +1003,7 @@ toolchain
|
|||||||
topcell
|
topcell
|
||||||
toplevel
|
toplevel
|
||||||
toupper
|
toupper
|
||||||
|
traceCapable
|
||||||
traceEverOn
|
traceEverOn
|
||||||
tran
|
tran
|
||||||
treei
|
treei
|
||||||
@@ -1060,6 +1068,7 @@ vluint
|
|||||||
vpi
|
vpi
|
||||||
vpiConstType
|
vpiConstType
|
||||||
vpiDefName
|
vpiDefName
|
||||||
|
vpiInertialDelay
|
||||||
vpiInstance
|
vpiInstance
|
||||||
vpiLeftRange
|
vpiLeftRange
|
||||||
vpiModule
|
vpiModule
|
||||||
|
|||||||
@@ -47,12 +47,12 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
// Simulate until $finish
|
// Simulate until $finish
|
||||||
while (!contextp->gotFinish()) {
|
while (!contextp->gotFinish()) {
|
||||||
contextp->timeInc(1);
|
|
||||||
top->clk = ~top->clk & 0x1;
|
top->clk = ~top->clk & 0x1;
|
||||||
top->eval();
|
top->eval();
|
||||||
#if VM_TRACE
|
#if VM_TRACE
|
||||||
if (tfp) tfp->dump(contextp->time());
|
if (tfp) tfp->dump(contextp->time());
|
||||||
#endif
|
#endif
|
||||||
|
contextp->timeInc(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Final model cleanup
|
// Final model cleanup
|
||||||
|
|||||||
@@ -347,8 +347,9 @@ return(NULL);
|
|||||||
|
|
||||||
static void *fstMmap2(size_t __len, int __fd, fst_off_t __off)
|
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,
|
HANDLE handle = CreateFileMapping((HANDLE)_get_osfhandle(__fd), NULL,
|
||||||
PAGE_READWRITE, (DWORD)(__len >> 32),
|
PAGE_READWRITE, (DWORD)(len64 >> 32),
|
||||||
(DWORD)__len, NULL);
|
(DWORD)__len, NULL);
|
||||||
if (!handle) { return NULL; }
|
if (!handle) { return NULL; }
|
||||||
|
|
||||||
|
|||||||
+87
-52
@@ -82,6 +82,12 @@
|
|||||||
|
|
||||||
#include "verilated_trace.h"
|
#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
|
// Max characters in static char string for VL_VALUE_STRING
|
||||||
constexpr unsigned VL_VALUE_STRING_MAX_WIDTH = 8192;
|
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 {
|
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);
|
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,
|
void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp, size_t posstart,
|
||||||
size_t posstart, size_t posend) VL_MT_SAFE {
|
size_t posend) VL_MT_SAFE {
|
||||||
// Read in base "2^^baseLog2" digits from strp[posstart..posend-1] into owp of size obits.
|
// Read in base "2^^baseLog2" digits from strp[posstart..posend-1] into owp of size obits.
|
||||||
VL_ZERO_W(obits, owp);
|
VL_ZERO_W(obits, owp);
|
||||||
int lsb = 0;
|
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 {
|
IData VL_SYSTEM_IW(int lhswords, const WDataInP lhsp) VL_MT_SAFE {
|
||||||
char filenamez[VL_VALUE_STRING_MAX_CHARS + 1];
|
char filenamez[VL_VALUE_STRING_MAX_CHARS + 1];
|
||||||
_vl_vint_to_string(lhswords * VL_EDATASIZE, filenamez, lhsp);
|
_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
|
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;
|
if (VL_UNLIKELY(!m_fp)) return false;
|
||||||
valuer = "";
|
valuer = "";
|
||||||
// Prep for reading
|
// Prep for reading
|
||||||
bool indata = false;
|
bool inData = false;
|
||||||
bool ignore_to_eol = false;
|
bool ignoreToEol = false;
|
||||||
bool ignore_to_cmt = false;
|
bool ignoreToComment = false;
|
||||||
bool reading_addr = false;
|
bool readingAddress = false;
|
||||||
int lastc = ' ';
|
int lastCh = ' ';
|
||||||
// Read the data
|
// Read the data
|
||||||
// We process a character at a time, as then we don't need to deal
|
// We process a character at a time, as then we don't need to deal
|
||||||
// with changing buffer sizes dynamically, etc.
|
// with changing buffer sizes dynamically, etc.
|
||||||
while (true) {
|
while (true) {
|
||||||
int c = std::fgetc(m_fp);
|
int c = std::fgetc(m_fp);
|
||||||
if (VL_UNLIKELY(c == EOF)) break;
|
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",
|
// 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
|
// See if previous data value has completed, and if so return
|
||||||
if (c == '_') continue; // Ignore _ e.g. inside a number
|
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());
|
// printf("Got data @%lx = %s\n", m_addr, valuer.c_str());
|
||||||
ungetc(c, m_fp);
|
ungetc(c, m_fp);
|
||||||
addrr = m_addr;
|
addrr = m_addr;
|
||||||
@@ -2062,50 +2075,46 @@ bool VlReadMem::get(QData& addrr, std::string& valuer) {
|
|||||||
// Parse line
|
// Parse line
|
||||||
if (c == '\n') {
|
if (c == '\n') {
|
||||||
++m_linenum;
|
++m_linenum;
|
||||||
ignore_to_eol = false;
|
ignoreToEol = false;
|
||||||
reading_addr = false;
|
readingAddress = false;
|
||||||
} else if (c == '\t' || c == ' ' || c == '\r' || c == '\f') {
|
} else if (c == '\t' || c == ' ' || c == '\r' || c == '\f') {
|
||||||
reading_addr = false;
|
readingAddress = false;
|
||||||
}
|
}
|
||||||
// Skip // comments and detect /* comments
|
// Skip // comments and detect /* comments
|
||||||
else if (ignore_to_cmt && lastc == '*' && c == '/') {
|
else if (ignoreToComment && lastCh == '*' && c == '/') {
|
||||||
ignore_to_cmt = false;
|
ignoreToComment = false;
|
||||||
reading_addr = false;
|
readingAddress = false;
|
||||||
} else if (!ignore_to_eol && !ignore_to_cmt) {
|
} else if (!ignoreToEol && !ignoreToComment) {
|
||||||
if (lastc == '/' && c == '*') {
|
if (lastCh == '/' && c == '*') {
|
||||||
ignore_to_cmt = true;
|
ignoreToComment = true;
|
||||||
} else if (lastc == '/' && c == '/') {
|
} else if (lastCh == '/' && c == '/') {
|
||||||
ignore_to_eol = true;
|
ignoreToEol = true;
|
||||||
} else if (c == '/') { // Part of /* or //
|
} else if (c == '/') { // Part of /* or //
|
||||||
} else if (c == '#') {
|
} else if (c == '#') {
|
||||||
ignore_to_eol = true;
|
ignoreToEol = true;
|
||||||
} else if (c == '@') {
|
} else if (c == '@') {
|
||||||
reading_addr = true;
|
readingAddress = true;
|
||||||
m_anyAddr = true;
|
m_anyAddr = true;
|
||||||
m_addr = 0;
|
m_addr = 0;
|
||||||
}
|
} else if (readingAddress && chIs2StateHex) {
|
||||||
// Check for hex or binary digits as file format requests
|
|
||||||
else if (std::isxdigit(c) || (!reading_addr && (c == 'x' || c == 'X'))) {
|
|
||||||
c = std::tolower(c);
|
c = std::tolower(c);
|
||||||
const int value
|
const int addressValue = (c >= 'a') ? (c - 'a' + 10) : (c - '0');
|
||||||
= (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c - 'a' + 10)) : (c - '0'));
|
m_addr = (m_addr << 4) + addressValue;
|
||||||
if (reading_addr) {
|
} else if (readingAddress && chIs4StateHex) {
|
||||||
// Decode @ addresses
|
VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
|
||||||
m_addr = (m_addr << 4) + value;
|
"$readmem address contains 4-state characters");
|
||||||
} else {
|
} else if (chIs4StateHex) {
|
||||||
indata = true;
|
inData = true;
|
||||||
valuer += static_cast<char>(c);
|
valuer += static_cast<char>(c);
|
||||||
// printf(" Value width=%d @%x = %c\n", width, m_addr, c);
|
if (VL_UNLIKELY(!m_hex && !chIs4StateBin)) {
|
||||||
if (VL_UNLIKELY(value > 1 && !m_hex)) {
|
VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
|
||||||
VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
|
"$readmemb (binary) file contains hex characters");
|
||||||
"$readmemb (binary) file contains hex characters");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
VL_FATAL_MT(m_filename.c_str(), m_linenum, "", "$readmem file syntax error");
|
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)) {
|
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;
|
addrr = m_addr;
|
||||||
return indata; // EOF
|
return inData; // EOF
|
||||||
}
|
}
|
||||||
void VlReadMem::setData(void* valuep, const std::string& rhs) {
|
void VlReadMem::setData(void* valuep, const std::string& rhs) {
|
||||||
const QData shift = m_hex ? 4ULL : 1ULL;
|
const QData shift = m_hex ? 4ULL : 1ULL;
|
||||||
@@ -2122,8 +2131,9 @@ void VlReadMem::setData(void* valuep, const std::string& rhs) {
|
|||||||
// Shift value in
|
// Shift value in
|
||||||
for (const auto& i : rhs) {
|
for (const auto& i : rhs) {
|
||||||
const char c = std::tolower(i);
|
const char c = std::tolower(i);
|
||||||
const int value
|
const int value = (c == 'x' || c == 'z') ? VL_RAND_RESET_I(m_hex ? 4 : 1)
|
||||||
= (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c - 'a' + 10)) : (c - '0'));
|
: (c >= 'a') ? (c - 'a' + 10)
|
||||||
|
: (c - '0');
|
||||||
if (m_bits <= 8) {
|
if (m_bits <= 8) {
|
||||||
CData* const datap = reinterpret_cast<CData*>(valuep);
|
CData* const datap = reinterpret_cast<CData*>(valuep);
|
||||||
if (!innum) *datap = 0;
|
if (!innum) *datap = 0;
|
||||||
@@ -2457,6 +2467,7 @@ VerilatedContext::VerilatedContext()
|
|||||||
m_ns.m_coverageFilename = "coverage.dat";
|
m_ns.m_coverageFilename = "coverage.dat";
|
||||||
m_ns.m_profExecFilename = "profile_exec.dat";
|
m_ns.m_profExecFilename = "profile_exec.dat";
|
||||||
m_ns.m_profVltFilename = "profile.vlt";
|
m_ns.m_profVltFilename = "profile.vlt";
|
||||||
|
m_ns.m_solverProgram = VlOs::getenvStr("VERILATOR_SOLVER", VL_SOLVER_DEFAULT);
|
||||||
m_fdps.resize(31);
|
m_fdps.resize(31);
|
||||||
std::fill(m_fdps.begin(), m_fdps.end(), static_cast<FILE*>(nullptr));
|
std::fill(m_fdps.begin(), m_fdps.end(), static_cast<FILE*>(nullptr));
|
||||||
m_fdFreeMct.resize(30);
|
m_fdFreeMct.resize(30);
|
||||||
@@ -2567,6 +2578,14 @@ std::string VerilatedContext::profVltFilename() const VL_MT_SAFE {
|
|||||||
const VerilatedLockGuard lock{m_mutex};
|
const VerilatedLockGuard lock{m_mutex};
|
||||||
return m_ns.m_profVltFilename;
|
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 {
|
void VerilatedContext::quiet(bool flag) VL_MT_SAFE {
|
||||||
const VerilatedLockGuard lock{m_mutex};
|
const VerilatedLockGuard lock{m_mutex};
|
||||||
m_s.m_quiet = flag;
|
m_s.m_quiet = flag;
|
||||||
@@ -2645,8 +2664,16 @@ void VerilatedContext::addModel(VerilatedModel* modelp) {
|
|||||||
m_ns.m_cpuTimeStart.start();
|
m_ns.m_cpuTimeStart.start();
|
||||||
m_ns.m_wallTimeStart.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();
|
m_threadsInModels += modelp->threads();
|
||||||
if (VL_UNLIKELY(modelp->threads() > m_threads)) {
|
if (VL_UNLIKELY(modelp->threads() > m_threads)) {
|
||||||
std::ostringstream msg;
|
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(),
|
VL_PRINTF("- Verilator: %s at %s; walltime %0.3f s; speed %s/s\n", endwhy.c_str(),
|
||||||
simtime.c_str(), walltime, simtimePerf.c_str());
|
simtime.c_str(), walltime, simtimePerf.c_str());
|
||||||
const double modelMB = VlOs::memUsageBytes() / 1024.0 / 1024.0;
|
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);
|
threadsInModels(), modelMB);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2933,7 +2960,6 @@ void VerilatedContext::trace(VerilatedTraceBaseC* tfp, int levels, int options)
|
|||||||
VL_FATAL_MT("", 0, "",
|
VL_FATAL_MT("", 0, "",
|
||||||
"Testbench C call to 'VerilatedContext::trace()' must not be called"
|
"Testbench C call to 'VerilatedContext::trace()' must not be called"
|
||||||
" after 'VerilatedTrace*::open()'\n");
|
" after 'VerilatedTrace*::open()'\n");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
// Legacy usage may call {modela}->trace(...) then {modelb}->trace(...)
|
// 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;
|
if (haveSize == RLIM_INFINITY) haveSize = 0;
|
||||||
}
|
}
|
||||||
// VL_PRINTF_MT("-Info: stackCheck(%" PRIu64 ") have %" PRIu64 "\n", needSize, haveSize);
|
// 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
|
// Check and request for 1.5x need. This is automated so the user doesn't need to do anything.
|
||||||
// if the user follows the suggestions
|
QData requestSize = needSize + needSize / 2;
|
||||||
if (VL_UNLIKELY(haveSize && needSize && haveSize < (needSize + needSize / 2))) {
|
if (VL_UNLIKELY(haveSize && needSize && haveSize < requestSize)) {
|
||||||
VL_PRINTF_MT("%%Warning: System has stack size %" PRIu64 " kb"
|
// Try to increase the stack limit to the requested size
|
||||||
" which may be too small; suggest 'ulimit -s %" PRIu64 "' or larger\n",
|
rlim.rlim_cur = requestSize;
|
||||||
haveSize / 1024, (needSize * 2) / 1024);
|
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
|
#else
|
||||||
(void)needSize; // Unused argument
|
(void)needSize; // Unused argument
|
||||||
|
|||||||
@@ -396,6 +396,7 @@ protected:
|
|||||||
std::string m_coverageFilename; // +coverage+file filename
|
std::string m_coverageFilename; // +coverage+file filename
|
||||||
std::string m_profExecFilename; // +prof+exec+file filename
|
std::string m_profExecFilename; // +prof+exec+file filename
|
||||||
std::string m_profVltFilename; // +prof+vlt 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::DeltaCpuTime m_cpuTimeStart{false}; // CPU time, starts when create first model
|
||||||
VlOs::DeltaWallTime m_wallTimeStart{false}; // Wall 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
|
std::vector<traceBaseModelCb_t> m_traceBaseModelCbs; // Callbacks to traceRegisterModel
|
||||||
@@ -624,6 +625,10 @@ public:
|
|||||||
void profVltFilename(const std::string& flag) VL_MT_SAFE;
|
void profVltFilename(const std::string& flag) VL_MT_SAFE;
|
||||||
std::string profVltFilename() const 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
|
// Internal: Find scope
|
||||||
const VerilatedScope* scopeFind(const char* namep) const VL_MT_SAFE;
|
const VerilatedScope* scopeFind(const char* namep) const VL_MT_SAFE;
|
||||||
const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE;
|
const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE;
|
||||||
|
|||||||
@@ -225,7 +225,6 @@ endif
|
|||||||
# 1. Extract object files from .a
|
# 1. Extract object files from .a
|
||||||
# 2. Create a new archive from extracted .o and given .o
|
# 2. Create a new archive from extracted .o and given .o
|
||||||
%.a: | %.verilator_deplist.tmp
|
%.a: | %.verilator_deplist.tmp
|
||||||
$(info Archive $(AR) -rcs $@ $^)
|
|
||||||
$(foreach L, $(filter-out %.a,$^), $(shell echo $L >>[email protected]_deplist.tmp))
|
$(foreach L, $(filter-out %.a,$^), $(shell echo $L >>[email protected]_deplist.tmp))
|
||||||
@if test $(words $(filter %.a,$^)) -eq 0; then \
|
@if test $(words $(filter %.a,$^)) -eq 0; then \
|
||||||
$(RM) -f $@; \
|
$(RM) -f $@; \
|
||||||
|
|||||||
@@ -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,
|
extern WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP const lwp, WDataInP const rwp,
|
||||||
bool is_modulus) VL_MT_SAFE;
|
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 IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE;
|
||||||
|
|
||||||
extern void VL_FFLUSH_I(IData fdi) 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_IW(int lhswords, WDataInP const lhsp) VL_MT_SAFE;
|
||||||
extern IData VL_SYSTEM_IQ(QData lhs) 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); }
|
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 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
|
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;
|
return u.q;
|
||||||
}
|
}
|
||||||
// clang-format on
|
// 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
|
// Return double from lhs (numeric) unsigned
|
||||||
double VL_ITOR_D_W(int lbits, WDataInP const lwp) VL_PURE;
|
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);
|
VL_SET_WQ(lwp, lhs);
|
||||||
return VL_ISTOR_D_W(lbits, lwp);
|
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)); }
|
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
|
// 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); }
|
{ (svar).write(rd); }
|
||||||
#define VL_ASSIGN_SBQ(obits, svar, rd) \
|
#define VL_ASSIGN_SBQ(obits, svar, rd) \
|
||||||
{ (svar).write(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) \
|
#define VL_ASSIGN_SBW(obits, svar, rwp) \
|
||||||
{ \
|
{ \
|
||||||
sc_dt::sc_biguint<(obits)> _butemp; \
|
sc_dt::sc_biguint<(obits)> _butemp; \
|
||||||
int32_t lsb = 0; \
|
int32_t lsb = 0; \
|
||||||
uint32_t* chunkp = _butemp.get_raw(); \
|
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"); \
|
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, \
|
const uint32_t data \
|
||||||
VL_SC_BITS_PER_DIGIT); \
|
= VL_SEL_IWII(lsb + BITS_PER_DIGIT + 1, (rwp).data(), lsb, BITS_PER_DIGIT); \
|
||||||
*chunkp = data & VL_MASK_E(VL_SC_BITS_PER_DIGIT); \
|
*chunkp = data & VL_MASK_E(BITS_PER_DIGIT); \
|
||||||
++chunkp; \
|
++chunkp; \
|
||||||
lsb += VL_SC_BITS_PER_DIGIT; \
|
lsb += BITS_PER_DIGIT; \
|
||||||
} \
|
} \
|
||||||
if (lsb < (obits)) { \
|
if (lsb < (obits)) { \
|
||||||
const uint32_t msb_data = VL_SEL_IWII((obits) + 1, (rwp).data(), lsb, (obits)-lsb); \
|
const uint32_t msb_data = VL_SEL_IWII((obits) + 1, (rwp).data(), lsb, (obits)-lsb); \
|
||||||
|
|||||||
@@ -0,0 +1,424 @@
|
|||||||
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||||
|
//*************************************************************************
|
||||||
|
//
|
||||||
|
// Code available from: https://verilator.org
|
||||||
|
//
|
||||||
|
// Copyright 2024 by Wilson Snyder. This program is free software; you can
|
||||||
|
// redistribute it and/or modify it under the terms of either the GNU Lesser
|
||||||
|
// General Public License Version 3 or the Perl Artistic License Version 2.0.
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
//
|
||||||
|
//=========================================================================
|
||||||
|
///
|
||||||
|
/// \file
|
||||||
|
/// \brief Verilated randomization implementation code
|
||||||
|
///
|
||||||
|
/// This file must be compiled and linked against all Verilated objects
|
||||||
|
/// that use randomization features.
|
||||||
|
///
|
||||||
|
/// See the internals documentation docs/internals.rst for details.
|
||||||
|
///
|
||||||
|
//=========================================================================
|
||||||
|
|
||||||
|
#include "verilated_random.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <streambuf>
|
||||||
|
|
||||||
|
#define _VL_SOLVER_HASH_LEN 1
|
||||||
|
#define _VL_SOLVER_HASH_LEN_TOTAL 4
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
#if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
|
||||||
|
# define _VL_SOLVER_PIPE // Allow pipe SMT solving. Needs fork()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _VL_SOLVER_PIPE
|
||||||
|
# include <sys/wait.h>
|
||||||
|
# include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN32) || defined(__MINGW32__)
|
||||||
|
# include <io.h> // open, read, write, close
|
||||||
|
#endif
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
class Process final : private std::streambuf, public std::iostream {
|
||||||
|
static constexpr int BUFFER_SIZE = 4096;
|
||||||
|
const char* const* m_cmd = nullptr; // fork() process argv
|
||||||
|
#ifdef _VL_SOLVER_PIPE
|
||||||
|
pid_t m_pid = 0; // fork() process id
|
||||||
|
#else
|
||||||
|
int m_pid = 0; // fork() process id - always zero as disabled
|
||||||
|
#endif
|
||||||
|
bool m_pidExited = true; // If subprocess has exited and can be opened
|
||||||
|
int m_pidStatus = 0; // fork() process exit status, valid if m_pidExited
|
||||||
|
int m_writeFd = -1; // File descriptor TO subprocess
|
||||||
|
int m_readFd = -1; // File descriptor FROM subprocess
|
||||||
|
char m_readBuf[BUFFER_SIZE];
|
||||||
|
char m_writeBuf[BUFFER_SIZE];
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef std::streambuf::traits_type traits_type;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int overflow(int c = traits_type::eof()) override {
|
||||||
|
char c2 = static_cast<char>(c);
|
||||||
|
if (pbase() == pptr()) return 0;
|
||||||
|
size_t size = pptr() - pbase();
|
||||||
|
ssize_t n = ::write(m_writeFd, pbase(), size);
|
||||||
|
if (n == -1) perror("write");
|
||||||
|
if (n <= 0) {
|
||||||
|
wait_report();
|
||||||
|
return traits_type::eof();
|
||||||
|
}
|
||||||
|
if (n == size)
|
||||||
|
setp(m_writeBuf, m_writeBuf + sizeof(m_writeBuf));
|
||||||
|
else
|
||||||
|
setp(m_writeBuf + n, m_writeBuf + sizeof(m_writeBuf));
|
||||||
|
if (c != traits_type::eof()) sputc(c2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int underflow() override {
|
||||||
|
sync();
|
||||||
|
ssize_t n = ::read(m_readFd, m_readBuf, sizeof(m_readBuf));
|
||||||
|
if (n == -1) perror("read");
|
||||||
|
if (n <= 0) {
|
||||||
|
wait_report();
|
||||||
|
return traits_type::eof();
|
||||||
|
}
|
||||||
|
setg(m_readBuf, m_readBuf, m_readBuf + n);
|
||||||
|
return traits_type::to_int_type(m_readBuf[0]);
|
||||||
|
}
|
||||||
|
int sync() override {
|
||||||
|
overflow();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Process(const char* const* const cmd = nullptr)
|
||||||
|
: std::streambuf{}
|
||||||
|
, std::iostream{this}
|
||||||
|
, m_cmd{cmd} {
|
||||||
|
open(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait_report() {
|
||||||
|
if (m_pidExited) return;
|
||||||
|
#ifdef _VL_SOLVER_PIPE
|
||||||
|
if (waitpid(m_pid, &m_pidStatus, 0) != m_pid) return;
|
||||||
|
if (m_pidStatus) {
|
||||||
|
std::stringstream msg;
|
||||||
|
msg << "Subprocess command `" << m_cmd[0];
|
||||||
|
for (const char* const* arg = m_cmd + 1; *arg; arg++) msg << ' ' << *arg;
|
||||||
|
msg << "' failed: ";
|
||||||
|
if (WIFSIGNALED(m_pidStatus))
|
||||||
|
msg << strsignal(WTERMSIG(m_pidStatus))
|
||||||
|
<< (WCOREDUMP(m_pidStatus) ? " (core dumped)" : "");
|
||||||
|
else if (WIFEXITED(m_pidStatus))
|
||||||
|
msg << "exit status " << WEXITSTATUS(m_pidStatus);
|
||||||
|
const std::string str = msg.str();
|
||||||
|
VL_WARN_MT("", 0, "Process", str.c_str());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
m_pidExited = true;
|
||||||
|
m_pid = 0;
|
||||||
|
closeFds();
|
||||||
|
}
|
||||||
|
|
||||||
|
void closeFds() {
|
||||||
|
if (m_writeFd != -1) {
|
||||||
|
close(m_writeFd);
|
||||||
|
m_writeFd = -1;
|
||||||
|
}
|
||||||
|
if (m_readFd != -1) {
|
||||||
|
close(m_readFd);
|
||||||
|
m_readFd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool open(const char* const* const cmd) {
|
||||||
|
setp(std::begin(m_writeBuf), std::end(m_writeBuf));
|
||||||
|
setg(m_readBuf, m_readBuf, m_readBuf);
|
||||||
|
#ifdef _VL_SOLVER_PIPE
|
||||||
|
if (!cmd || !cmd[0]) return false;
|
||||||
|
m_cmd = cmd;
|
||||||
|
int fd_stdin[2]; // Can't use std::array
|
||||||
|
int fd_stdout[2]; // Can't use std::array
|
||||||
|
constexpr int P_RD = 0;
|
||||||
|
constexpr int P_WR = 1;
|
||||||
|
|
||||||
|
if (pipe(fd_stdin) != 0) {
|
||||||
|
perror("Process::open: pipe");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (pipe(fd_stdout) != 0) {
|
||||||
|
perror("Process::open: pipe");
|
||||||
|
close(fd_stdin[P_RD]);
|
||||||
|
close(fd_stdin[P_WR]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fd_stdin[P_RD] <= 2 || fd_stdin[P_WR] <= 2 || fd_stdout[P_RD] <= 2
|
||||||
|
|| fd_stdout[P_WR] <= 2) {
|
||||||
|
// We'd have to rearrange all of the FD usages in this case.
|
||||||
|
// Too unlikely; verilator isn't a daemon.
|
||||||
|
fprintf(stderr, "stdin/stdout closed before pipe opened\n");
|
||||||
|
close(fd_stdin[P_RD]);
|
||||||
|
close(fd_stdin[P_WR]);
|
||||||
|
close(fd_stdout[P_RD]);
|
||||||
|
close(fd_stdout[P_WR]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pid_t pid = fork();
|
||||||
|
if (pid < 0) {
|
||||||
|
perror("Process::open: fork");
|
||||||
|
close(fd_stdin[P_RD]);
|
||||||
|
close(fd_stdin[P_WR]);
|
||||||
|
close(fd_stdout[P_RD]);
|
||||||
|
close(fd_stdout[P_WR]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (pid == 0) {
|
||||||
|
// Child
|
||||||
|
close(fd_stdin[P_WR]);
|
||||||
|
dup2(fd_stdin[P_RD], STDIN_FILENO);
|
||||||
|
close(fd_stdout[P_RD]);
|
||||||
|
dup2(fd_stdout[P_WR], STDOUT_FILENO);
|
||||||
|
execvp(cmd[0], const_cast<char* const*>(cmd));
|
||||||
|
std::stringstream msg;
|
||||||
|
msg << "Process::open: execvp(" << cmd[0] << ")";
|
||||||
|
const std::string str = msg.str();
|
||||||
|
perror(str.c_str());
|
||||||
|
_exit(127);
|
||||||
|
}
|
||||||
|
// Parent
|
||||||
|
m_pid = pid;
|
||||||
|
m_pidExited = false;
|
||||||
|
m_pidStatus = 0;
|
||||||
|
m_readFd = fd_stdout[P_RD];
|
||||||
|
m_writeFd = fd_stdin[P_WR];
|
||||||
|
|
||||||
|
close(fd_stdin[P_RD]);
|
||||||
|
close(fd_stdout[P_WR]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static Process& getSolver() {
|
||||||
|
static Process s_solver;
|
||||||
|
static bool s_done = false;
|
||||||
|
if (s_done) return s_solver;
|
||||||
|
s_done = true;
|
||||||
|
|
||||||
|
static std::vector<const char*> s_argv;
|
||||||
|
static std::string s_program = Verilated::threadContextp()->solverProgram();
|
||||||
|
s_argv.emplace_back(&s_program[0]);
|
||||||
|
for (char* arg = &s_program[0]; *arg; arg++) {
|
||||||
|
if (*arg == ' ') {
|
||||||
|
*arg = '\0';
|
||||||
|
s_argv.emplace_back(arg + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s_argv.emplace_back(nullptr);
|
||||||
|
|
||||||
|
const char* const* const cmd = &s_argv[0];
|
||||||
|
s_solver.open(cmd);
|
||||||
|
s_solver << "(set-logic QF_BV)\n";
|
||||||
|
s_solver << "(check-sat)\n";
|
||||||
|
s_solver << "(reset)\n";
|
||||||
|
std::string s;
|
||||||
|
getline(s_solver, s);
|
||||||
|
if (s == "sat") return s_solver;
|
||||||
|
|
||||||
|
std::stringstream msg;
|
||||||
|
msg << "Unable to communicate with SAT solver, please check its installation or specify a "
|
||||||
|
"different one in VERILATOR_SOLVER environment variable.\n";
|
||||||
|
msg << " ... Tried: $";
|
||||||
|
for (const char* const* arg = cmd; *arg; arg++) msg << ' ' << *arg;
|
||||||
|
msg << '\n';
|
||||||
|
const std::string str = msg.str();
|
||||||
|
VL_WARN_MT("", 0, "randomize", str.c_str());
|
||||||
|
|
||||||
|
while (getline(s_solver, s)) {}
|
||||||
|
return s_solver;
|
||||||
|
}
|
||||||
|
|
||||||
|
//======================================================================
|
||||||
|
// VlRandomizer:: Methods
|
||||||
|
|
||||||
|
void VlRandomVar::emit(std::ostream& s) const { s << m_name; }
|
||||||
|
void VlRandomConst::emit(std::ostream& s) const {
|
||||||
|
s << "#b";
|
||||||
|
for (int i = 0; i < m_width; i++) s << (VL_BITISSET_Q(m_val, m_width - i - 1) ? '1' : '0');
|
||||||
|
}
|
||||||
|
void VlRandomBinOp::emit(std::ostream& s) const {
|
||||||
|
s << '(' << m_op << ' ';
|
||||||
|
m_lhs->emit(s);
|
||||||
|
s << ' ';
|
||||||
|
m_rhs->emit(s);
|
||||||
|
s << ')';
|
||||||
|
}
|
||||||
|
void VlRandomExtract::emit(std::ostream& s) const {
|
||||||
|
s << "((_ extract " << m_idx << ' ' << m_idx << ") ";
|
||||||
|
m_expr->emit(s);
|
||||||
|
s << ')';
|
||||||
|
}
|
||||||
|
bool VlRandomVar::set(std::string&& val) const {
|
||||||
|
VlWide<VL_WQ_WORDS_E> qowp;
|
||||||
|
VL_SET_WQ(qowp, 0ULL);
|
||||||
|
WDataOutP owp = qowp;
|
||||||
|
int obits = width();
|
||||||
|
if (obits > VL_QUADSIZE) owp = reinterpret_cast<WDataOutP>(datap());
|
||||||
|
int i;
|
||||||
|
for (i = 0; val[i] && val[i] != '#'; i++) {}
|
||||||
|
if (val[i++] != '#') return false;
|
||||||
|
switch (val[i++]) {
|
||||||
|
case 'b': _vl_vsss_based(owp, obits, 1, &val[i], 0, val.size() - i); break;
|
||||||
|
case 'o': _vl_vsss_based(owp, obits, 3, &val[i], 0, val.size() - i); break;
|
||||||
|
case 'h': // FALLTHRU
|
||||||
|
case 'x': _vl_vsss_based(owp, obits, 4, &val[i], 0, val.size() - i); break;
|
||||||
|
default:
|
||||||
|
VL_WARN_MT(__FILE__, __LINE__, "randomize",
|
||||||
|
"Internal: Unable to parse solver's randomized number");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (obits <= VL_BYTESIZE) {
|
||||||
|
CData* const p = static_cast<CData*>(datap());
|
||||||
|
*p = VL_CLEAN_II(obits, obits, owp[0]);
|
||||||
|
} else if (obits <= VL_SHORTSIZE) {
|
||||||
|
SData* const p = static_cast<SData*>(datap());
|
||||||
|
*p = VL_CLEAN_II(obits, obits, owp[0]);
|
||||||
|
} else if (obits <= VL_IDATASIZE) {
|
||||||
|
IData* const p = static_cast<IData*>(datap());
|
||||||
|
*p = VL_CLEAN_II(obits, obits, owp[0]);
|
||||||
|
} else if (obits <= VL_QUADSIZE) {
|
||||||
|
QData* const p = static_cast<QData*>(datap());
|
||||||
|
*p = VL_CLEAN_QQ(obits, obits, VL_SET_QW(owp));
|
||||||
|
} else {
|
||||||
|
_vl_clean_inplace_w(obits, owp);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<const VlRandomExpr> VlRandomizer::randomConstraint(VlRNG& rngr, int bits) {
|
||||||
|
unsigned long long hash = VL_RANDOM_RNG_I(rngr) & ((1 << bits) - 1);
|
||||||
|
std::shared_ptr<const VlRandomExpr> concat = nullptr;
|
||||||
|
std::vector<std::shared_ptr<const VlRandomExpr>> varbits;
|
||||||
|
for (const auto& var : m_vars) {
|
||||||
|
for (int i = 0; i < var.second->width(); i++)
|
||||||
|
varbits.emplace_back(std::make_shared<const VlRandomExtract>(var.second, i));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < bits; i++) {
|
||||||
|
std::shared_ptr<const VlRandomExpr> bit = nullptr;
|
||||||
|
for (unsigned j = 0; j * 2 < varbits.size(); j++) {
|
||||||
|
unsigned idx = j + VL_RANDOM_RNG_I(rngr) % (varbits.size() - j);
|
||||||
|
auto sel = varbits[idx];
|
||||||
|
std::swap(varbits[idx], varbits[j]);
|
||||||
|
bit = bit == nullptr ? sel : std::make_shared<const VlRandomBinOp>("bvxor", bit, sel);
|
||||||
|
}
|
||||||
|
concat = concat == nullptr ? bit
|
||||||
|
: std::make_shared<const VlRandomBinOp>("concat", concat, bit);
|
||||||
|
}
|
||||||
|
return std::make_shared<const VlRandomBinOp>(
|
||||||
|
"=", concat, std::make_shared<const VlRandomConst>(hash, bits));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VlRandomizer::next(VlRNG& rngr) {
|
||||||
|
if (m_vars.empty()) return true;
|
||||||
|
std::iostream& f = getSolver();
|
||||||
|
if (!f) return false;
|
||||||
|
|
||||||
|
f << "(set-option :produce-models true)\n";
|
||||||
|
f << "(set-logic QF_BV)\n";
|
||||||
|
for (const auto& var : m_vars) {
|
||||||
|
f << "(declare-fun " << var.second->name() << " () (_ BitVec " << var.second->width()
|
||||||
|
<< "))\n";
|
||||||
|
}
|
||||||
|
for (const std::string& constraint : m_constraints) { f << "(assert " << constraint << ")\n"; }
|
||||||
|
f << "(check-sat)\n";
|
||||||
|
|
||||||
|
bool sat = parseSolution(f);
|
||||||
|
if (!sat) {
|
||||||
|
f << "(reset)\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < _VL_SOLVER_HASH_LEN_TOTAL && sat; i++) {
|
||||||
|
f << "(assert ";
|
||||||
|
randomConstraint(rngr, _VL_SOLVER_HASH_LEN)->emit(f);
|
||||||
|
f << ")\n";
|
||||||
|
f << "\n(check-sat)\n";
|
||||||
|
sat = parseSolution(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
f << "(reset)\n";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VlRandomizer::parseSolution(std::iostream& f) {
|
||||||
|
std::string sat;
|
||||||
|
do { std::getline(f, sat); } while (sat == "");
|
||||||
|
|
||||||
|
if (sat == "unsat") return false;
|
||||||
|
if (sat != "sat") {
|
||||||
|
std::stringstream msg;
|
||||||
|
msg << "Internal: Solver error: " << sat;
|
||||||
|
const std::string str = msg.str();
|
||||||
|
VL_WARN_MT(__FILE__, __LINE__, "randomize", str.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
f << "(get-value (";
|
||||||
|
for (const auto& var : m_vars) f << var.second->name() << ' ';
|
||||||
|
f << "))\n";
|
||||||
|
|
||||||
|
// Quasi-parse S-expression of the form ((x #xVALUE) (y #bVALUE) (z #xVALUE))
|
||||||
|
char c;
|
||||||
|
f >> c;
|
||||||
|
if (c != '(') {
|
||||||
|
VL_WARN_MT(__FILE__, __LINE__, "randomize",
|
||||||
|
"Internal: Unable to parse solver's response: invalid S-expression");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
f >> c;
|
||||||
|
if (c == ')') break;
|
||||||
|
if (c != '(') {
|
||||||
|
VL_WARN_MT(__FILE__, __LINE__, "randomize",
|
||||||
|
"Internal: Unable to parse solver's response: invalid S-expression");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name, value;
|
||||||
|
f >> name;
|
||||||
|
std::getline(f, value, ')');
|
||||||
|
|
||||||
|
auto it = m_vars.find(name);
|
||||||
|
if (it == m_vars.end()) continue;
|
||||||
|
|
||||||
|
it->second->set(std::move(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VlRandomizer::hard(std::string&& constraint) {
|
||||||
|
m_constraints.emplace_back(std::move(constraint));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef VL_DEBUG
|
||||||
|
void VlRandomizer::dump() const {
|
||||||
|
for (const auto& var : m_vars) {
|
||||||
|
VL_PRINTF("Variable (%d): %s\n", var.second->width(), var.second->name());
|
||||||
|
}
|
||||||
|
for (const std::string& c : m_constraints) VL_PRINTF("Constraint: %s\n", c.c_str());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||||
|
//*************************************************************************
|
||||||
|
//
|
||||||
|
// Code available from: https://verilator.org
|
||||||
|
//
|
||||||
|
// Copyright 2024 by Wilson Snyder. This program is free software; you can
|
||||||
|
// redistribute it and/or modify it under the terms of either the GNU Lesser
|
||||||
|
// General Public License Version 3 or the Perl Artistic License Version 2.0.
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
//
|
||||||
|
//*************************************************************************
|
||||||
|
///
|
||||||
|
/// \file
|
||||||
|
/// \brief Verilated randomization header
|
||||||
|
///
|
||||||
|
/// This file is included automatically by Verilator in some of the C++ files
|
||||||
|
/// it generates if randomization features are used.
|
||||||
|
///
|
||||||
|
/// This file is not part of the Verilated public-facing API.
|
||||||
|
/// It is only for internal use.
|
||||||
|
///
|
||||||
|
/// See the internals documentation docs/internals.rst for details.
|
||||||
|
///
|
||||||
|
//*************************************************************************
|
||||||
|
#ifndef VERILATOR_VERILATED_RANDOM_H_
|
||||||
|
#define VERILATOR_VERILATED_RANDOM_H_
|
||||||
|
|
||||||
|
#include "verilated.h"
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
// VlRandomExpr and subclasses represent expressions for the constraint solver.
|
||||||
|
|
||||||
|
class VlRandomExpr VL_NOT_FINAL {
|
||||||
|
public:
|
||||||
|
virtual void emit(std::ostream& s) const = 0;
|
||||||
|
};
|
||||||
|
class VlRandomVar final : public VlRandomExpr {
|
||||||
|
const char* const m_name; // Variable name
|
||||||
|
void* const m_datap; // Reference to variable data
|
||||||
|
const int m_width; // Variable width in bits
|
||||||
|
|
||||||
|
public:
|
||||||
|
VlRandomVar(const char* name, int width, void* datap)
|
||||||
|
: m_name{name}
|
||||||
|
, m_datap{datap}
|
||||||
|
, m_width{width} {}
|
||||||
|
const char* name() const { return m_name; }
|
||||||
|
int width() const { return m_width; }
|
||||||
|
void* datap() const { return m_datap; }
|
||||||
|
bool set(std::string&&) const;
|
||||||
|
void emit(std::ostream& s) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VlRandomConst final : public VlRandomExpr {
|
||||||
|
const QData m_val; // Constant value
|
||||||
|
const int m_width; // Constant width in bits
|
||||||
|
|
||||||
|
public:
|
||||||
|
VlRandomConst(QData val, int width)
|
||||||
|
: m_val{val}
|
||||||
|
, m_width{width} {
|
||||||
|
assert(width <= sizeof(m_val) * 8);
|
||||||
|
}
|
||||||
|
void emit(std::ostream& s) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VlRandomExtract final : public VlRandomExpr {
|
||||||
|
const std::shared_ptr<const VlRandomExpr> m_expr; // Sub-expression
|
||||||
|
const unsigned m_idx; // Extracted index
|
||||||
|
|
||||||
|
public:
|
||||||
|
VlRandomExtract(std::shared_ptr<const VlRandomExpr> expr, unsigned idx)
|
||||||
|
: m_expr{expr}
|
||||||
|
, m_idx{idx} {}
|
||||||
|
void emit(std::ostream& s) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VlRandomBinOp final : public VlRandomExpr {
|
||||||
|
const char* const m_op; // Binary operation identifier
|
||||||
|
const std::shared_ptr<const VlRandomExpr> m_lhs, m_rhs; // Sub-expressions
|
||||||
|
|
||||||
|
public:
|
||||||
|
VlRandomBinOp(const char* op, std::shared_ptr<const VlRandomExpr> lhs,
|
||||||
|
std::shared_ptr<const VlRandomExpr> rhs)
|
||||||
|
: m_op{op}
|
||||||
|
, m_lhs{lhs}
|
||||||
|
, m_rhs{rhs} {}
|
||||||
|
void emit(std::ostream& s) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
// VlRandomizer is the object holding constraints and variable references.
|
||||||
|
|
||||||
|
class VlRandomizer final {
|
||||||
|
// MEMBERS
|
||||||
|
std::vector<std::string> m_constraints; // Solver-dependent constraints
|
||||||
|
std::map<std::string, std::shared_ptr<const VlRandomVar>>
|
||||||
|
m_vars; // Solver-dependent variables
|
||||||
|
|
||||||
|
// PRIVATE METHODS
|
||||||
|
std::shared_ptr<const VlRandomExpr> randomConstraint(VlRNG& rngr, int bits);
|
||||||
|
bool parseSolution(std::iostream& file);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// METHODS
|
||||||
|
// Finds the next solution satisfying the constraints
|
||||||
|
bool next(VlRNG& rngr);
|
||||||
|
template <typename T>
|
||||||
|
void write_var(T& var, int width, const char* name) {
|
||||||
|
auto it = m_vars.find(name);
|
||||||
|
if (it != m_vars.end()) return;
|
||||||
|
m_vars[name] = std::make_shared<const VlRandomVar>(name, width, &var);
|
||||||
|
}
|
||||||
|
void hard(std::string&& constraint);
|
||||||
|
#ifdef VL_DEBUG
|
||||||
|
void dump() const;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // Guard
|
||||||
@@ -67,9 +67,16 @@ void VlDelayScheduler::resume() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!m_zeroDelayed.empty()) {
|
if (!m_zeroDelayed.empty()) {
|
||||||
for (auto&& handle : m_zeroDelayed) handle.resume();
|
// First, we need to move the coroutines out of the queue, as a resumed coroutine can
|
||||||
m_zeroDelayed.clear();
|
// 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;
|
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) {
|
if (!resumed) {
|
||||||
|
|||||||
+18
-11
@@ -32,14 +32,18 @@
|
|||||||
// clang-format off
|
// clang-format off
|
||||||
// Some preprocessor magic to support both Clang and GCC coroutines with both libc++ and libstdc++
|
// Some preprocessor magic to support both Clang and GCC coroutines with both libc++ and libstdc++
|
||||||
#if defined _LIBCPP_VERSION // libc++
|
#if defined _LIBCPP_VERSION // libc++
|
||||||
# if __clang_major__ > 13 // Clang > 13 warns that coroutine types in std::experimental are deprecated
|
# if defined(__has_include) && !__has_include(<coroutine>) && __has_include(<experimental/coroutine>)
|
||||||
# pragma clang diagnostic push
|
# if __clang_major__ > 13 // Clang > 13 warns that coroutine types in std::experimental are deprecated
|
||||||
# pragma clang diagnostic ignored "-Wdeprecated-experimental-coroutine"
|
# 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
|
# endif
|
||||||
# include <experimental/coroutine>
|
|
||||||
namespace std {
|
|
||||||
using namespace experimental; // Bring std::experimental into the std namespace
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
# if defined __clang__ && defined __GLIBCXX__ && !defined __cpp_impl_coroutine
|
# 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++
|
# 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;
|
VerilatedContext& m_context;
|
||||||
VlDelayedCoroutineQueue m_queue; // Coroutines to be restored at a certain simulation time
|
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_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:
|
public:
|
||||||
// CONSTRUCTORS
|
// CONSTRUCTORS
|
||||||
@@ -193,9 +200,9 @@ public:
|
|||||||
VlProcessRef process; // Data of the suspended process, null if not needed
|
VlProcessRef process; // Data of the suspended process, null if not needed
|
||||||
VlDelayedCoroutineQueue& queue;
|
VlDelayedCoroutineQueue& queue;
|
||||||
std::vector<VlCoroutineHandle>& queueZeroDelay;
|
std::vector<VlCoroutineHandle>& queueZeroDelay;
|
||||||
uint64_t delay;
|
const uint64_t delay;
|
||||||
VlDelayPhase phase;
|
const VlDelayPhase phase;
|
||||||
VlFileLineDebug fileline;
|
const VlFileLineDebug fileline;
|
||||||
|
|
||||||
bool await_ready() const { return false; } // Always suspend
|
bool await_ready() const { return false; } // Always suspend
|
||||||
void await_suspend(std::coroutine_handle<> coro) {
|
void await_suspend(std::coroutine_handle<> coro) {
|
||||||
@@ -208,7 +215,7 @@ public:
|
|||||||
void await_resume() const {}
|
void await_resume() const {}
|
||||||
};
|
};
|
||||||
|
|
||||||
VlDelayPhase phase = (delay == 0) ? VlDelayPhase::INACTIVE : VlDelayPhase::ACTIVE;
|
const VlDelayPhase phase = (delay == 0) ? VlDelayPhase::INACTIVE : VlDelayPhase::ACTIVE;
|
||||||
#ifdef VL_DEBUG
|
#ifdef VL_DEBUG
|
||||||
if (phase == VlDelayPhase::INACTIVE) {
|
if (phase == VlDelayPhase::INACTIVE) {
|
||||||
VL_WARN_MT(filename, lineno, VL_UNKNOWN,
|
VL_WARN_MT(filename, lineno, VL_UNKNOWN,
|
||||||
|
|||||||
@@ -30,8 +30,9 @@
|
|||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <type_traits>
|
||||||
#include <unordered_set>
|
#include <map>
|
||||||
|
#include <set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
@@ -294,7 +295,7 @@ private:
|
|||||||
uint64_t m_timeLastDump = 0; // Last time we did a dump
|
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)
|
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
|
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)
|
void addCallbackRecord(std::vector<CallbackRecord>& cbVec, CallbackRecord&& cbRec)
|
||||||
VL_MT_SAFE_EXCLUDES(m_mutex);
|
VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||||
|
|||||||
@@ -633,12 +633,13 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addModel(VerilatedModel* modelp)
|
|||||||
|
|
||||||
// Validate
|
// Validate
|
||||||
if (!newModel) { // LCOV_EXCL_START
|
if (!newModel) { // LCOV_EXCL_START
|
||||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
VL_FATAL_MT(
|
||||||
"The same model has already been added to this trace file");
|
__FILE__, __LINE__, "",
|
||||||
|
"The same model has already been added to this trace file or VerilatedContext");
|
||||||
}
|
}
|
||||||
if (VL_UNCOVERABLE(m_contextp && contextp != m_contextp)) {
|
if (VL_UNCOVERABLE(m_contextp && contextp != m_contextp)) {
|
||||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
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)) {
|
if (VL_UNCOVERABLE(m_didSomeDump)) {
|
||||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||||
|
|||||||
+204
-5
@@ -36,7 +36,6 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_set>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
@@ -415,8 +414,20 @@ public:
|
|||||||
|
|
||||||
static int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_PURE;
|
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>
|
template <std::size_t T_Words>
|
||||||
struct VlWide final {
|
struct VlWide final {
|
||||||
|
static constexpr size_t Words = T_Words;
|
||||||
|
|
||||||
// MEMBERS
|
// MEMBERS
|
||||||
// This should be the only data member, otherwise generated static initializers need updating
|
// This should be the only data member, otherwise generated static initializers need updating
|
||||||
EData m_storage[T_Words]; // Contents of the packed array
|
EData m_storage[T_Words]; // Contents of the packed array
|
||||||
@@ -891,8 +902,8 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T_Value>
|
template <class T_Value, size_t T_MaxSize>
|
||||||
std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
|
std::string VL_TO_STRING(const VlQueue<T_Value, T_MaxSize>& obj) {
|
||||||
return obj.to_string();
|
return obj.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1309,7 +1320,11 @@ public:
|
|||||||
// Similar to 'neq' above, *this = that used for change detection
|
// Similar to 'neq' above, *this = that used for change detection
|
||||||
void assign(const VlUnpacked<T_Value, T_Depth>& that) { *this = that; }
|
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) 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)
|
// inside (set membership operator)
|
||||||
bool inside(const T_Value& value) const {
|
bool inside(const T_Value& value) const {
|
||||||
@@ -1386,7 +1401,7 @@ public:
|
|||||||
VlQueue<T_Key> unique_index(Func with_func) const {
|
VlQueue<T_Key> unique_index(Func with_func) const {
|
||||||
VlQueue<T_Key> out;
|
VlQueue<T_Key> out;
|
||||||
IData index = 0;
|
IData index = 0;
|
||||||
std::unordered_set<T_Value> saw;
|
std::set<T_Value> saw;
|
||||||
for (const auto& i : m_storage) {
|
for (const auto& i : m_storage) {
|
||||||
const auto i_mapped = with_func(index, i);
|
const auto i_mapped = with_func(index, i);
|
||||||
auto it = saw.find(i_mapped);
|
auto it = saw.find(i_mapped);
|
||||||
@@ -1499,6 +1514,15 @@ private:
|
|||||||
return false;
|
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> //
|
template <typename T_Other> //
|
||||||
static bool neq(const T_Other& a, const T_Other& b) {
|
static bool neq(const T_Other& a, const T_Other& b) {
|
||||||
// Base case (T_Other is not VlUnpacked<_, _>), fall back on !=
|
// 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();
|
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
|
// Object that VlDeleter is capable of deleting
|
||||||
|
|
||||||
|
|||||||
+222
-81
@@ -31,11 +31,14 @@
|
|||||||
#include "verilated.h"
|
#include "verilated.h"
|
||||||
#include "verilated_imp.h"
|
#include "verilated_imp.h"
|
||||||
|
|
||||||
|
#include "vltstd/vpi_user.h"
|
||||||
|
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <cstdio>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_set>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -630,6 +633,146 @@ public:
|
|||||||
void invalidate() { m_id = 0; }
|
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 {
|
struct VerilatedVpiTimedCbsCmp final {
|
||||||
// Ordering sets keyed by time, then callback unique id
|
// Ordering sets keyed by time, then callback unique id
|
||||||
bool operator()(const std::pair<QData, uint64_t>& a,
|
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;
|
std::array<VpioCbList, CB_ENUM_MAX_VALUE> m_cbCurrentLists;
|
||||||
VpioFutureCbs m_futureCbs; // Time based callbacks for future timestamps
|
VpioFutureCbs m_futureCbs; // Time based callbacks for future timestamps
|
||||||
VpioFutureCbs m_nextCbs; // cbNextSimTime callbacks
|
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
|
VerilatedVpiError* m_errorInfop = nullptr; // Container for vpi error info
|
||||||
VerilatedAssertOneThread m_assertOne; // Assert only called from single thread
|
VerilatedAssertOneThread m_assertOne; // Assert only called from single thread
|
||||||
uint64_t m_nextCallbackId = 1; // Id to identify callback
|
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() { // Singleton
|
||||||
static VerilatedVpiImp s_s;
|
static VerilatedVpiImp s_s;
|
||||||
@@ -776,7 +921,7 @@ public:
|
|||||||
assertOneCheck();
|
assertOneCheck();
|
||||||
VpioCbList& cbObjList = s().m_cbCurrentLists[cbValueChange];
|
VpioCbList& cbObjList = s().m_cbCurrentLists[cbValueChange];
|
||||||
bool called = false;
|
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;
|
if (cbObjList.empty()) return called;
|
||||||
const auto last = std::prev(cbObjList.end()); // prevent looping over newly added elements
|
const auto last = std::prev(cbObjList.end()); // prevent looping over newly added elements
|
||||||
for (auto it = cbObjList.begin(); true;) {
|
for (auto it = cbObjList.begin(); true;) {
|
||||||
@@ -813,6 +958,17 @@ public:
|
|||||||
}
|
}
|
||||||
static void dumpCbs() VL_MT_UNSAFE_ONE;
|
static void dumpCbs() VL_MT_UNSAFE_ONE;
|
||||||
static VerilatedVpiError* error_info() VL_MT_UNSAFE_ONE; // getter for vpi error info
|
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;
|
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
|
// VerilatedVpiImp implementation
|
||||||
|
|
||||||
@@ -2194,13 +2355,28 @@ bool vl_check_format(const VerilatedVar* varp, const p_vpi_value valuep, const c
|
|||||||
return status;
|
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,
|
void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||||
const char* fullname) {
|
const char* fullname) {
|
||||||
if (!vl_check_format(varp, valuep, fullname, true)) return;
|
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
|
// string data type is dynamic and may vary in size during simulation
|
||||||
static thread_local std::string t_outDynamicStr;
|
static thread_local std::string t_outDynamicStr;
|
||||||
// We used to presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
} else if (valuep->format == vpiBinStrVal) {
|
} else if (valuep->format == vpiBinStrVal) {
|
||||||
valuep->value.str = t_outStr;
|
|
||||||
int bits = varp->packed().elements();
|
int bits = varp->packed().elements();
|
||||||
|
t_outDynamicStr.resize(bits);
|
||||||
const CData* datap = (reinterpret_cast<CData*>(varDatap));
|
const CData* datap = (reinterpret_cast<CData*>(varDatap));
|
||||||
int i;
|
for (size_t i = 0; i < bits; ++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) {
|
|
||||||
const char val = (datap[i >> 3] >> (i & 7)) & 1;
|
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;
|
return;
|
||||||
} else if (valuep->format == vpiOctStrVal) {
|
} else if (valuep->format == vpiOctStrVal) {
|
||||||
valuep->value.str = t_outStr;
|
|
||||||
int chars = (varp->packed().elements() + 2) / 3;
|
int chars = (varp->packed().elements() + 2) / 3;
|
||||||
|
t_outDynamicStr.resize(chars);
|
||||||
const int bytes = VL_BYTES_I(varp->packed().elements());
|
const int bytes = VL_BYTES_I(varp->packed().elements());
|
||||||
const CData* datap = (reinterpret_cast<CData*>(varDatap));
|
const CData* datap = (reinterpret_cast<CData*>(varDatap));
|
||||||
int i;
|
for (size_t i = 0; i < chars; ++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) {
|
|
||||||
const div_t idx = div(i * 3, 8);
|
const div_t idx = div(i * 3, 8);
|
||||||
int val = datap[idx.quot];
|
int val = datap[idx.quot];
|
||||||
if ((idx.quot + 1) < bytes) {
|
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;
|
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;
|
return;
|
||||||
} else if (valuep->format == vpiDecStrVal) {
|
} else if (valuep->format == vpiDecStrVal) {
|
||||||
valuep->value.str = t_outStr;
|
|
||||||
// outStrSz does not include nullptr termination so add one
|
|
||||||
if (varp->vltype() == VLVT_UINT8) {
|
if (varp->vltype() == VLVT_UINT8) {
|
||||||
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%hhu",
|
vl_strprintf(t_outDynamicStr, "%hhu",
|
||||||
static_cast<unsigned char>(*(reinterpret_cast<CData*>(varDatap))));
|
static_cast<unsigned char>(*(reinterpret_cast<CData*>(varDatap))));
|
||||||
return;
|
|
||||||
} else if (varp->vltype() == VLVT_UINT16) {
|
} else if (varp->vltype() == VLVT_UINT16) {
|
||||||
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%hu",
|
vl_strprintf(t_outDynamicStr, "%hu",
|
||||||
static_cast<unsigned short>(*(reinterpret_cast<SData*>(varDatap))));
|
static_cast<unsigned short>(*(reinterpret_cast<SData*>(varDatap))));
|
||||||
return;
|
|
||||||
} else if (varp->vltype() == VLVT_UINT32) {
|
} else if (varp->vltype() == VLVT_UINT32) {
|
||||||
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%u",
|
vl_strprintf(t_outDynamicStr, "%u",
|
||||||
static_cast<unsigned int>(*(reinterpret_cast<IData*>(varDatap))));
|
static_cast<unsigned int>(*(reinterpret_cast<IData*>(varDatap))));
|
||||||
return;
|
|
||||||
} else if (varp->vltype() == VLVT_UINT64) {
|
} else if (varp->vltype() == VLVT_UINT64) {
|
||||||
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%llu",
|
vl_strprintf(t_outDynamicStr, "%llu",
|
||||||
static_cast<unsigned long long>(*(reinterpret_cast<QData*>(varDatap))));
|
static_cast<unsigned long long>(*(reinterpret_cast<QData*>(varDatap))));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
|
||||||
|
return;
|
||||||
} else if (valuep->format == vpiHexStrVal) {
|
} else if (valuep->format == vpiHexStrVal) {
|
||||||
valuep->value.str = t_outStr;
|
|
||||||
int chars = (varp->packed().elements() + 3) >> 2;
|
int chars = (varp->packed().elements() + 3) >> 2;
|
||||||
|
t_outDynamicStr.resize(chars);
|
||||||
const CData* datap = (reinterpret_cast<CData*>(varDatap));
|
const CData* datap = (reinterpret_cast<CData*>(varDatap));
|
||||||
int i;
|
for (size_t i = 0; i < chars; ++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) {
|
|
||||||
char val = (datap[i >> 1] >> ((i & 1) << 2)) & 15;
|
char val = (datap[i >> 1] >> ((i & 1) << 2)) & 15;
|
||||||
if (i == (chars - 1)) {
|
if (i == (chars - 1)) {
|
||||||
// most significant char, mask off nonexistent bits when vector
|
// 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;
|
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;
|
return;
|
||||||
} else if (valuep->format == vpiStringVal) {
|
} else if (valuep->format == vpiStringVal) {
|
||||||
if (varp->vltype() == VLVT_STRING) {
|
if (varp->vltype() == VLVT_STRING) {
|
||||||
@@ -2364,26 +2503,15 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
valuep->value.str = t_outStr;
|
|
||||||
int bytes = VL_BYTES_I(varp->packed().elements());
|
int bytes = VL_BYTES_I(varp->packed().elements());
|
||||||
|
t_outDynamicStr.resize(bytes);
|
||||||
const CData* datap = (reinterpret_cast<CData*>(varDatap));
|
const CData* datap = (reinterpret_cast<CData*>(varDatap));
|
||||||
int i;
|
for (size_t i = 0; i < bytes; ++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) {
|
|
||||||
const char val = datap[bytes - i - 1];
|
const char val = datap[bytes - i - 1];
|
||||||
// other simulators replace [leading?] zero chars with spaces, replicate here.
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
} else if (valuep->format == vpiIntVal) {
|
} 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*/,
|
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););
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_put_value %p %p\n", object, valuep););
|
||||||
VerilatedVpiImp::assertOneCheck();
|
VerilatedVpiImp::assertOneCheck();
|
||||||
VL_VPI_ERROR_RESET_();
|
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");
|
VL_VPI_WARNING_(__FILE__, __LINE__, "Ignoring vpi_put_value with nullptr value pointer");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
PLI_INT32 delay_mode = flags & 0xfff;
|
||||||
if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) {
|
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_DEBUG_IF_PLI(
|
||||||
VL_DBG_MSGF("- vpi: vpi_put_value name=%s fmt=%d vali=%d\n", vop->fullname(),
|
VL_DBG_MSGF("- vpi: vpi_put_value name=%s fmt=%d vali=%d\n", vop->fullname(),
|
||||||
valuep->format, valuep->value.integer);
|
valuep->format, valuep->value.integer);
|
||||||
VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n", vop->varp()->datap(), vop->varDatap()););
|
VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n", vop->varp()->datap(), vop->varDatap()););
|
||||||
|
VerilatedVpiImp::evalNeeded(true);
|
||||||
|
|
||||||
if (VL_UNLIKELY(!vop->varp()->isPublicRW())) {
|
if (VL_UNLIKELY(!vop->varp()->isPublicRW())) {
|
||||||
VL_VPI_WARNING_(__FILE__, __LINE__,
|
VL_VPI_WARNING_(__FILE__, __LINE__,
|
||||||
|
|||||||
@@ -54,6 +54,13 @@ public:
|
|||||||
static QData cbNextDeadline() VL_MT_UNSAFE_ONE;
|
static QData cbNextDeadline() VL_MT_UNSAFE_ONE;
|
||||||
/// Debug dump of callbacks
|
/// Debug dump of callbacks
|
||||||
static void dumpCbs() VL_MT_UNSAFE_ONE;
|
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
|
// Self test, for internal use only
|
||||||
static void selfTest() VL_MT_UNSAFE_ONE;
|
static void selfTest() VL_MT_UNSAFE_ONE;
|
||||||
|
|||||||
@@ -614,8 +614,13 @@ static inline double VL_ROUND(double n) {
|
|||||||
//=========================================================================
|
//=========================================================================
|
||||||
// Time and performance
|
// Time and performance
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace VlOs {
|
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
|
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
|
// Internal: Record CPU time, starting point on construction, and current delta from that
|
||||||
|
|||||||
@@ -99,5 +99,31 @@ uint64_t memUsageBytes() VL_MT_SAFE {
|
|||||||
#endif
|
#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
|
} //namespace VlOs
|
||||||
|
|||||||
@@ -480,6 +480,7 @@ set_target_properties(${verilator} PROPERTIES
|
|||||||
MSVC_RUNTIME_LIBRARY MultiThreaded$<IF:$<CONFIG:Release>,,DebugDLL>
|
MSVC_RUNTIME_LIBRARY MultiThreaded$<IF:$<CONFIG:Release>,,DebugDLL>
|
||||||
#JOB_POOL_LINK one_job # Linking takes lots of resources
|
#JOB_POOL_LINK one_job # Linking takes lots of resources
|
||||||
INTERPROCEDURAL_OPTIMIZATION_RELEASE $<IF:MINGW,FALSE,TRUE>
|
INTERPROCEDURAL_OPTIMIZATION_RELEASE $<IF:MINGW,FALSE,TRUE>
|
||||||
|
INCLUDE_DIRECTORIES ${FLEX_INCLUDE_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
add_dependencies(${verilator}
|
add_dependencies(${verilator}
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ pkgdatadir = @pkgdatadir@
|
|||||||
# Compile options
|
# Compile options
|
||||||
CFG_WITH_CCWARN = @CFG_WITH_CCWARN@
|
CFG_WITH_CCWARN = @CFG_WITH_CCWARN@
|
||||||
CFG_WITH_DEFENV = @CFG_WITH_DEFENV@
|
CFG_WITH_DEFENV = @CFG_WITH_DEFENV@
|
||||||
|
CFG_WITH_SOLVER = @CFG_WITH_SOLVER@
|
||||||
CPPFLAGS += @CPPFLAGS@
|
CPPFLAGS += @CPPFLAGS@
|
||||||
CFLAGS += @CFLAGS@
|
CFLAGS += @CFLAGS@
|
||||||
CXXFLAGS += @CXXFLAGS@
|
CXXFLAGS += @CXXFLAGS@
|
||||||
@@ -125,6 +126,13 @@ ifeq ($(CFG_WITH_DEFENV),yes)
|
|||||||
else
|
else
|
||||||
CPPFLAGS += -DDEFENV_VERILATOR_ROOT=\"$(VERILATOR_ROOT)\"
|
CPPFLAGS += -DDEFENV_VERILATOR_ROOT=\"$(VERILATOR_ROOT)\"
|
||||||
endif
|
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
|
endif
|
||||||
|
|
||||||
HEADERS = $(wildcard V*.h v*.h)
|
HEADERS = $(wildcard V*.h v*.h)
|
||||||
|
|||||||
+2
-2
@@ -68,7 +68,7 @@ class ActiveTopVisitor final : public VNVisitor {
|
|||||||
UINFO(4, " ACTIVE " << nodep << endl);
|
UINFO(4, " ACTIVE " << nodep << endl);
|
||||||
// Remove duplicate clocks and such; sensesp() may change!
|
// Remove duplicate clocks and such; sensesp() may change!
|
||||||
V3Const::constifyExpensiveEdit(nodep);
|
V3Const::constifyExpensiveEdit(nodep);
|
||||||
AstSenTree* const sensesp = nodep->sensesp();
|
AstSenTree* sensesp = nodep->sensesp();
|
||||||
UASSERT_OBJ(sensesp, nodep, "nullptr");
|
UASSERT_OBJ(sensesp, nodep, "nullptr");
|
||||||
if (sensesp->sensesp() && sensesp->sensesp()->isNever()) {
|
if (sensesp->sensesp() && sensesp->sensesp()->isNever()) {
|
||||||
// Never executing. Kill it.
|
// Never executing. Kill it.
|
||||||
@@ -106,7 +106,7 @@ class ActiveTopVisitor final : public VNVisitor {
|
|||||||
// There may be other references to same sense tree,
|
// There may be other references to same sense tree,
|
||||||
// we'll be removing all references when we get to them,
|
// we'll be removing all references when we get to them,
|
||||||
// but don't dangle our pointer yet!
|
// but don't dangle our pointer yet!
|
||||||
pushDeletep(sensesp);
|
VL_DO_DANGLING(pushDeletep(sensesp), sensesp);
|
||||||
}
|
}
|
||||||
nodep->sensesp(wantp);
|
nodep->sensesp(wantp);
|
||||||
}
|
}
|
||||||
|
|||||||
+49
-1
@@ -287,7 +287,7 @@ class AssertVisitor final : public VNVisitor {
|
|||||||
checkifp->isBoundsCheck(true); // To avoid LATCH warning
|
checkifp->isBoundsCheck(true); // To avoid LATCH warning
|
||||||
checkifp->branchPred(VBranchPred::BP_UNLIKELY);
|
checkifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||||
nodep->replaceWith(checkifp);
|
nodep->replaceWith(checkifp);
|
||||||
pushDeletep(nodep);
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
} else {
|
} else {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
@@ -518,6 +518,54 @@ class AssertVisitor final : public VNVisitor {
|
|||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
newPslAssertion(nodep, nodep->failsp());
|
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 {
|
void visit(AstAssertIntrinsic* nodep) override {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
newPslAssertion(nodep, nodep->failsp());
|
newPslAssertion(nodep, nodep->failsp());
|
||||||
|
|||||||
+12
-4
@@ -158,6 +158,13 @@ private:
|
|||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
void visit(AstClockingItem* const nodep) override {
|
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();
|
FileLine* const flp = nodep->fileline();
|
||||||
V3Const::constifyEdit(nodep->skewp());
|
V3Const::constifyEdit(nodep->skewp());
|
||||||
if (!VN_IS(nodep->skewp(), Const)) {
|
if (!VN_IS(nodep->skewp(), Const)) {
|
||||||
@@ -168,9 +175,7 @@ private:
|
|||||||
AstConst* const skewp = VN_AS(nodep->skewp(), Const);
|
AstConst* const skewp = VN_AS(nodep->skewp(), Const);
|
||||||
if (skewp->num().isNegative()) skewp->v3error("Skew cannot be negative");
|
if (skewp->num().isNegative()) skewp->v3error("Skew cannot be negative");
|
||||||
AstNodeExpr* const exprp = nodep->exprp();
|
AstNodeExpr* const exprp = nodep->exprp();
|
||||||
// Get a ref to the sampled/driven variable
|
m_clockingp->addVarsp(varp->unlinkFrBack());
|
||||||
AstVar* const varp = nodep->varp()->unlinkFrBack();
|
|
||||||
m_clockingp->addVarsp(varp);
|
|
||||||
varp->user1p(nodep);
|
varp->user1p(nodep);
|
||||||
if (nodep->direction() == VDirection::OUTPUT) {
|
if (nodep->direction() == VDirection::OUTPUT) {
|
||||||
AstVarRef* const skewedRefp = new AstVarRef{flp, varp, VAccess::READ};
|
AstVarRef* const skewedRefp = new AstVarRef{flp, varp, VAccess::READ};
|
||||||
@@ -224,6 +229,7 @@ private:
|
|||||||
AstVar* const queueVarp = new AstVar{
|
AstVar* const queueVarp = new AstVar{
|
||||||
flp, VVarType::MODULETEMP,
|
flp, VVarType::MODULETEMP,
|
||||||
"__Vqueue__" + m_clockingp->name() + "__DOT__" + varp->name(), queueDtp};
|
"__Vqueue__" + m_clockingp->name() + "__DOT__" + varp->name(), queueDtp};
|
||||||
|
queueVarp->lifetime(VLifetime::STATIC);
|
||||||
m_clockingp->addNextHere(queueVarp);
|
m_clockingp->addNextHere(queueVarp);
|
||||||
// Create a process like this:
|
// Create a process like this:
|
||||||
// always queue.push(<sampled var>);
|
// always queue.push(<sampled var>);
|
||||||
@@ -250,7 +256,7 @@ private:
|
|||||||
} else {
|
} else {
|
||||||
nodep->v3fatal("Invalid direction");
|
nodep->v3fatal("Invalid direction");
|
||||||
}
|
}
|
||||||
pushDeletep(nodep->unlinkFrBack());
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||||
}
|
}
|
||||||
void visit(AstDelay* nodep) override {
|
void visit(AstDelay* nodep) override {
|
||||||
// Only cycle delays are relevant in this stage; also only process once
|
// 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);
|
const std::string delayName = m_cycleDlyNames.get(nodep);
|
||||||
AstVar* const cntVarp = new AstVar{flp, VVarType::BLOCKTEMP, delayName + "__counter",
|
AstVar* const cntVarp = new AstVar{flp, VVarType::BLOCKTEMP, delayName + "__counter",
|
||||||
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
|
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
|
||||||
|
cntVarp->lifetime(VLifetime::AUTOMATIC);
|
||||||
|
cntVarp->funcLocal(true);
|
||||||
AstBegin* const beginp = new AstBegin{flp, delayName + "__block", cntVarp, false, 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 AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, valuep});
|
||||||
beginp->addStmtsp(new AstWhile{
|
beginp->addStmtsp(new AstWhile{
|
||||||
|
|||||||
+71
-19
@@ -253,17 +253,17 @@ public:
|
|||||||
, m_final{false}
|
, m_final{false}
|
||||||
, m_initial{false} {}
|
, m_initial{false} {}
|
||||||
class Extends {};
|
class Extends {};
|
||||||
VBaseOverride(Extends)
|
explicit VBaseOverride(Extends)
|
||||||
: m_extends{true}
|
: m_extends{true}
|
||||||
, m_final{false}
|
, m_final{false}
|
||||||
, m_initial{false} {}
|
, m_initial{false} {}
|
||||||
class Final {};
|
class Final {};
|
||||||
VBaseOverride(Final)
|
explicit VBaseOverride(Final)
|
||||||
: m_extends{false}
|
: m_extends{false}
|
||||||
, m_final{true}
|
, m_final{true}
|
||||||
, m_initial{false} {}
|
, m_initial{false} {}
|
||||||
class Initial {};
|
class Initial {};
|
||||||
VBaseOverride(Initial)
|
explicit VBaseOverride(Initial)
|
||||||
: m_extends{false}
|
: m_extends{false}
|
||||||
, m_final{false}
|
, m_final{false}
|
||||||
, m_initial{true} {}
|
, m_initial{true} {}
|
||||||
@@ -559,6 +559,7 @@ public:
|
|||||||
DYNAMIC_TRIGGER_SCHEDULER,
|
DYNAMIC_TRIGGER_SCHEDULER,
|
||||||
FORK_SYNC,
|
FORK_SYNC,
|
||||||
PROCESS_REFERENCE,
|
PROCESS_REFERENCE,
|
||||||
|
RANDOM_GENERATOR,
|
||||||
// Unsigned and two state; fundamental types
|
// Unsigned and two state; fundamental types
|
||||||
UINT32,
|
UINT32,
|
||||||
UINT64,
|
UINT64,
|
||||||
@@ -592,6 +593,7 @@ public:
|
|||||||
"VlDynamicTriggerScheduler",
|
"VlDynamicTriggerScheduler",
|
||||||
"VlFork",
|
"VlFork",
|
||||||
"VlProcessRef",
|
"VlProcessRef",
|
||||||
|
"VlRandomizer",
|
||||||
"IData",
|
"IData",
|
||||||
"QData",
|
"QData",
|
||||||
"LOGIC_IMPLICIT",
|
"LOGIC_IMPLICIT",
|
||||||
@@ -599,20 +601,13 @@ public:
|
|||||||
return names[m_e];
|
return names[m_e];
|
||||||
}
|
}
|
||||||
const char* dpiType() const {
|
const char* dpiType() const {
|
||||||
static const char* const names[] = {"%E-unk", "svBit",
|
static const char* const names[]
|
||||||
"char", "void*",
|
= {"%E-unk", "svBit", "char", "void*", "char",
|
||||||
"char", "int",
|
"int", "%E-integer", "svLogic", "long long", "double",
|
||||||
"%E-integer", "svLogic",
|
"short", "%E-time", "const char*", "%E-untyped", "dpiScope",
|
||||||
"long long", "double",
|
"const char*", "%E-mtaskstate", "%E-triggervec", "%E-dly-sched", "%E-trig-sched",
|
||||||
"short", "%E-time",
|
"%E-dyn-sched", "%E-fork", "%E-proc-ref", "%E-rand-gen", "IData",
|
||||||
"const char*", "%E-untyped",
|
"QData", "%E-logic-implct", " MAX"};
|
||||||
"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"};
|
|
||||||
return names[m_e];
|
return names[m_e];
|
||||||
}
|
}
|
||||||
static void selfTest() {
|
static void selfTest() {
|
||||||
@@ -652,6 +647,7 @@ public:
|
|||||||
case DYNAMIC_TRIGGER_SCHEDULER: return 0; // opaque
|
case DYNAMIC_TRIGGER_SCHEDULER: return 0; // opaque
|
||||||
case FORK_SYNC: return 0; // opaque
|
case FORK_SYNC: return 0; // opaque
|
||||||
case PROCESS_REFERENCE: return 0; // opaque
|
case PROCESS_REFERENCE: return 0; // opaque
|
||||||
|
case RANDOM_GENERATOR: return 0; // opaque
|
||||||
case UINT32: return 32;
|
case UINT32: return 32;
|
||||||
case UINT64: return 64;
|
case UINT64: return 64;
|
||||||
default: return 0;
|
default: return 0;
|
||||||
@@ -691,7 +687,8 @@ public:
|
|||||||
return (m_e == EVENT || m_e == STRING || m_e == SCOPEPTR || m_e == CHARPTR
|
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 == MTASKSTATE || m_e == TRIGGERVEC || m_e == DELAY_SCHEDULER
|
||||||
|| m_e == TRIGGER_SCHEDULER || m_e == DYNAMIC_TRIGGER_SCHEDULER || m_e == FORK_SYNC
|
|| 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 isDouble() const VL_MT_SAFE { return m_e == DOUBLE; }
|
||||||
bool isEvent() const { return m_e == EVENT; }
|
bool isEvent() const { return m_e == EVENT; }
|
||||||
@@ -903,7 +900,7 @@ public:
|
|||||||
TRIWIRE,
|
TRIWIRE,
|
||||||
TRI0,
|
TRI0,
|
||||||
TRI1,
|
TRI1,
|
||||||
PORT, // Temp type used in parser only
|
PORT, // Used in parser and V3Fork to recognize ports
|
||||||
BLOCKTEMP,
|
BLOCKTEMP,
|
||||||
MODULETEMP,
|
MODULETEMP,
|
||||||
STMTTEMP,
|
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 {
|
class VCaseType final {
|
||||||
public:
|
public:
|
||||||
enum en : uint8_t { CT_CASE, CT_CASEX, CT_CASEZ, CT_CASEINSIDE };
|
enum en : uint8_t { CT_CASE, CT_CASEX, CT_CASEZ, CT_CASEINSIDE };
|
||||||
|
|||||||
+48
-13
@@ -89,8 +89,8 @@ public:
|
|||||||
virtual AstNodeDType* subDTypep() const VL_MT_SAFE { return nullptr; }
|
virtual AstNodeDType* subDTypep() const VL_MT_SAFE { return nullptr; }
|
||||||
virtual bool isFourstate() const;
|
virtual bool isFourstate() const;
|
||||||
// Ideally an IEEE $typename
|
// Ideally an IEEE $typename
|
||||||
virtual string prettyDTypeName() const { return prettyTypeName(); }
|
virtual string prettyDTypeName(bool) const { return prettyTypeName(); }
|
||||||
string prettyDTypeNameQ() const { return "'" + prettyDTypeName() + "'"; }
|
string prettyDTypeNameQ() const { return "'" + prettyDTypeName(false) + "'"; }
|
||||||
//
|
//
|
||||||
// Changing the width may confuse the data type resolution, so must clear
|
// Changing the width may confuse the data type resolution, so must clear
|
||||||
// TypeTable cache after use.
|
// TypeTable cache after use.
|
||||||
@@ -215,6 +215,7 @@ public:
|
|||||||
int uniqueNum() const { return m_uniqueNum; }
|
int uniqueNum() const { return m_uniqueNum; }
|
||||||
void dump(std::ostream& str) const override;
|
void dump(std::ostream& str) const override;
|
||||||
void dumpJson(std::ostream& str) const override;
|
void dumpJson(std::ostream& str) const override;
|
||||||
|
string prettyDTypeName(bool) const override;
|
||||||
bool isCompound() const override { return !packed(); }
|
bool isCompound() const override { return !packed(); }
|
||||||
// For basicp() we reuse the size to indicate a "fake" basic type of same size
|
// For basicp() we reuse the size to indicate a "fake" basic type of same size
|
||||||
AstBasicDType* basicp() const override {
|
AstBasicDType* basicp() const override {
|
||||||
@@ -313,7 +314,7 @@ public:
|
|||||||
return asamep->subDTypep()
|
return asamep->subDTypep()
|
||||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
||||||
}
|
}
|
||||||
string prettyDTypeName() const override;
|
string prettyDTypeName(bool full) const override;
|
||||||
void dumpSmall(std::ostream& str) const override;
|
void dumpSmall(std::ostream& str) const override;
|
||||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||||
AstNodeDType* getChild2DTypep() const override { return keyChildDTypep(); }
|
AstNodeDType* getChild2DTypep() const override { return keyChildDTypep(); }
|
||||||
@@ -387,7 +388,7 @@ public:
|
|||||||
return type() == samep->type() && same(samep);
|
return type() == samep->type() && same(samep);
|
||||||
}
|
}
|
||||||
string name() const override VL_MT_STABLE { return m.m_keyword.ascii(); }
|
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 {
|
const char* broken() const override {
|
||||||
BROKEN_RTN(dtypep() != this);
|
BROKEN_RTN(dtypep() != this);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -428,6 +429,9 @@ public:
|
|||||||
bool isDynamicTriggerScheduler() const VL_MT_SAFE {
|
bool isDynamicTriggerScheduler() const VL_MT_SAFE {
|
||||||
return keyword() == VBasicDTypeKwd::DYNAMIC_TRIGGER_SCHEDULER;
|
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 isOpaque() const VL_MT_SAFE { return keyword().isOpaque(); }
|
||||||
bool isString() const VL_MT_SAFE { return keyword().isString(); }
|
bool isString() const VL_MT_SAFE { return keyword().isString(); }
|
||||||
bool isZeroInit() const { return keyword().isZeroInit(); }
|
bool isZeroInit() const { return keyword().isZeroInit(); }
|
||||||
@@ -501,7 +505,7 @@ public:
|
|||||||
}
|
}
|
||||||
bool similarDType(const AstNodeDType* samep) const override { return same(samep); }
|
bool similarDType(const AstNodeDType* samep) const override { return same(samep); }
|
||||||
string name() const override VL_MT_STABLE { return m_name; }
|
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
|
// METHODS
|
||||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
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 dump(std::ostream& str = std::cout) const override;
|
||||||
void dumpJson(std::ostream& str = std::cout) const override;
|
void dumpJson(std::ostream& str = std::cout) const override;
|
||||||
void dumpSmall(std::ostream& str) const override;
|
void dumpSmall(std::ostream& str) const override;
|
||||||
|
string prettyDTypeName(bool full) const override;
|
||||||
string name() const override VL_MT_STABLE;
|
string name() const override VL_MT_STABLE;
|
||||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||||
@@ -716,7 +721,7 @@ public:
|
|||||||
return asamep->subDTypep()
|
return asamep->subDTypep()
|
||||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
||||||
}
|
}
|
||||||
string prettyDTypeName() const override;
|
string prettyDTypeName(bool full) const override;
|
||||||
void dumpSmall(std::ostream& str) const override;
|
void dumpSmall(std::ostream& str) const override;
|
||||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||||
AstNodeDType* subDTypep() const override VL_MT_STABLE {
|
AstNodeDType* subDTypep() const override VL_MT_STABLE {
|
||||||
@@ -806,6 +811,7 @@ public:
|
|||||||
void dump(std::ostream& str = std::cout) const override;
|
void dump(std::ostream& str = std::cout) const override;
|
||||||
void dumpJson(std::ostream& str = std::cout) const override;
|
void dumpJson(std::ostream& str = std::cout) const override;
|
||||||
void dumpSmall(std::ostream& str) const override;
|
void dumpSmall(std::ostream& str) const override;
|
||||||
|
string prettyDTypeName(bool full) const override;
|
||||||
// METHODS
|
// METHODS
|
||||||
AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep()->basicp(); }
|
AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep()->basicp(); }
|
||||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return subDTypep()->skipRefp(); }
|
AstNodeDType* skipRefp() const override VL_MT_STABLE { return subDTypep()->skipRefp(); }
|
||||||
@@ -814,9 +820,9 @@ public:
|
|||||||
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||||
int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); }
|
int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); }
|
||||||
int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); }
|
int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); }
|
||||||
int itemCount() const {
|
size_t itemCount() const {
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
for (AstNode* itemp = itemsp(); itemp; itemp = itemp->nextp()) count++;
|
for (AstNode* itemp = itemsp(); itemp; itemp = itemp->nextp()) ++count;
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
bool isCompound() const override { return false; }
|
bool isCompound() const override { return false; }
|
||||||
@@ -959,6 +965,30 @@ public:
|
|||||||
return false;
|
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 {
|
class AstParamTypeDType final : public AstNodeDType {
|
||||||
// Parents: MODULE
|
// Parents: MODULE
|
||||||
// A parameter type statement; much like a var or typedef
|
// A parameter type statement; much like a var or typedef
|
||||||
@@ -1066,7 +1096,7 @@ public:
|
|||||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
||||||
}
|
}
|
||||||
void dumpSmall(std::ostream& str) const override;
|
void dumpSmall(std::ostream& str) const override;
|
||||||
string prettyDTypeName() const override;
|
string prettyDTypeName(bool full) const override;
|
||||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||||
AstNodeDType* subDTypep() const override VL_MT_STABLE {
|
AstNodeDType* subDTypep() const override VL_MT_STABLE {
|
||||||
return m_refDTypep ? m_refDTypep : childDTypep();
|
return m_refDTypep ? m_refDTypep : childDTypep();
|
||||||
@@ -1129,8 +1159,8 @@ public:
|
|||||||
void dumpJson(std::ostream& str = std::cout) const override;
|
void dumpJson(std::ostream& str = std::cout) const override;
|
||||||
void dumpSmall(std::ostream& str) const override;
|
void dumpSmall(std::ostream& str) const override;
|
||||||
string name() const override VL_MT_STABLE { return m_name; }
|
string name() const override VL_MT_STABLE { return m_name; }
|
||||||
string prettyDTypeName() const override {
|
string prettyDTypeName(bool full) const override {
|
||||||
return subDTypep() ? prettyName(subDTypep()->name()) : prettyName();
|
return subDTypep() ? prettyName(subDTypep()->prettyDTypeName(full)) : prettyName();
|
||||||
}
|
}
|
||||||
AstBasicDType* basicp() const override VL_MT_STABLE {
|
AstBasicDType* basicp() const override VL_MT_STABLE {
|
||||||
return subDTypep() ? subDTypep()->basicp() : nullptr;
|
return subDTypep() ? subDTypep()->basicp() : nullptr;
|
||||||
@@ -1262,6 +1292,11 @@ public:
|
|||||||
refDTypep(nullptr);
|
refDTypep(nullptr);
|
||||||
dtypep(nullptr); // V3Width will resolve
|
dtypep(nullptr); // V3Width will resolve
|
||||||
}
|
}
|
||||||
|
AstUnsizedArrayDType(FileLine* fl, AstNodeDType* dtp)
|
||||||
|
: ASTGEN_SUPER_UnsizedArrayDType(fl) {
|
||||||
|
refDTypep(dtp);
|
||||||
|
dtypep(nullptr); // V3Width will resolve
|
||||||
|
}
|
||||||
ASTGEN_MEMBERS_AstUnsizedArrayDType;
|
ASTGEN_MEMBERS_AstUnsizedArrayDType;
|
||||||
const char* broken() const override {
|
const char* broken() const override {
|
||||||
BROKEN_RTN(!((m_refDTypep && !childDTypep()) || (!m_refDTypep && childDTypep())));
|
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, VFlagChildDType, AstNodeDType* dtp, AstRange* rangep);
|
||||||
inline AstPackArrayDType(FileLine* fl, AstNodeDType* dtp, AstRange* rangep);
|
inline AstPackArrayDType(FileLine* fl, AstNodeDType* dtp, AstRange* rangep);
|
||||||
ASTGEN_MEMBERS_AstPackArrayDType;
|
ASTGEN_MEMBERS_AstPackArrayDType;
|
||||||
string prettyDTypeName() const override;
|
string prettyDTypeName(bool full) const override;
|
||||||
bool isCompound() const override { return false; }
|
bool isCompound() const override { return false; }
|
||||||
};
|
};
|
||||||
class AstUnpackArrayDType final : public AstNodeArrayDType {
|
class AstUnpackArrayDType final : public AstNodeArrayDType {
|
||||||
@@ -1384,7 +1419,7 @@ public:
|
|||||||
widthFromSub(subDTypep());
|
widthFromSub(subDTypep());
|
||||||
}
|
}
|
||||||
ASTGEN_MEMBERS_AstUnpackArrayDType;
|
ASTGEN_MEMBERS_AstUnpackArrayDType;
|
||||||
string prettyDTypeName() const override;
|
string prettyDTypeName(bool full) const override;
|
||||||
bool same(const AstNode* samep) const override {
|
bool same(const AstNode* samep) const override {
|
||||||
const AstUnpackArrayDType* const sp = VN_DBG_AS(samep, UnpackArrayDType);
|
const AstUnpackArrayDType* const sp = VN_DBG_AS(samep, UnpackArrayDType);
|
||||||
return m_isCompound == sp->m_isCompound;
|
return m_isCompound == sp->m_isCompound;
|
||||||
|
|||||||
+47
-5
@@ -56,6 +56,7 @@ public:
|
|||||||
virtual string emitVerilog() = 0; /// Format string for verilog writing; see V3EmitV
|
virtual string emitVerilog() = 0; /// Format string for verilog writing; see V3EmitV
|
||||||
// For documentation on emitC format see EmitCFunc::emitOpName
|
// For documentation on emitC format see EmitCFunc::emitOpName
|
||||||
virtual string emitC() = 0;
|
virtual string emitC() = 0;
|
||||||
|
virtual string emitSMT() const { V3ERROR_NA_RETURN(""); };
|
||||||
virtual string emitSimpleOperator() { return ""; } // "" means not ok to use
|
virtual string emitSimpleOperator() { return ""; } // "" means not ok to use
|
||||||
virtual bool emitCheckMaxWords() { return false; } // Check VL_MULS_MAX_WORDS
|
virtual bool emitCheckMaxWords() { return false; } // Check VL_MULS_MAX_WORDS
|
||||||
virtual bool cleanOut() const = 0; // True if output has extra upper bits zero
|
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]
|
// @astgen op3 := exprp : List[AstNode]
|
||||||
public:
|
public:
|
||||||
AstWith(FileLine* fl, AstLambdaArgRef* indexArgRefp, AstLambdaArgRef* valueArgRefp,
|
AstWith(FileLine* fl, AstLambdaArgRef* indexArgRefp, AstLambdaArgRef* valueArgRefp,
|
||||||
AstNodeExpr* exprp)
|
AstNode* exprp)
|
||||||
: ASTGEN_SUPER_With(fl) {
|
: ASTGEN_SUPER_With(fl) {
|
||||||
this->indexArgRefp(indexArgRefp);
|
this->indexArgRefp(indexArgRefp);
|
||||||
this->valueArgRefp(valueArgRefp);
|
this->valueArgRefp(valueArgRefp);
|
||||||
@@ -2282,13 +2283,13 @@ class AstWithParse final : public AstNodeExpr {
|
|||||||
// Replaced with AstWith
|
// Replaced with AstWith
|
||||||
// Parents: expr|stmt
|
// Parents: expr|stmt
|
||||||
// Children: funcref, expr
|
// Children: funcref, expr
|
||||||
// @astgen op1 := funcrefp : AstNode
|
// @astgen op1 := funcrefp : AstNodeExpr
|
||||||
// @astgen op2 := exprp : Optional[AstNodeExpr]
|
// @astgen op2 := exprsp : List[AstNode]
|
||||||
public:
|
public:
|
||||||
AstWithParse(FileLine* fl, AstNode* funcrefp, AstNodeExpr* exprp)
|
AstWithParse(FileLine* fl, AstNodeExpr* funcrefp, AstNode* exprsp)
|
||||||
: ASTGEN_SUPER_WithParse(fl) {
|
: ASTGEN_SUPER_WithParse(fl) {
|
||||||
this->funcrefp(funcrefp);
|
this->funcrefp(funcrefp);
|
||||||
this->exprp(exprp);
|
this->addExprsp(exprsp);
|
||||||
}
|
}
|
||||||
ASTGEN_MEMBERS_AstWithParse;
|
ASTGEN_MEMBERS_AstWithParse;
|
||||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||||
@@ -2401,6 +2402,7 @@ public:
|
|||||||
out.opConcat(lhs, rhs);
|
out.opConcat(lhs, rhs);
|
||||||
}
|
}
|
||||||
string emitC() override { return "VL_CONCAT_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
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 cleanOut() const override { return true; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
bool cleanRhs() const override { return true; }
|
bool cleanRhs() const override { return true; }
|
||||||
@@ -2447,6 +2449,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f/ %r)"; }
|
string emitVerilog() override { return "%k(%l %f/ %r)"; }
|
||||||
string emitC() override { return "VL_DIV_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
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 cleanOut() const override { return false; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
bool cleanRhs() const override { return true; }
|
bool cleanRhs() const override { return true; }
|
||||||
@@ -2493,6 +2496,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f/ %r)"; }
|
string emitVerilog() override { return "%k(%l %f/ %r)"; }
|
||||||
string emitC() override { return "VL_DIVS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
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 cleanOut() const override { return false; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
bool cleanRhs() const override { return true; }
|
bool cleanRhs() const override { return true; }
|
||||||
@@ -2519,6 +2523,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f==? %r)"; }
|
string emitVerilog() override { return "%k(%l %f==? %r)"; }
|
||||||
string emitC() override { return "VL_EQ_%lq(%lW, %P, %li, %ri)"; }
|
string emitC() override { return "VL_EQ_%lq(%lW, %P, %li, %ri)"; }
|
||||||
|
string emitSMT() const override { return "(= %l %r)"; }
|
||||||
string emitSimpleOperator() override { return "=="; }
|
string emitSimpleOperator() override { return "=="; }
|
||||||
bool cleanOut() const override { return true; }
|
bool cleanOut() const override { return true; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
@@ -2641,6 +2646,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f> %r)"; }
|
string emitVerilog() override { return "%k(%l %f> %r)"; }
|
||||||
string emitC() override { return "VL_GT_%lq(%lW, %P, %li, %ri)"; }
|
string emitC() override { return "VL_GT_%lq(%lW, %P, %li, %ri)"; }
|
||||||
|
string emitSMT() const override { return "(bvugt %l %r)"; }
|
||||||
string emitSimpleOperator() override { return ">"; }
|
string emitSimpleOperator() override { return ">"; }
|
||||||
bool cleanOut() const override { return true; }
|
bool cleanOut() const override { return true; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
@@ -2711,6 +2717,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f> %r)"; }
|
string emitVerilog() override { return "%k(%l %f> %r)"; }
|
||||||
string emitC() override { return "VL_GTS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
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 ""; }
|
string emitSimpleOperator() override { return ""; }
|
||||||
bool cleanOut() const override { return true; }
|
bool cleanOut() const override { return true; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
@@ -2734,6 +2741,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f>= %r)"; }
|
string emitVerilog() override { return "%k(%l %f>= %r)"; }
|
||||||
string emitC() override { return "VL_GTE_%lq(%lW, %P, %li, %ri)"; }
|
string emitC() override { return "VL_GTE_%lq(%lW, %P, %li, %ri)"; }
|
||||||
|
string emitSMT() const override { return "(bvuge %l %r)"; }
|
||||||
string emitSimpleOperator() override { return ">="; }
|
string emitSimpleOperator() override { return ">="; }
|
||||||
bool cleanOut() const override { return true; }
|
bool cleanOut() const override { return true; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
@@ -2804,6 +2812,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f>= %r)"; }
|
string emitVerilog() override { return "%k(%l %f>= %r)"; }
|
||||||
string emitC() override { return "VL_GTES_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
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 ""; }
|
string emitSimpleOperator() override { return ""; }
|
||||||
bool cleanOut() const override { return true; }
|
bool cleanOut() const override { return true; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
@@ -2827,6 +2836,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f&& %r)"; }
|
string emitVerilog() override { return "%k(%l %f&& %r)"; }
|
||||||
string emitC() override { return "VL_LOGAND_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
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 "&&"; }
|
string emitSimpleOperator() override { return "&&"; }
|
||||||
bool cleanOut() const override { return true; }
|
bool cleanOut() const override { return true; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
@@ -2850,6 +2860,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f-> %r)"; }
|
string emitVerilog() override { return "%k(%l %f-> %r)"; }
|
||||||
string emitC() override { return "VL_LOGIF_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
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 "->"; }
|
string emitSimpleOperator() override { return "->"; }
|
||||||
bool cleanOut() const override { return true; }
|
bool cleanOut() const override { return true; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
@@ -2873,6 +2884,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f|| %r)"; }
|
string emitVerilog() override { return "%k(%l %f|| %r)"; }
|
||||||
string emitC() override { return "VL_LOGOR_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
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 "||"; }
|
string emitSimpleOperator() override { return "||"; }
|
||||||
bool cleanOut() const override { return true; }
|
bool cleanOut() const override { return true; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
@@ -2896,6 +2908,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f< %r)"; }
|
string emitVerilog() override { return "%k(%l %f< %r)"; }
|
||||||
string emitC() override { return "VL_LT_%lq(%lW, %P, %li, %ri)"; }
|
string emitC() override { return "VL_LT_%lq(%lW, %P, %li, %ri)"; }
|
||||||
|
string emitSMT() const override { return "(bvult %l %r)"; }
|
||||||
string emitSimpleOperator() override { return "<"; }
|
string emitSimpleOperator() override { return "<"; }
|
||||||
bool cleanOut() const override { return true; }
|
bool cleanOut() const override { return true; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
@@ -2966,6 +2979,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f< %r)"; }
|
string emitVerilog() override { return "%k(%l %f< %r)"; }
|
||||||
string emitC() override { return "VL_LTS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
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 ""; }
|
string emitSimpleOperator() override { return ""; }
|
||||||
bool cleanOut() const override { return true; }
|
bool cleanOut() const override { return true; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
@@ -2989,6 +3003,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f<= %r)"; }
|
string emitVerilog() override { return "%k(%l %f<= %r)"; }
|
||||||
string emitC() override { return "VL_LTE_%lq(%lW, %P, %li, %ri)"; }
|
string emitC() override { return "VL_LTE_%lq(%lW, %P, %li, %ri)"; }
|
||||||
|
string emitSMT() const override { return "(bvule %l %r)"; }
|
||||||
string emitSimpleOperator() override { return "<="; }
|
string emitSimpleOperator() override { return "<="; }
|
||||||
bool cleanOut() const override { return true; }
|
bool cleanOut() const override { return true; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
@@ -3059,6 +3074,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f<= %r)"; }
|
string emitVerilog() override { return "%k(%l %f<= %r)"; }
|
||||||
string emitC() override { return "VL_LTES_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
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 ""; }
|
string emitSimpleOperator() override { return ""; }
|
||||||
bool cleanOut() const override { return true; }
|
bool cleanOut() const override { return true; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
@@ -3082,6 +3098,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f%% %r)"; }
|
string emitVerilog() override { return "%k(%l %f%% %r)"; }
|
||||||
string emitC() override { return "VL_MODDIV_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
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 cleanOut() const override { return false; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
bool cleanRhs() const override { return true; }
|
bool cleanRhs() const override { return true; }
|
||||||
@@ -3104,6 +3121,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f%% %r)"; }
|
string emitVerilog() override { return "%k(%l %f%% %r)"; }
|
||||||
string emitC() override { return "VL_MODDIVS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
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 cleanOut() const override { return false; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
bool cleanRhs() const override { return true; }
|
bool cleanRhs() const override { return true; }
|
||||||
@@ -3277,6 +3295,9 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%f{%r{%k%l}}"; }
|
string emitVerilog() override { return "%f{%r{%k%l}}"; }
|
||||||
string emitC() override { return "VL_REPLICATE_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
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 cleanOut() const override { return false; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
bool cleanRhs() const override { return true; }
|
bool cleanRhs() const override { return true; }
|
||||||
@@ -3325,6 +3346,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f<< %r)"; }
|
string emitVerilog() override { return "%k(%l %f<< %r)"; }
|
||||||
string emitC() override { return "VL_SHIFTL_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
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 {
|
string emitSimpleOperator() override {
|
||||||
return (rhsp()->isWide() || rhsp()->isQuad()) ? "" : "<<";
|
return (rhsp()->isWide() || rhsp()->isQuad()) ? "" : "<<";
|
||||||
}
|
}
|
||||||
@@ -3373,6 +3395,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f>> %r)"; }
|
string emitVerilog() override { return "%k(%l %f>> %r)"; }
|
||||||
string emitC() override { return "VL_SHIFTR_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
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 {
|
string emitSimpleOperator() override {
|
||||||
return (rhsp()->isWide() || rhsp()->isQuad()) ? "" : ">>";
|
return (rhsp()->isWide() || rhsp()->isQuad()) ? "" : ">>";
|
||||||
}
|
}
|
||||||
@@ -3425,6 +3448,7 @@ public:
|
|||||||
out.opShiftRS(lhs, rhs, lhsp()->widthMinV());
|
out.opShiftRS(lhs, rhs, lhsp()->widthMinV());
|
||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f>>> %r)"; }
|
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 emitC() override { return "VL_SHIFTRS_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
||||||
string emitSimpleOperator() override { return ""; }
|
string emitSimpleOperator() override { return ""; }
|
||||||
bool cleanOut() const override { return false; }
|
bool cleanOut() const override { return false; }
|
||||||
@@ -3476,6 +3500,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f- %r)"; }
|
string emitVerilog() override { return "%k(%l %f- %r)"; }
|
||||||
string emitC() override { return "VL_SUB_%lq(%lW, %P, %li, %ri)"; }
|
string emitC() override { return "VL_SUB_%lq(%lW, %P, %li, %ri)"; }
|
||||||
|
string emitSMT() const override { return "(bvsub %l %r)"; }
|
||||||
string emitSimpleOperator() override { return "-"; }
|
string emitSimpleOperator() override { return "-"; }
|
||||||
bool cleanOut() const override { return false; }
|
bool cleanOut() const override { return false; }
|
||||||
bool cleanLhs() const override { return false; }
|
bool cleanLhs() const override { return false; }
|
||||||
@@ -3552,6 +3577,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f== %r)"; }
|
string emitVerilog() override { return "%k(%l %f== %r)"; }
|
||||||
string emitC() override { return "VL_EQ_%lq(%lW, %P, %li, %ri)"; }
|
string emitC() override { return "VL_EQ_%lq(%lW, %P, %li, %ri)"; }
|
||||||
|
string emitSMT() const override { return "(= %l %r)"; }
|
||||||
string emitSimpleOperator() override { return "=="; }
|
string emitSimpleOperator() override { return "=="; }
|
||||||
bool cleanOut() const override { return true; }
|
bool cleanOut() const override { return true; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
@@ -3668,6 +3694,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f<-> %r)"; }
|
string emitVerilog() override { return "%k(%l %f<-> %r)"; }
|
||||||
string emitC() override { return "VL_LOGEQ_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
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 "<->"; }
|
string emitSimpleOperator() override { return "<->"; }
|
||||||
bool cleanOut() const override { return true; }
|
bool cleanOut() const override { return true; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
@@ -3692,6 +3719,7 @@ public:
|
|||||||
string emitVerilog() override { return "%k(%l %f!= %r)"; }
|
string emitVerilog() override { return "%k(%l %f!= %r)"; }
|
||||||
string emitC() override { return "VL_NEQ_%lq(%lW, %P, %li, %ri)"; }
|
string emitC() override { return "VL_NEQ_%lq(%lW, %P, %li, %ri)"; }
|
||||||
string emitSimpleOperator() override { return "!="; }
|
string emitSimpleOperator() override { return "!="; }
|
||||||
|
string emitSMT() const override { return "(not (= %l %r))"; }
|
||||||
bool cleanOut() const override { return true; }
|
bool cleanOut() const override { return true; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
bool cleanRhs() const override { return true; }
|
bool cleanRhs() const override { return true; }
|
||||||
@@ -3809,6 +3837,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f+ %r)"; }
|
string emitVerilog() override { return "%k(%l %f+ %r)"; }
|
||||||
string emitC() override { return "VL_ADD_%lq(%lW, %P, %li, %ri)"; }
|
string emitC() override { return "VL_ADD_%lq(%lW, %P, %li, %ri)"; }
|
||||||
|
string emitSMT() const override { return "(bvadd %l %r)"; }
|
||||||
string emitSimpleOperator() override { return "+"; }
|
string emitSimpleOperator() override { return "+"; }
|
||||||
bool cleanOut() const override { return false; }
|
bool cleanOut() const override { return false; }
|
||||||
bool cleanLhs() const override { return false; }
|
bool cleanLhs() const override { return false; }
|
||||||
@@ -3855,6 +3884,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f& %r)"; }
|
string emitVerilog() override { return "%k(%l %f& %r)"; }
|
||||||
string emitC() override { return "VL_AND_%lq(%lW, %P, %li, %ri)"; }
|
string emitC() override { return "VL_AND_%lq(%lW, %P, %li, %ri)"; }
|
||||||
|
string emitSMT() const override { return "(bvand %l %r)"; }
|
||||||
string emitSimpleOperator() override { return "&"; }
|
string emitSimpleOperator() override { return "&"; }
|
||||||
bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
|
bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
|
||||||
bool cleanLhs() const override { return false; }
|
bool cleanLhs() const override { return false; }
|
||||||
@@ -3878,6 +3908,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f* %r)"; }
|
string emitVerilog() override { return "%k(%l %f* %r)"; }
|
||||||
string emitC() override { return "VL_MUL_%lq(%lW, %P, %li, %ri)"; }
|
string emitC() override { return "VL_MUL_%lq(%lW, %P, %li, %ri)"; }
|
||||||
|
string emitSMT() const override { return "(bvmul %l %r)"; }
|
||||||
string emitSimpleOperator() override { return "*"; }
|
string emitSimpleOperator() override { return "*"; }
|
||||||
bool cleanOut() const override { return false; }
|
bool cleanOut() const override { return false; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
@@ -3950,6 +3981,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f| %r)"; }
|
string emitVerilog() override { return "%k(%l %f| %r)"; }
|
||||||
string emitC() override { return "VL_OR_%lq(%lW, %P, %li, %ri)"; }
|
string emitC() override { return "VL_OR_%lq(%lW, %P, %li, %ri)"; }
|
||||||
|
string emitSMT() const override { return "(bvor %l %r)"; }
|
||||||
string emitSimpleOperator() override { return "|"; }
|
string emitSimpleOperator() override { return "|"; }
|
||||||
bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
|
bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
|
||||||
bool cleanLhs() const override { return false; }
|
bool cleanLhs() const override { return false; }
|
||||||
@@ -3973,6 +4005,7 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%k(%l %f^ %r)"; }
|
string emitVerilog() override { return "%k(%l %f^ %r)"; }
|
||||||
string emitC() override { return "VL_XOR_%lq(%lW, %P, %li, %ri)"; }
|
string emitC() override { return "VL_XOR_%lq(%lW, %P, %li, %ri)"; }
|
||||||
|
string emitSMT() const override { return "(bvxor %l %r)"; }
|
||||||
string emitSimpleOperator() override { return "^"; }
|
string emitSimpleOperator() override { return "^"; }
|
||||||
bool cleanOut() const override { return false; } // Lclean && Rclean
|
bool cleanOut() const override { return false; } // Lclean && Rclean
|
||||||
bool cleanLhs() const override { return false; }
|
bool cleanLhs() const override { return false; }
|
||||||
@@ -4895,6 +4928,9 @@ public:
|
|||||||
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAssign(lhs); }
|
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAssign(lhs); }
|
||||||
string emitVerilog() override { return "%l"; }
|
string emitVerilog() override { return "%l"; }
|
||||||
string emitC() override { return "VL_EXTEND_%nq%lq(%nw,%lw, %P, %li)"; }
|
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 cleanOut() const override { return true; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
bool sizeMattersLhs() const override {
|
bool sizeMattersLhs() const override {
|
||||||
@@ -4918,6 +4954,9 @@ public:
|
|||||||
}
|
}
|
||||||
string emitVerilog() override { return "%l"; }
|
string emitVerilog() override { return "%l"; }
|
||||||
string emitC() override { return "VL_EXTENDS_%nq%lq(%nw,%lw, %P, %li)"; }
|
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 cleanOut() const override { return false; }
|
||||||
bool cleanLhs() const override { return true; }
|
bool cleanLhs() const override { return true; }
|
||||||
bool sizeMattersLhs() const override {
|
bool sizeMattersLhs() const override {
|
||||||
@@ -5050,6 +5089,7 @@ public:
|
|||||||
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opLogNot(lhs); }
|
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opLogNot(lhs); }
|
||||||
string emitVerilog() override { return "%f(! %l)"; }
|
string emitVerilog() override { return "%f(! %l)"; }
|
||||||
string emitC() override { return "VL_LOGNOT_%nq%lq(%nw,%lw, %P, %li)"; }
|
string emitC() override { return "VL_LOGNOT_%nq%lq(%nw,%lw, %P, %li)"; }
|
||||||
|
string emitSMT() const override { return "(not %l)"; }
|
||||||
string emitSimpleOperator() override { return "!"; }
|
string emitSimpleOperator() override { return "!"; }
|
||||||
bool cleanOut() const override { return true; }
|
bool cleanOut() const override { return true; }
|
||||||
bool cleanLhs() 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); }
|
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opNegate(lhs); }
|
||||||
string emitVerilog() override { return "%f(- %l)"; }
|
string emitVerilog() override { return "%f(- %l)"; }
|
||||||
string emitC() override { return "VL_NEGATE_%lq(%lW, %P, %li)"; }
|
string emitC() override { return "VL_NEGATE_%lq(%lW, %P, %li)"; }
|
||||||
|
string emitSMT() const override { return "(bvneg %l)"; }
|
||||||
string emitSimpleOperator() override { return "-"; }
|
string emitSimpleOperator() override { return "-"; }
|
||||||
bool cleanOut() const override { return false; }
|
bool cleanOut() const override { return false; }
|
||||||
bool cleanLhs() 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); }
|
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opNot(lhs); }
|
||||||
string emitVerilog() override { return "%f(~ %l)"; }
|
string emitVerilog() override { return "%f(~ %l)"; }
|
||||||
string emitC() override { return "VL_NOT_%lq(%lW, %P, %li)"; }
|
string emitC() override { return "VL_NOT_%lq(%lW, %P, %li)"; }
|
||||||
|
string emitSMT() const override { return "(bvnot %l)"; }
|
||||||
string emitSimpleOperator() override { return "~"; }
|
string emitSimpleOperator() override { return "~"; }
|
||||||
bool cleanOut() const override { return false; }
|
bool cleanOut() const override { return false; }
|
||||||
bool cleanLhs() const override { return false; }
|
bool cleanLhs() const override { return false; }
|
||||||
|
|||||||
+37
-6
@@ -956,6 +956,7 @@ class AstClockingItem final : public AstNode {
|
|||||||
// @astgen op2 := exprp : Optional[AstNodeExpr]
|
// @astgen op2 := exprp : Optional[AstNodeExpr]
|
||||||
// @astgen op3 := assignp : Optional[AstAssign]
|
// @astgen op3 := assignp : Optional[AstAssign]
|
||||||
// @astgen op4 := varp : Optional[AstVar]
|
// @astgen op4 := varp : Optional[AstVar]
|
||||||
|
// @astgen ptr := m_outputp : Optional[AstClockingItem]
|
||||||
VDirection m_direction;
|
VDirection m_direction;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -971,6 +972,9 @@ public:
|
|||||||
}
|
}
|
||||||
ASTGEN_MEMBERS_AstClockingItem;
|
ASTGEN_MEMBERS_AstClockingItem;
|
||||||
VDirection direction() const { return m_direction; }
|
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 {
|
class AstConstPool final : public AstNode {
|
||||||
// Container for const static data
|
// Container for const static data
|
||||||
@@ -1809,6 +1813,7 @@ class AstVar final : public AstNode {
|
|||||||
bool m_trace : 1; // Trace this variable
|
bool m_trace : 1; // Trace this variable
|
||||||
bool m_isLatched : 1; // Not assigned in all control paths of combo always
|
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_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_isWrittenByDpi : 1; // This variable can be written by a DPI Export
|
||||||
bool m_isWrittenBySuspendable : 1; // This variable can be written by a suspendable process
|
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_trace = false;
|
||||||
m_isLatched = false;
|
m_isLatched = false;
|
||||||
m_isForceable = false;
|
m_isForceable = false;
|
||||||
|
m_isForcedByCode = false;
|
||||||
m_isWrittenByDpi = false;
|
m_isWrittenByDpi = false;
|
||||||
m_isWrittenBySuspendable = false;
|
m_isWrittenBySuspendable = false;
|
||||||
m_attrClocker = VVarAttrClocker::CLOCKER_UNKNOWN;
|
m_attrClocker = VVarAttrClocker::CLOCKER_UNKNOWN;
|
||||||
@@ -2009,6 +2015,8 @@ public:
|
|||||||
void isLatched(bool flag) { m_isLatched = flag; }
|
void isLatched(bool flag) { m_isLatched = flag; }
|
||||||
bool isForceable() const { return m_isForceable; }
|
bool isForceable() const { return m_isForceable; }
|
||||||
void setForceable() { m_isForceable = true; }
|
void setForceable() { m_isForceable = true; }
|
||||||
|
void setForcedByCode() { m_isForcedByCode = true; }
|
||||||
|
bool isForced() const { return m_isForceable || m_isForcedByCode; }
|
||||||
bool isWrittenByDpi() const { return m_isWrittenByDpi; }
|
bool isWrittenByDpi() const { return m_isWrittenByDpi; }
|
||||||
void setWrittenByDpi() { m_isWrittenByDpi = true; }
|
void setWrittenByDpi() { m_isWrittenByDpi = true; }
|
||||||
bool isWrittenBySuspendable() const { return m_isWrittenBySuspendable; }
|
bool isWrittenBySuspendable() const { return m_isWrittenBySuspendable; }
|
||||||
@@ -2434,13 +2442,11 @@ public:
|
|||||||
ASTGEN_MEMBERS_AstAlwaysObserved;
|
ASTGEN_MEMBERS_AstAlwaysObserved;
|
||||||
};
|
};
|
||||||
class AstAlwaysPost final : public AstNodeProcedure {
|
class AstAlwaysPost final : public AstNodeProcedure {
|
||||||
// Like always but post assignments for memory assignment IFs
|
// Like always but 'post' scheduled, e.g. for array NBA commits
|
||||||
// @astgen op1 := sensesp : Optional[AstSenTree] // Sensitivity list iff clocked
|
|
||||||
public:
|
public:
|
||||||
AstAlwaysPost(FileLine* fl, AstSenTree* sensesp, AstNode* stmtsp)
|
explicit AstAlwaysPost(FileLine* fl)
|
||||||
: ASTGEN_SUPER_AlwaysPost(fl, stmtsp) {
|
: ASTGEN_SUPER_AlwaysPost(fl, nullptr) {}
|
||||||
this->sensesp(sensesp);
|
|
||||||
}
|
|
||||||
ASTGEN_MEMBERS_AstAlwaysPost;
|
ASTGEN_MEMBERS_AstAlwaysPost;
|
||||||
};
|
};
|
||||||
class AstAlwaysPostponed final : public AstNodeProcedure {
|
class AstAlwaysPostponed final : public AstNodeProcedure {
|
||||||
@@ -2580,6 +2586,31 @@ public:
|
|||||||
bool isJustOneBodyStmt() const { return stmtsp() && !stmtsp()->nextp(); }
|
bool isJustOneBodyStmt() const { return stmtsp() && !stmtsp()->nextp(); }
|
||||||
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
|
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 {
|
class AstBreak final : public AstNodeStmt {
|
||||||
public:
|
public:
|
||||||
explicit AstBreak(FileLine* fl)
|
explicit AstBreak(FileLine* fl)
|
||||||
|
|||||||
+93
-15
@@ -788,6 +788,22 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe
|
|||||||
info.m_type = "VlUnpacked<" + sub.m_type;
|
info.m_type = "VlUnpacked<" + sub.m_type;
|
||||||
info.m_type += ", " + cvtToStr(adtypep->declRange().elements());
|
info.m_type += ", " + cvtToStr(adtypep->declRange().elements());
|
||||||
info.m_type += ">";
|
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))) {
|
} else if (packed && (VN_IS(dtypep, PackArrayDType))) {
|
||||||
const AstPackArrayDType* const adtypep = VN_CAST(dtypep, PackArrayDType);
|
const AstPackArrayDType* const adtypep = VN_CAST(dtypep, PackArrayDType);
|
||||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(false, true);
|
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(false, true);
|
||||||
@@ -827,6 +843,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe
|
|||||||
info.m_type = "VlForkSync";
|
info.m_type = "VlForkSync";
|
||||||
} else if (bdtypep->isProcessRef()) {
|
} else if (bdtypep->isProcessRef()) {
|
||||||
info.m_type = "VlProcessRef";
|
info.m_type = "VlProcessRef";
|
||||||
|
} else if (bdtypep->isRandomGenerator()) {
|
||||||
|
info.m_type = "VlRandomizer";
|
||||||
} else if (bdtypep->isEvent()) {
|
} else if (bdtypep->isEvent()) {
|
||||||
info.m_type = v3Global.assignsEvents() ? "VlAssignableEvent" : "VlEvent";
|
info.m_type = v3Global.assignsEvents() ? "VlAssignableEvent" : "VlEvent";
|
||||||
} else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width
|
} 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();
|
dtypep = adtypep->subDTypep();
|
||||||
continue;
|
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++;
|
unpacked++;
|
||||||
dtypep = qdtypep->subDTypep();
|
dtypep = dtypep->subDTypep();
|
||||||
continue;
|
continue;
|
||||||
} else if (const AstBasicDType* const adtypep = VN_CAST(dtypep, BasicDType)) {
|
} else if (const AstBasicDType* const adtypep = VN_CAST(dtypep, BasicDType)) {
|
||||||
if (includeBasic && (adtypep->isRanged() || adtypep->isString())) packed++;
|
if (includeBasic && (adtypep->isRanged() || adtypep->isString())) packed++;
|
||||||
@@ -1442,7 +1461,32 @@ void AstAlways::dumpJson(std::ostream& str) const {
|
|||||||
dumpJsonStr(str, "keyword", keyword().ascii());
|
dumpJsonStr(str, "keyword", keyword().ascii());
|
||||||
dumpJsonGen(str);
|
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 {
|
void AstAttrOf::dump(std::ostream& str) const {
|
||||||
this->AstNode::dump(str);
|
this->AstNode::dump(str);
|
||||||
str << " [" << attrType().ascii() << "]";
|
str << " [" << attrType().ascii() << "]";
|
||||||
@@ -1463,7 +1507,7 @@ void AstBasicDType::dumpJson(std::ostream& str) const {
|
|||||||
}
|
}
|
||||||
dumpJsonGen(str);
|
dumpJsonGen(str);
|
||||||
}
|
}
|
||||||
string AstBasicDType::prettyDTypeName() const {
|
string AstBasicDType::prettyDTypeName(bool) const {
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
os << keyword().ascii();
|
os << keyword().ascii();
|
||||||
if (isRanged() && !rangep() && keyword().width() <= 1) {
|
if (isRanged() && !rangep() && keyword().width() <= 1) {
|
||||||
@@ -1588,6 +1632,7 @@ void AstClassRefDType::dumpSmall(std::ostream& str) const {
|
|||||||
this->AstNodeDType::dumpSmall(str);
|
this->AstNodeDType::dumpSmall(str);
|
||||||
str << "class:" << name();
|
str << "class:" << name();
|
||||||
}
|
}
|
||||||
|
string AstClassRefDType::prettyDTypeName(bool) const { return "class{}"s + prettyName(); }
|
||||||
string AstClassRefDType::name() const { return classp() ? classp()->name() : "<unlinked>"; }
|
string AstClassRefDType::name() const { return classp() ? classp()->name() : "<unlinked>"; }
|
||||||
void AstNodeCoverOrAssert::dump(std::ostream& str) const {
|
void AstNodeCoverOrAssert::dump(std::ostream& str) const {
|
||||||
this->AstNodeStmt::dump(str);
|
this->AstNodeStmt::dump(str);
|
||||||
@@ -1624,6 +1669,22 @@ void AstEnumDType::dumpSmall(std::ostream& str) const {
|
|||||||
this->AstNodeDType::dumpSmall(str);
|
this->AstNodeDType::dumpSmall(str);
|
||||||
str << "enum";
|
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 {
|
void AstEnumItemRef::dump(std::ostream& str) const {
|
||||||
this->AstNodeExpr::dump(str);
|
this->AstNodeExpr::dump(str);
|
||||||
str << " -> ";
|
str << " -> ";
|
||||||
@@ -1938,6 +1999,18 @@ void AstNodeUOrStructDType::dumpJson(std::ostream& str) const {
|
|||||||
dumpJsonBoolFunc(str, isFourstate);
|
dumpJsonBoolFunc(str, isFourstate);
|
||||||
dumpJsonGen(str);
|
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 {
|
void AstNodeDType::dump(std::ostream& str) const {
|
||||||
this->AstNode::dump(str);
|
this->AstNode::dump(str);
|
||||||
if (generic()) str << " [GENERIC]";
|
if (generic()) str << " [GENERIC]";
|
||||||
@@ -1977,13 +2050,13 @@ void AstNodeArrayDType::dumpJson(std::ostream& str) const {
|
|||||||
dumpJsonStr(str, "declRange", cvtToStr(declRange()));
|
dumpJsonStr(str, "declRange", cvtToStr(declRange()));
|
||||||
dumpJsonGen(str);
|
dumpJsonGen(str);
|
||||||
}
|
}
|
||||||
string AstPackArrayDType::prettyDTypeName() const {
|
string AstPackArrayDType::prettyDTypeName(bool full) const {
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
if (const auto subp = subDTypep()) os << subp->prettyDTypeName();
|
if (const auto subp = subDTypep()) os << subp->prettyDTypeName(full);
|
||||||
os << declRange();
|
os << declRange();
|
||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
string AstUnpackArrayDType::prettyDTypeName() const {
|
string AstUnpackArrayDType::prettyDTypeName(bool full) const {
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
string ranges = cvtToStr(declRange());
|
string ranges = cvtToStr(declRange());
|
||||||
// Unfortunately we need a single $ for the first unpacked, and all
|
// Unfortunately we need a single $ for the first unpacked, and all
|
||||||
@@ -1993,7 +2066,7 @@ string AstUnpackArrayDType::prettyDTypeName() const {
|
|||||||
ranges += cvtToStr(adtypep->declRange());
|
ranges += cvtToStr(adtypep->declRange());
|
||||||
subp = adtypep->subDTypep()->skipRefp();
|
subp = adtypep->subDTypep()->skipRefp();
|
||||||
}
|
}
|
||||||
os << subp->prettyDTypeName() << "$" << ranges;
|
os << subp->prettyDTypeName(full) << "$" << ranges;
|
||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
std::vector<AstUnpackArrayDType*> AstUnpackArrayDType::unpackDimensions() {
|
std::vector<AstUnpackArrayDType*> AstUnpackArrayDType::unpackDimensions() {
|
||||||
@@ -2137,20 +2210,22 @@ void AstAssocArrayDType::dumpSmall(std::ostream& str) const {
|
|||||||
this->AstNodeDType::dumpSmall(str);
|
this->AstNodeDType::dumpSmall(str);
|
||||||
str << "[assoc-" << nodeAddr(keyDTypep()) << "]";
|
str << "[assoc-" << nodeAddr(keyDTypep()) << "]";
|
||||||
}
|
}
|
||||||
string AstAssocArrayDType::prettyDTypeName() const {
|
string AstAssocArrayDType::prettyDTypeName(bool full) const {
|
||||||
return subDTypep()->prettyDTypeName() + "[" + keyDTypep()->prettyDTypeName() + "]";
|
return subDTypep()->prettyDTypeName(full) + "$[" + keyDTypep()->prettyDTypeName(full) + "]";
|
||||||
}
|
}
|
||||||
void AstDynArrayDType::dumpSmall(std::ostream& str) const {
|
void AstDynArrayDType::dumpSmall(std::ostream& str) const {
|
||||||
this->AstNodeDType::dumpSmall(str);
|
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 {
|
void AstQueueDType::dumpSmall(std::ostream& str) const {
|
||||||
this->AstNodeDType::dumpSmall(str);
|
this->AstNodeDType::dumpSmall(str);
|
||||||
str << "[queue]";
|
str << "[queue]";
|
||||||
}
|
}
|
||||||
string AstQueueDType::prettyDTypeName() const {
|
string AstQueueDType::prettyDTypeName(bool full) const {
|
||||||
string str = subDTypep()->prettyDTypeName() + "[$";
|
string str = subDTypep()->prettyDTypeName(full) + "$[$";
|
||||||
if (boundConst()) str += ":" + cvtToStr(boundConst());
|
if (boundConst()) str += ":" + cvtToStr(boundConst());
|
||||||
return str + "]";
|
return str + "]";
|
||||||
}
|
}
|
||||||
@@ -2683,6 +2758,7 @@ void AstCMethodHard::setPurity() {
|
|||||||
{"commit", false},
|
{"commit", false},
|
||||||
{"delay", false},
|
{"delay", false},
|
||||||
{"done", false},
|
{"done", false},
|
||||||
|
{"enqueue", false},
|
||||||
{"erase", false},
|
{"erase", false},
|
||||||
{"evaluate", false},
|
{"evaluate", false},
|
||||||
{"evaluation", false},
|
{"evaluation", false},
|
||||||
@@ -2695,6 +2771,7 @@ void AstCMethodHard::setPurity() {
|
|||||||
{"find_last_index", true},
|
{"find_last_index", true},
|
||||||
{"fire", false},
|
{"fire", false},
|
||||||
{"first", false},
|
{"first", false},
|
||||||
|
{"hard", false},
|
||||||
{"init", false},
|
{"init", false},
|
||||||
{"insert", false},
|
{"insert", false},
|
||||||
{"inside", true},
|
{"inside", true},
|
||||||
@@ -2734,7 +2811,8 @@ void AstCMethodHard::setPurity() {
|
|||||||
{"trigger", false},
|
{"trigger", false},
|
||||||
{"unique", true},
|
{"unique", true},
|
||||||
{"unique_index", true},
|
{"unique_index", true},
|
||||||
{"word", true}};
|
{"word", true},
|
||||||
|
{"write_var", false}};
|
||||||
|
|
||||||
auto isPureIt = isPureMethod.find(name());
|
auto isPureIt = isPureMethod.find(name());
|
||||||
UASSERT_OBJ(isPureIt != isPureMethod.end(), this, "Unknown purity of method " + name());
|
UASSERT_OBJ(isPureIt != isPureMethod.end(), this, "Unknown purity of method " + name());
|
||||||
|
|||||||
+3
-1
@@ -113,7 +113,9 @@ static void makeToStringMiddle(AstClass* nodep) {
|
|||||||
std::string comma;
|
std::string comma;
|
||||||
for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) {
|
for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) {
|
||||||
if (const auto* const varp = VN_CAST(itemp, Var)) {
|
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 += \"";
|
string stmt = "out += \"";
|
||||||
stmt += comma;
|
stmt += comma;
|
||||||
comma = ", ";
|
comma = ", ";
|
||||||
|
|||||||
+9
-5
@@ -1556,7 +1556,9 @@ class ConstVisitor final : public VNVisitor {
|
|||||||
}
|
}
|
||||||
return false;
|
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.
|
// Constant Replacement functions.
|
||||||
@@ -3071,8 +3073,9 @@ class ConstVisitor final : public VNVisitor {
|
|||||||
&& varrefp // Don't do messes with BITREFs/ARRAYREFs
|
&& varrefp // Don't do messes with BITREFs/ARRAYREFs
|
||||||
&& !varrefp->varp()->hasStrengthAssignment() // Strengths are resolved in V3Tristate
|
&& !varrefp->varp()->hasStrengthAssignment() // Strengths are resolved in V3Tristate
|
||||||
&& !varrefp->varp()->valuep() // Not already constified
|
&& !varrefp->varp()->valuep() // Not already constified
|
||||||
&& !varrefp->varScopep()) { // Not scoped (or each scope may have different initial
|
&& !varrefp->varScopep() // Not scoped (or each scope may have different initial val.)
|
||||||
// value)
|
&& !varrefp->varp()->isForced() // Not forced (not really a constant)
|
||||||
|
) {
|
||||||
// ASSIGNW (VARREF, const) -> INITIAL ( ASSIGN (VARREF, const) )
|
// ASSIGNW (VARREF, const) -> INITIAL ( ASSIGN (VARREF, const) )
|
||||||
UINFO(4, "constAssignW " << nodep << endl);
|
UINFO(4, "constAssignW " << nodep << endl);
|
||||||
// Make a initial assignment
|
// Make a initial assignment
|
||||||
@@ -3542,7 +3545,7 @@ class ConstVisitor final : public VNVisitor {
|
|||||||
TREEOP ("AstMulS {$lhsp.isOne, $rhsp}", "replaceWRhs(nodep)");
|
TREEOP ("AstMulS {$lhsp.isOne, $rhsp}", "replaceWRhs(nodep)");
|
||||||
TREEOP ("AstDiv {$lhsp, $rhsp.isOne}", "replaceWLhs(nodep)");
|
TREEOP ("AstDiv {$lhsp, $rhsp.isOne}", "replaceWLhs(nodep)");
|
||||||
TREEOP ("AstDivS {$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 ("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 ("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
|
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");
|
TREEOPC("AstXor {matchBitOpTree(nodep)}", "DONE");
|
||||||
// Note can't simplify a extend{extends}, extends{extend}, as the sign
|
// Note can't simplify a extend{extends}, extends{extend}, as the sign
|
||||||
// bits end up in the wrong places
|
// 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("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("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
|
TREEOPV("AstReplicateN{$lhsp, $rhsp.isOne, $lhsp->width()==nodep->width()}", "replaceWLhs(nodep)"); // {1{lhs}}->lhs
|
||||||
|
|||||||
+679
-433
File diff suppressed because it is too large
Load Diff
+1
-2
@@ -223,8 +223,7 @@ class DescopeVisitor final : public VNVisitor {
|
|||||||
}
|
}
|
||||||
void visit(AstVarScope* nodep) override {
|
void visit(AstVarScope* nodep) override {
|
||||||
// Delete the varscope when we're finished
|
// Delete the varscope when we're finished
|
||||||
nodep->unlinkFrBack();
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||||
pushDeletep(nodep);
|
|
||||||
}
|
}
|
||||||
void visit(AstNodeVarRef* nodep) override {
|
void visit(AstNodeVarRef* nodep) override {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
|
|||||||
@@ -443,7 +443,7 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||||||
// Mark variables with external references
|
// Mark variables with external references
|
||||||
if (nodep->isIO() // Ports
|
if (nodep->isIO() // Ports
|
||||||
|| nodep->user2() // Target of a hierarchical reference
|
|| nodep->user2() // Target of a hierarchical reference
|
||||||
|| nodep->isForceable() // Forceable
|
|| nodep->isForced() // Forced
|
||||||
) {
|
) {
|
||||||
getNet(nodep)->setHasExtRefs();
|
getNet(nodep)->setHasExtRefs();
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-1
@@ -64,6 +64,7 @@ public:
|
|||||||
|
|
||||||
struct Hash final {
|
struct Hash final {
|
||||||
size_t operator()(const KeySel& key) const {
|
size_t operator()(const KeySel& key) const {
|
||||||
|
// cppcheck-suppress unreadVariable // cppcheck bug
|
||||||
V3Hash hash{vertexHash(key.m_fromp)};
|
V3Hash hash{vertexHash(key.m_fromp)};
|
||||||
hash += key.m_lsb;
|
hash += key.m_lsb;
|
||||||
hash += key.m_width;
|
hash += key.m_width;
|
||||||
@@ -83,6 +84,7 @@ class KeyUnary final {
|
|||||||
const DfgVertex* const m_source0p;
|
const DfgVertex* const m_source0p;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// cppcheck-suppress noExplicitConstructor
|
||||||
KeyUnary(DfgVertex* source0p)
|
KeyUnary(DfgVertex* source0p)
|
||||||
: m_source0p{source0p} {}
|
: m_source0p{source0p} {}
|
||||||
|
|
||||||
@@ -110,6 +112,7 @@ public:
|
|||||||
|
|
||||||
struct Hash final {
|
struct Hash final {
|
||||||
size_t operator()(const KeyBinary& key) const {
|
size_t operator()(const KeyBinary& key) const {
|
||||||
|
// cppcheck-suppress unreadVariable // cppcheck bug
|
||||||
V3Hash hash{vertexHash(key.m_source0p)};
|
V3Hash hash{vertexHash(key.m_source0p)};
|
||||||
hash += vertexHash(key.m_source1p);
|
hash += vertexHash(key.m_source1p);
|
||||||
return hash.value();
|
return hash.value();
|
||||||
@@ -137,6 +140,7 @@ public:
|
|||||||
|
|
||||||
struct Hash final {
|
struct Hash final {
|
||||||
size_t operator()(const KeyTernary& key) const {
|
size_t operator()(const KeyTernary& key) const {
|
||||||
|
// cppcheck-suppress unreadVariable // cppcheck bug
|
||||||
V3Hash hash{vertexHash(key.m_source0p)};
|
V3Hash hash{vertexHash(key.m_source0p)};
|
||||||
hash += vertexHash(key.m_source1p);
|
hash += vertexHash(key.m_source1p);
|
||||||
hash += vertexHash(key.m_source2p);
|
hash += vertexHash(key.m_source2p);
|
||||||
@@ -348,7 +352,7 @@ class V3DfgCache final {
|
|||||||
inline CacheType<Vertex>& cacheForType();
|
inline CacheType<Vertex>& cacheForType();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
V3DfgCache(DfgGraph& dfg)
|
explicit V3DfgCache(DfgGraph& dfg)
|
||||||
: m_dfg{dfg} {}
|
: m_dfg{dfg} {}
|
||||||
|
|
||||||
// Find a vertex of type 'Vertex', with the given operands, or create a new one and add it.
|
// Find a vertex of type 'Vertex', with the given operands, or create a new one and add it.
|
||||||
|
|||||||
@@ -295,7 +295,8 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
|
|||||||
// For each cyclic component
|
// For each cyclic component
|
||||||
for (auto& component : cyclicComponents) {
|
for (auto& component : cyclicComponents) {
|
||||||
if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
|
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)
|
// Add back under the main DFG (we will convert everything back in one go)
|
||||||
dfg->addGraph(*component);
|
dfg->addGraph(*component);
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-9
@@ -165,17 +165,11 @@ void V3DfgPasses::inlineVars(DfgGraph& dfg) {
|
|||||||
const AstVar* const astVarp = driverVarp->varp();
|
const AstVar* const astVarp = driverVarp->varp();
|
||||||
// If driven from a SystemC variable
|
// If driven from a SystemC variable
|
||||||
if (astVarp->isSc()) continue;
|
if (astVarp->isSc()) continue;
|
||||||
// If the variable is forceable
|
// If the variable is forced
|
||||||
if (astVarp->isForceable()) continue;
|
if (astVarp->isForced()) continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
varp->forEachSinkEdge([=](DfgEdge& edge) {
|
varp->forEachSinkEdge([=](DfgEdge& edge) { edge.relinkSource(driverp); });
|
||||||
// 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);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-6
@@ -27,6 +27,7 @@
|
|||||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||||
|
|
||||||
std::string V3DfgRegularizeContext::tmpNamePrefix(DfgGraph& dfg) {
|
std::string V3DfgRegularizeContext::tmpNamePrefix(DfgGraph& dfg) {
|
||||||
|
// cppcheck-suppress unreadVariable // cppcheck bug
|
||||||
V3Hash hash{dfg.modulep()->name()};
|
V3Hash hash{dfg.modulep()->name()};
|
||||||
hash += m_label;
|
hash += m_label;
|
||||||
std::string name = hash.toString();
|
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
|
// Return canonical variable that can be used to hold the value of this vertex
|
||||||
DfgVarPacked* getCanonicalVariable(DfgVertex& vtx) {
|
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;
|
std::vector<DfgVarPacked*> varVtxps;
|
||||||
vtx.forEachSink([&](DfgVertex& sink) {
|
vtx.forEachSink([&](DfgVertex& sink) {
|
||||||
if (DfgVarPacked* const varVtxp = sink.cast<DfgVarPacked>()) {
|
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
|
// Ensure intermediate values used multiple times are written to variables
|
||||||
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
||||||
// Operations without multiple sinks need no variables
|
const bool needsIntermediateVariable = [&]() {
|
||||||
if (!vtx.hasMultipleSinks()) continue;
|
// Anything that drives an SC variable needs an intermediate,
|
||||||
// Array selects need no variables, they are just memory references
|
// as we can only assign simple variables to SC variables at runtime.
|
||||||
if (vtx.is<DfgArraySel>()) continue;
|
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.
|
// This is an op which has multiple sinks. Ensure it is assigned to a variable.
|
||||||
DfgVarPacked* const varp = getCanonicalVariable(vtx);
|
DfgVarPacked* const varp = getCanonicalVariable(vtx);
|
||||||
|
|||||||
+1
-1
@@ -239,7 +239,7 @@ public:
|
|||||||
ASTGEN_MEMBERS_DfgVarPacked;
|
ASTGEN_MEMBERS_DfgVarPacked;
|
||||||
|
|
||||||
bool isDrivenFullyByDfg() const {
|
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) {
|
void addDriver(FileLine* flp, uint32_t lsb, DfgVertex* vtxp) {
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ class EmitCConstPool final : public EmitCConstInit {
|
|||||||
putns(varp, "extern const ");
|
putns(varp, "extern const ");
|
||||||
putns(varp, varp->dtypep()->cType(nameProtect, false, false));
|
putns(varp, varp->dtypep()->cType(nameProtect, false, false));
|
||||||
putns(varp, " = ");
|
putns(varp, " = ");
|
||||||
|
UASSERT_OBJ(varp, varp->valuep(), "Var without value");
|
||||||
iterateConst(varp->valuep());
|
iterateConst(varp->valuep());
|
||||||
putns(varp, ";\n");
|
putns(varp, ";\n");
|
||||||
// Keep track of stats
|
// Keep track of stats
|
||||||
|
|||||||
@@ -722,6 +722,8 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP
|
|||||||
return "";
|
return "";
|
||||||
} else if (basicp && basicp->isDynamicTriggerScheduler()) {
|
} else if (basicp && basicp->isDynamicTriggerScheduler()) {
|
||||||
return "";
|
return "";
|
||||||
|
} else if (basicp && basicp->isRandomGenerator()) {
|
||||||
|
return "";
|
||||||
} else if (basicp) {
|
} else if (basicp) {
|
||||||
const bool zeroit
|
const bool zeroit
|
||||||
= (varp->attrFileDescr() // Zero so we don't do file IO if never $fopen
|
= (varp->attrFileDescr() // Zero so we don't do file IO if never $fopen
|
||||||
|
|||||||
@@ -515,6 +515,7 @@ class EmitCHeader final : public EmitCConstInit {
|
|||||||
if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n");
|
if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n");
|
||||||
if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n");
|
if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n");
|
||||||
if (v3Global.usesTiming()) puts("#include \"verilated_timing.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;
|
std::set<string> cuse_set;
|
||||||
auto add_to_cuse_set = [&](string s) { cuse_set.insert(s); };
|
auto add_to_cuse_set = [&](string s) { cuse_set.insert(s); };
|
||||||
|
|||||||
@@ -389,6 +389,7 @@ class EmitCImp final : EmitCFunc {
|
|||||||
} else if (varp->isParam()) {
|
} else if (varp->isParam()) {
|
||||||
} else if (varp->isStatic() && varp->isConst()) {
|
} else if (varp->isStatic() && varp->isConst()) {
|
||||||
} else if (varp->basicp() && varp->basicp()->isTriggerVec()) {
|
} else if (varp->basicp() && varp->basicp()->isTriggerVec()) {
|
||||||
|
} else if (VN_IS(varp->dtypep(), NBACommitQueueDType)) {
|
||||||
} else {
|
} else {
|
||||||
int vects = 0;
|
int vects = 0;
|
||||||
AstNodeDType* elementp = varp->dtypeSkipRefp();
|
AstNodeDType* elementp = varp->dtypeSkipRefp();
|
||||||
|
|||||||
@@ -85,6 +85,9 @@ class CMakeEmitter final {
|
|||||||
cmake_set(*of, "VERILATOR_ROOT",
|
cmake_set(*of, "VERILATOR_ROOT",
|
||||||
V3OutFormatter::quoteNameControls(V3Options::getenvVERILATOR_ROOT()), "PATH",
|
V3OutFormatter::quoteNameControls(V3Options::getenvVERILATOR_ROOT()), "PATH",
|
||||||
"Path to Verilator kit (from $VERILATOR_ROOT)");
|
"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";
|
*of << "\n### Compiler flags...\n";
|
||||||
|
|
||||||
@@ -160,6 +163,9 @@ class CMakeEmitter final {
|
|||||||
if (v3Global.usesTiming()) {
|
if (v3Global.usesTiming()) {
|
||||||
global.emplace_back("${VERILATOR_ROOT}/include/verilated_timing.cpp");
|
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");
|
global.emplace_back("${VERILATOR_ROOT}/include/verilated_threads.cpp");
|
||||||
if (v3Global.opt.usesProfiler()) {
|
if (v3Global.opt.usesProfiler()) {
|
||||||
global.emplace_back("${VERILATOR_ROOT}/include/verilated_profiler.cpp");
|
global.emplace_back("${VERILATOR_ROOT}/include/verilated_profiler.cpp");
|
||||||
|
|||||||
@@ -101,6 +101,12 @@ class EmitCModel final : public EmitCFunc {
|
|||||||
|
|
||||||
puts("\n");
|
puts("\n");
|
||||||
ofp()->putsPrivate(false); // public:
|
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
|
// User accessible IO
|
||||||
puts("\n// PORTS\n"
|
puts("\n// PORTS\n"
|
||||||
"// The application code writes and reads these signals to\n"
|
"// The application code writes and reads these signals to\n"
|
||||||
|
|||||||
@@ -106,6 +106,8 @@ public:
|
|||||||
}
|
}
|
||||||
if (v3Global.usesProbDist()) putMakeClassEntry(of, "verilated_probdist.cpp");
|
if (v3Global.usesProbDist()) putMakeClassEntry(of, "verilated_probdist.cpp");
|
||||||
if (v3Global.usesTiming()) putMakeClassEntry(of, "verilated_timing.cpp");
|
if (v3Global.usesTiming()) putMakeClassEntry(of, "verilated_timing.cpp");
|
||||||
|
if (v3Global.useRandomizeMethods())
|
||||||
|
putMakeClassEntry(of, "verilated_random.cpp");
|
||||||
putMakeClassEntry(of, "verilated_threads.cpp");
|
putMakeClassEntry(of, "verilated_threads.cpp");
|
||||||
if (v3Global.opt.usesProfiler()) {
|
if (v3Global.opt.usesProfiler()) {
|
||||||
putMakeClassEntry(of, "verilated_profiler.cpp");
|
putMakeClassEntry(of, "verilated_profiler.cpp");
|
||||||
@@ -187,6 +189,10 @@ public:
|
|||||||
|
|
||||||
of.puts("# User CFLAGS (from -CFLAGS on Verilator command line)\n");
|
of.puts("# User CFLAGS (from -CFLAGS on Verilator command line)\n");
|
||||||
of.puts("VM_USER_CFLAGS = \\\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");
|
if (!v3Global.opt.libCreate().empty()) of.puts("\t-fPIC \\\n");
|
||||||
const V3StringList& cFlags = v3Global.opt.cFlags();
|
const V3StringList& cFlags = v3Global.opt.cFlags();
|
||||||
for (const string& i : cFlags) of.puts("\t" + i + " \\\n");
|
for (const string& i : cFlags) of.puts("\t" + i + " \\\n");
|
||||||
|
|||||||
+1
-1
@@ -608,8 +608,8 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
|
|||||||
puts(";\n");
|
puts(";\n");
|
||||||
}
|
}
|
||||||
void visit(AstBasicDType* nodep) override {
|
void visit(AstBasicDType* nodep) override {
|
||||||
if (nodep->isSigned()) putfs(nodep, "signed ");
|
|
||||||
putfs(nodep, nodep->prettyName());
|
putfs(nodep, nodep->prettyName());
|
||||||
|
if (nodep->isSigned()) putfs(nodep, " signed");
|
||||||
if (nodep->rangep()) {
|
if (nodep->rangep()) {
|
||||||
puts(" ");
|
puts(" ");
|
||||||
iterateAndNextConstNull(nodep->rangep());
|
iterateAndNextConstNull(nodep->rangep());
|
||||||
|
|||||||
+2
-1
@@ -121,6 +121,7 @@ public:
|
|||||||
MULTITOP, // Multiple top level modules
|
MULTITOP, // Multiple top level modules
|
||||||
NEWERSTD, // Newer language standard required
|
NEWERSTD, // Newer language standard required
|
||||||
NOLATCH, // No latch detected in always_latch block
|
NOLATCH, // No latch detected in always_latch block
|
||||||
|
NONSTD, // Non-standard feature present in other sims
|
||||||
NULLPORT, // Null port detected in module definition
|
NULLPORT, // Null port detected in module definition
|
||||||
PINCONNECTEMPTY,// Cell pin connected by name with empty reference
|
PINCONNECTEMPTY,// Cell pin connected by name with empty reference
|
||||||
PINMISSING, // Cell pin not specified
|
PINMISSING, // Cell pin not specified
|
||||||
@@ -204,7 +205,7 @@ public:
|
|||||||
"IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE",
|
"IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE",
|
||||||
"INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE",
|
"INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE",
|
||||||
"LATCH", "LITENDIAN", "MINTYPMAXDLY", "MISINDENT", "MODDUP",
|
"LATCH", "LITENDIAN", "MINTYPMAXDLY", "MISINDENT", "MODDUP",
|
||||||
"MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOLATCH", "NULLPORT", "PINCONNECTEMPTY",
|
"MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOLATCH", "NONSTD", "NULLPORT", "PINCONNECTEMPTY",
|
||||||
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PREPROCZERO", "PROCASSWIRE",
|
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PREPROCZERO", "PROCASSWIRE",
|
||||||
"PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY",
|
"PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY",
|
||||||
"SELRANGE", "SHORTREAL", "SIDEEFFECT", "SPLITVAR",
|
"SELRANGE", "SHORTREAL", "SIDEEFFECT", "SPLITVAR",
|
||||||
|
|||||||
+6
-17
@@ -64,15 +64,6 @@ class ForceConvertVisitor final : public VNVisitor {
|
|||||||
m_rdVarp->addNext(m_enVarp);
|
m_rdVarp->addNext(m_enVarp);
|
||||||
m_rdVarp->addNext(m_valVarp);
|
m_rdVarp->addNext(m_valVarp);
|
||||||
varp->addNextHere(m_rdVarp);
|
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 {
|
void visit(AstAssignForce* nodep) override {
|
||||||
// The AstAssignForce node will be removed for sure
|
// The AstAssignForce node will be removed for sure
|
||||||
VNRelinker relinker;
|
|
||||||
nodep->unlinkFrBack(&relinker);
|
|
||||||
pushDeletep(nodep);
|
|
||||||
|
|
||||||
FileLine* const flp = nodep->fileline();
|
FileLine* const flp = nodep->fileline();
|
||||||
AstNodeExpr* const lhsp = nodep->lhsp(); // The LValue we are forcing
|
AstNodeExpr* const lhsp = nodep->lhsp(); // The LValue we are forcing
|
||||||
AstNodeExpr* const rhsp = nodep->rhsp(); // The value we are forcing it to
|
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
|
// Set corresponding enable signals to ones
|
||||||
V3Number ones{lhsp, isRangedDType(lhsp) ? lhsp->width() : 1};
|
V3Number ones{lhsp, isRangedDType(lhsp) ? lhsp->width() : 1};
|
||||||
@@ -208,13 +198,12 @@ class ForceConvertVisitor final : public VNVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void visit(AstRelease* nodep) override {
|
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
|
// The AstRelease node will be removed for sure
|
||||||
VNRelinker relinker;
|
VNRelinker relinker;
|
||||||
nodep->unlinkFrBack(&relinker);
|
nodep->unlinkFrBack(&relinker);
|
||||||
pushDeletep(nodep);
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
|
|
||||||
FileLine* const flp = nodep->fileline();
|
|
||||||
AstNodeExpr* const lhsp = nodep->lhsp(); // The LValue we are releasing
|
|
||||||
|
|
||||||
// Set corresponding enable signals to zero
|
// Set corresponding enable signals to zero
|
||||||
V3Number zero{lhsp, isRangedDType(lhsp) ? lhsp->width() : 1};
|
V3Number zero{lhsp, isRangedDType(lhsp) ? lhsp->width() : 1};
|
||||||
|
|||||||
@@ -616,6 +616,7 @@ class ForkVisitor final : public VNVisitor {
|
|||||||
if (m_forkDepth && !nodep->varp()->isFuncLocal() && nodep->varp()->isClassMember()) return;
|
if (m_forkDepth && !nodep->varp()->isFuncLocal() && nodep->varp()->isClassMember()) return;
|
||||||
|
|
||||||
if (m_forkDepth && (m_forkLocalsp.count(nodep->varp()) == 0)
|
if (m_forkDepth && (m_forkLocalsp.count(nodep->varp()) == 0)
|
||||||
|
&& nodep->varp()->varType() != VVarType::PORT // Basically static, so it's safe
|
||||||
&& !nodep->varp()->lifetime().isStatic()) {
|
&& !nodep->varp()->lifetime().isStatic()) {
|
||||||
if (nodep->access().isWriteOrRW()
|
if (nodep->access().isWriteOrRW()
|
||||||
&& (!nodep->isClassHandleValue() || nodep->user2())) {
|
&& (!nodep->isClassHandleValue() || nodep->user2())) {
|
||||||
|
|||||||
+9
-2
@@ -425,8 +425,8 @@ class GateClkDecomp final {
|
|||||||
void visit(GateVarVertex* vVtxp, int offset) {
|
void visit(GateVarVertex* vVtxp, int offset) {
|
||||||
AstVarScope* const vscp = vVtxp->varScp();
|
AstVarScope* const vscp = vVtxp->varScp();
|
||||||
|
|
||||||
// Can't propagate if this variable is forceable
|
// Can't propagate if this variable might be forced
|
||||||
if (vscp->varp()->isForceable()) return;
|
if (vscp->varp()->isForced()) return;
|
||||||
|
|
||||||
// Check that we haven't been here before
|
// Check that we haven't been here before
|
||||||
if (vscp->user2SetOnce()) return;
|
if (vscp->user2SetOnce()) return;
|
||||||
@@ -656,6 +656,9 @@ public:
|
|||||||
return m_substitutionp;
|
return m_substitutionp;
|
||||||
}
|
}
|
||||||
const std::vector<AstVarScope*>& readVscps() const { return m_readVscps; }
|
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?
|
// Was it ok?
|
||||||
if (!okVisitor.isSimple()) continue;
|
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?
|
// Does it read multiple source variables?
|
||||||
if (okVisitor.readVscps().size() > 1) {
|
if (okVisitor.readVscps().size() > 1) {
|
||||||
@@ -822,6 +827,8 @@ class GateInline final {
|
|||||||
|
|
||||||
if (debug() >= 9) dstVtxp->nodep()->dumpTree(" inside: ");
|
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());
|
recordSubstitution(vscp, substp, dstVtxp->nodep());
|
||||||
|
|
||||||
// If the new replacement referred to a signal,
|
// If the new replacement referred to a signal,
|
||||||
|
|||||||
+2
-4
@@ -98,8 +98,6 @@ static void V3HierWriteCommonInputs(const V3HierBlock* hblockp, std::ostream* of
|
|||||||
if (hblockp) topModuleFile = hblockp->vFileIfNecessary();
|
if (hblockp) topModuleFile = hblockp->vFileIfNecessary();
|
||||||
if (!forCMake) {
|
if (!forCMake) {
|
||||||
if (!topModuleFile.empty()) *of << topModuleFile << "\n";
|
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();
|
const V3StringSet& libraryFiles = v3Global.opt.libraryFiles();
|
||||||
for (const string& i : libraryFiles) {
|
for (const string& i : libraryFiles) {
|
||||||
@@ -227,7 +225,7 @@ void V3HierBlock::writeCommandArgsFile(bool forCMake) const {
|
|||||||
for (const string& opt : commandOpts) *of << opt << "\n";
|
for (const string& opt : commandOpts) *of << opt << "\n";
|
||||||
*of << hierBlockArgs().front() << "\n";
|
*of << hierBlockArgs().front() << "\n";
|
||||||
for (const auto& hierblockp : m_children) *of << hierblockp->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 {
|
string V3HierBlock::commandArgsFileName(bool forCMake) const {
|
||||||
@@ -424,7 +422,7 @@ void V3HierBlockPlan::writeCommandArgsFiles(bool forCMake) const {
|
|||||||
}
|
}
|
||||||
*of << "--threads " << cvtToStr(v3Global.opt.threads()) << "\n";
|
*of << "--threads " << cvtToStr(v3Global.opt.threads()) << "\n";
|
||||||
*of << (v3Global.opt.systemC() ? "--sc" : "--cc") << "\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) {
|
string V3HierBlockPlan::topCommandArgsFileName(bool forCMake) {
|
||||||
|
|||||||
+13
-3
@@ -320,8 +320,16 @@ class InlineRelinkVisitor final : public VNVisitor {
|
|||||||
// module, so a AstVarRef not AstVarXRef below
|
// module, so a AstVarRef not AstVarXRef below
|
||||||
exprvarrefp = exprvarrefp->cloneTree(false);
|
exprvarrefp = exprvarrefp->cloneTree(false);
|
||||||
exprvarrefp->access(VAccess::READ);
|
exprvarrefp->access(VAccess::READ);
|
||||||
m_modp->addStmtsp(new AstAssignAlias{
|
AstVarRef* const nodeVarRefp = new AstVarRef{flp, nodep, VAccess::WRITE};
|
||||||
flp, new AstVarRef{flp, nodep, VAccess::WRITE}, exprvarrefp});
|
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();
|
FileLine* const flbp = exprvarrefp->varp()->fileline();
|
||||||
flp->modifyStateInherit(flbp);
|
flp->modifyStateInherit(flbp);
|
||||||
flbp->modifyStateInherit(flp);
|
flbp->modifyStateInherit(flp);
|
||||||
@@ -370,7 +378,9 @@ class InlineRelinkVisitor final : public VNVisitor {
|
|||||||
if (nodep->varp()->user2p() // It's being converted to an alias.
|
if (nodep->varp()->user2p() // It's being converted to an alias.
|
||||||
&& !nodep->varp()->user3()
|
&& !nodep->varp()->user3()
|
||||||
// Don't constant propagate aliases (we just made)
|
// 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();
|
AstVar* const varp = nodep->varp();
|
||||||
if (AstConst* const constp = VN_CAST(varp->user2p(), Const)) {
|
if (AstConst* const constp = VN_CAST(varp->user2p(), Const)) {
|
||||||
nodep->replaceWith(constp->cloneTree(false));
|
nodep->replaceWith(constp->cloneTree(false));
|
||||||
|
|||||||
+1
-1
@@ -463,7 +463,7 @@ private:
|
|||||||
pushDeletep(pinVarp);
|
pushDeletep(pinVarp);
|
||||||
} // else pinVarp already unlinked when another instance did this step
|
} // else pinVarp already unlinked when another instance did this step
|
||||||
nodep->replaceWith(prevPinp);
|
nodep->replaceWith(prevPinp);
|
||||||
pushDeletep(nodep);
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-4
@@ -53,10 +53,7 @@ public:
|
|||||||
~LifeState() {
|
~LifeState() {
|
||||||
V3Stats::addStatSum("Optimizations, Lifetime assign deletions", m_statAssnDel);
|
V3Stats::addStatSum("Optimizations, Lifetime assign deletions", m_statAssnDel);
|
||||||
V3Stats::addStatSum("Optimizations, Lifetime constant prop", m_statAssnCon);
|
V3Stats::addStatSum("Optimizations, Lifetime constant prop", m_statAssnCon);
|
||||||
for (AstNode* ip : m_unlinkps) {
|
for (AstNode* ip : m_unlinkps) VL_DO_DANGLING(ip->unlinkFrBack()->deleteTree(), ip);
|
||||||
ip->unlinkFrBack();
|
|
||||||
ip->deleteTree();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// METHODS
|
// METHODS
|
||||||
void pushUnlinkDeletep(AstNode* nodep) { m_unlinkps.push_back(nodep); }
|
void pushUnlinkDeletep(AstNode* nodep) { m_unlinkps.push_back(nodep); }
|
||||||
|
|||||||
+1
-1
@@ -273,7 +273,7 @@ class LinkCellsVisitor final : public VNVisitor {
|
|||||||
iterateAndNextNull(cellsp);
|
iterateAndNextNull(cellsp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pushDeletep(nodep->unlinkFrBack());
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit(AstCell* nodep) override {
|
void visit(AstCell* nodep) override {
|
||||||
|
|||||||
+78
-10
@@ -1211,6 +1211,23 @@ class LinkDotFindVisitor final : public VNVisitor {
|
|||||||
foundp = m_modSymp; // Conflicts with modname?
|
foundp = m_modSymp; // Conflicts with modname?
|
||||||
}
|
}
|
||||||
AstVar* const findvarp = foundp ? VN_CAST(foundp->nodep(), Var) : nullptr;
|
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;
|
bool ins = false;
|
||||||
if (!foundp) {
|
if (!foundp) {
|
||||||
ins = true;
|
ins = true;
|
||||||
@@ -1504,11 +1521,11 @@ class LinkDotFindVisitor final : public VNVisitor {
|
|||||||
VL_DO_DANGLING(argp->unlinkFrBackWithNext()->deleteTree(), argp);
|
VL_DO_DANGLING(argp->unlinkFrBackWithNext()->deleteTree(), argp);
|
||||||
}
|
}
|
||||||
// Type depends on the method used, let V3Width figure it out later
|
// 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 indexArgRefp = new AstLambdaArgRef{argFl, name + "__DOT__index", true};
|
||||||
const auto valueArgRefp = new AstLambdaArgRef{argFl, name, false};
|
const auto valueArgRefp = new AstLambdaArgRef{argFl, name, false};
|
||||||
const auto newp = new AstWith{nodep->fileline(), indexArgRefp, valueArgRefp,
|
const auto newp = new AstWith{nodep->fileline(), indexArgRefp, valueArgRefp,
|
||||||
nodep->exprp()->unlinkFrBackWithNext()};
|
nodep->exprsp()->unlinkFrBackWithNext()};
|
||||||
funcrefp->addPinsp(newp);
|
funcrefp->addPinsp(newp);
|
||||||
}
|
}
|
||||||
nodep->replaceWith(funcrefp->unlinkFrBack());
|
nodep->replaceWith(funcrefp->unlinkFrBack());
|
||||||
@@ -1528,6 +1545,7 @@ class LinkDotFindVisitor final : public VNVisitor {
|
|||||||
// Insert argref's name into symbol table
|
// Insert argref's name into symbol table
|
||||||
m_statep->insertSym(m_curSymp, nodep->valueArgRefp()->name(), nodep->valueArgRefp(),
|
m_statep->insertSym(m_curSymp, nodep->valueArgRefp()->name(), nodep->valueArgRefp(),
|
||||||
nullptr);
|
nullptr);
|
||||||
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2181,6 +2199,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||||||
AstVar* const eventp = new AstVar{
|
AstVar* const eventp = new AstVar{
|
||||||
clockingp->fileline(), VVarType::MODULETEMP, clockingp->name(), VFlagChildDType{},
|
clockingp->fileline(), VVarType::MODULETEMP, clockingp->name(), VFlagChildDType{},
|
||||||
new AstBasicDType{clockingp->fileline(), VBasicDTypeKwd::EVENT}};
|
new AstBasicDType{clockingp->fileline(), VBasicDTypeKwd::EVENT}};
|
||||||
|
eventp->lifetime(VLifetime::STATIC);
|
||||||
clockingp->eventp(eventp);
|
clockingp->eventp(eventp);
|
||||||
// Trigger the clocking event in Observed (IEEE 1800-2023 14.13)
|
// Trigger the clocking event in Observed (IEEE 1800-2023 14.13)
|
||||||
clockingp->addNextHere(new AstAlwaysObserved{
|
clockingp->addNextHere(new AstAlwaysObserved{
|
||||||
@@ -2195,16 +2214,21 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||||||
return reinterpret_cast<VSymEnt*>(clockingp->eventp()->user1p());
|
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) {
|
bool isParamedClassRef(const AstNode* nodep) {
|
||||||
// Is this a parameterized reference to a class, or a reference to class parameter
|
// Is this a parameterized reference to a class, or a reference to class parameter
|
||||||
if (const auto* classRefp = VN_CAST(nodep, ClassOrPackageRef)) {
|
if (const auto* classRefp = VN_CAST(nodep, ClassOrPackageRef)) {
|
||||||
if (classRefp->paramsp()) return true;
|
if (classRefp->paramsp()) return true;
|
||||||
const auto* classp = classRefp->classOrPackageNodep();
|
const auto* classp = classRefp->classOrPackageNodep();
|
||||||
while (const auto* typedefp = VN_CAST(classp, Typedef)) classp = typedefp->subDTypep();
|
while (const auto* typedefp = VN_CAST(classp, Typedef)) classp = typedefp->subDTypep();
|
||||||
return (VN_IS(classp, ClassRefDType) && VN_AS(classp, ClassRefDType)->paramsp())
|
return isParamedClassRefDType(classp);
|
||||||
|| VN_IS(classp, ParamTypeDType);
|
|
||||||
}
|
}
|
||||||
return false;
|
return isParamedClassRefDType(nodep);
|
||||||
}
|
}
|
||||||
VSymEnt* getThisClassSymp() {
|
VSymEnt* getThisClassSymp() {
|
||||||
VSymEnt* classSymp = m_ds.m_dotSymp;
|
VSymEnt* classSymp = m_ds.m_dotSymp;
|
||||||
@@ -2240,9 +2264,9 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||||||
<< "... Location of interface class's function\n"
|
<< "... Location of interface class's function\n"
|
||||||
<< interfaceSubp->warnContextSecondary());
|
<< interfaceSubp->warnContextSecondary());
|
||||||
}
|
}
|
||||||
const auto it = m_ifClassImpNames.find(interfaceSubp->name());
|
const auto itn = m_ifClassImpNames.find(interfaceSubp->name());
|
||||||
if (!existsInChild && it != m_ifClassImpNames.end()
|
if (!existsInChild && itn != m_ifClassImpNames.end()
|
||||||
&& it->second != interfaceSubp) { // Not exact same function from diamond
|
&& itn->second != interfaceSubp) { // Not exact same function from diamond
|
||||||
implementsClassp->v3error(
|
implementsClassp->v3error(
|
||||||
"Class " << implementsClassp->prettyNameQ() << impOrExtends
|
"Class " << implementsClassp->prettyNameQ() << impOrExtends
|
||||||
<< baseClassp->prettyNameQ()
|
<< baseClassp->prettyNameQ()
|
||||||
@@ -2619,7 +2643,16 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||||||
= VN_AS(m_ds.m_dotp->lhsp(), ClassOrPackageRef);
|
= VN_AS(m_ds.m_dotp->lhsp(), ClassOrPackageRef);
|
||||||
classOrPackagep = cpackagerefp->classOrPackagep();
|
classOrPackagep = cpackagerefp->classOrPackagep();
|
||||||
UASSERT_OBJ(classOrPackagep, m_ds.m_dotp->lhsp(), "Bad package link");
|
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;
|
m_ds.m_dotPos = DP_SCOPE;
|
||||||
} else if (m_ds.m_dotPos == DP_SCOPE) {
|
} else if (m_ds.m_dotPos == DP_SCOPE) {
|
||||||
// {a}.{b}, where {a} maybe a module name
|
// {a}.{b}, where {a} maybe a module name
|
||||||
@@ -3078,8 +3111,42 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||||||
void visit(AstMethodCall* nodep) override {
|
void visit(AstMethodCall* nodep) override {
|
||||||
// Created here so should already be resolved.
|
// Created here so should already be resolved.
|
||||||
VL_RESTORER(m_ds);
|
VL_RESTORER(m_ds);
|
||||||
|
VL_RESTORER(m_pinSymp);
|
||||||
{
|
{
|
||||||
m_ds.init(m_curSymp);
|
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);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3095,7 +3162,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||||||
if (nodep->user3SetOnce()) return;
|
if (nodep->user3SetOnce()) return;
|
||||||
UINFO(8, " " << nodep << endl);
|
UINFO(8, " " << nodep << endl);
|
||||||
UINFO(8, " " << m_ds.ascii() << endl);
|
UINFO(8, " " << m_ds.ascii() << endl);
|
||||||
{
|
if (m_ds.m_dotPos != DP_MEMBER || nodep->name() != "randomize") {
|
||||||
// Visit arguments at the beginning.
|
// Visit arguments at the beginning.
|
||||||
// They may be visitted even if the current node can't be linked now.
|
// They may be visitted even if the current node can't be linked now.
|
||||||
VL_RESTORER(m_ds);
|
VL_RESTORER(m_ds);
|
||||||
@@ -3439,6 +3506,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||||||
VL_RESTORER(m_curSymp);
|
VL_RESTORER(m_curSymp);
|
||||||
{
|
{
|
||||||
m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep);
|
m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep);
|
||||||
|
if (m_pinSymp) m_curSymp->importFromClass(m_statep->symsp(), m_pinSymp);
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
m_ds.m_dotSymp = VL_RESTORER_PREV(m_curSymp);
|
m_ds.m_dotSymp = VL_RESTORER_PREV(m_curSymp);
|
||||||
|
|||||||
+2
-1
@@ -341,7 +341,8 @@ class LinkJumpVisitor final : public VNVisitor {
|
|||||||
}
|
}
|
||||||
// if (debug() >= 9) { UINFO(0, "\n"); blockp->dumpTree("- labeli: "); }
|
// if (debug() >= 9) { UINFO(0, "\n"); blockp->dumpTree("- labeli: "); }
|
||||||
if (!blockp) {
|
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)) {
|
} else if (AstBegin* const beginp = VN_CAST(blockp, Begin)) {
|
||||||
// Jump to the end of the named block
|
// Jump to the end of the named block
|
||||||
AstJumpLabel* const labelp = findAddLabel(beginp, false);
|
AstJumpLabel* const labelp = findAddLabel(beginp, false);
|
||||||
|
|||||||
+33
-19
@@ -22,6 +22,8 @@
|
|||||||
|
|
||||||
#include "V3LinkLValue.h"
|
#include "V3LinkLValue.h"
|
||||||
|
|
||||||
|
#include "V3Task.h"
|
||||||
|
|
||||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||||
|
|
||||||
//######################################################################
|
//######################################################################
|
||||||
@@ -33,6 +35,7 @@ class LinkLValueVisitor final : public VNVisitor {
|
|||||||
// STATE
|
// STATE
|
||||||
bool m_setContinuously = false; // Set that var has some continuous assignment
|
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_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
|
VAccess m_setRefLvalue; // Set VarRefs to lvalues for pin assignments
|
||||||
|
|
||||||
// VISITs
|
// VISITs
|
||||||
@@ -40,15 +43,20 @@ class LinkLValueVisitor final : public VNVisitor {
|
|||||||
void visit(AstNodeVarRef* nodep) override {
|
void visit(AstNodeVarRef* nodep) override {
|
||||||
// VarRef: LValue its reference
|
// VarRef: LValue its reference
|
||||||
if (m_setRefLvalue != VAccess::NOCHANGE) nodep->access(m_setRefLvalue);
|
if (m_setRefLvalue != VAccess::NOCHANGE) nodep->access(m_setRefLvalue);
|
||||||
if (nodep->varp()) {
|
if (nodep->varp() && nodep->access().isWriteOrRW()) {
|
||||||
if (nodep->access().isWriteOrRW() && m_setContinuously) {
|
if (m_setContinuously) {
|
||||||
nodep->varp()->isContinuously(true);
|
nodep->varp()->isContinuously(true);
|
||||||
// Strength may only be specified in continuous assignment,
|
// Strength may only be specified in continuous assignment,
|
||||||
// so it is needed to check only if m_setContinuously is true
|
// so it is needed to check only if m_setContinuously is true
|
||||||
if (m_setStrengthSpecified) nodep->varp()->hasStrengthAssignment(true);
|
if (m_setStrengthSpecified) nodep->varp()->hasStrengthAssignment(true);
|
||||||
}
|
}
|
||||||
if (nodep->access().isWriteOrRW() && !nodep->varp()->isFuncLocal()
|
if (const AstClockingItem* itemp = VN_CAST(nodep->varp()->backp(), ClockingItem)) {
|
||||||
&& nodep->varp()->isReadOnly()) {
|
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,
|
nodep->v3warn(ASSIGNIN,
|
||||||
"Assigning to input/const variable: " << nodep->prettyNameQ());
|
"Assigning to input/const variable: " << nodep->prettyNameQ());
|
||||||
}
|
}
|
||||||
@@ -77,7 +85,11 @@ class LinkLValueVisitor final : public VNVisitor {
|
|||||||
if (AstAssignW* assignwp = VN_CAST(nodep, AssignW)) {
|
if (AstAssignW* assignwp = VN_CAST(nodep, AssignW)) {
|
||||||
if (assignwp->strengthSpecp()) m_setStrengthSpecified = true;
|
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_setRefLvalue = VAccess::NOCHANGE;
|
||||||
m_setContinuously = false;
|
m_setContinuously = false;
|
||||||
m_setStrengthSpecified = false;
|
m_setStrengthSpecified = false;
|
||||||
@@ -87,9 +99,11 @@ class LinkLValueVisitor final : public VNVisitor {
|
|||||||
void visit(AstRelease* nodep) override {
|
void visit(AstRelease* nodep) override {
|
||||||
VL_RESTORER(m_setRefLvalue);
|
VL_RESTORER(m_setRefLvalue);
|
||||||
VL_RESTORER(m_setContinuously);
|
VL_RESTORER(m_setContinuously);
|
||||||
|
VL_RESTORER(m_setForcedByCode);
|
||||||
{
|
{
|
||||||
m_setRefLvalue = VAccess::WRITE;
|
m_setRefLvalue = VAccess::WRITE;
|
||||||
m_setContinuously = false;
|
m_setContinuously = false;
|
||||||
|
m_setForcedByCode = true;
|
||||||
iterateAndNextNull(nodep->lhsp());
|
iterateAndNextNull(nodep->lhsp());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,23 +293,23 @@ class LinkLValueVisitor final : public VNVisitor {
|
|||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
void visit(AstNodeFTaskRef* nodep) override {
|
void visit(AstNodeFTaskRef* nodep) override {
|
||||||
AstNode* pinp = nodep->pinsp();
|
|
||||||
const AstNodeFTask* const taskp = nodep->taskp();
|
const AstNodeFTask* const taskp = nodep->taskp();
|
||||||
// We'll deal with mismatching pins later
|
// We'll deal with mismatching pins later
|
||||||
if (!taskp) return;
|
if (!taskp) return;
|
||||||
for (AstNode* stmtp = taskp->stmtsp(); stmtp && pinp; stmtp = stmtp->nextp()) {
|
const V3TaskConnects tconnects
|
||||||
if (const AstVar* const portp = VN_CAST(stmtp, Var)) {
|
= V3Task::taskConnects(nodep, taskp->stmtsp(), nullptr, false);
|
||||||
if (portp->isIO()) {
|
for (const auto& tconnect : tconnects) {
|
||||||
if (portp->isWritable()) {
|
const AstVar* const portp = tconnect.first;
|
||||||
m_setRefLvalue = VAccess::WRITE;
|
const AstArg* const argp = tconnect.second;
|
||||||
iterate(pinp);
|
if (!argp) continue;
|
||||||
m_setRefLvalue = VAccess::NOCHANGE;
|
AstNodeExpr* const pinp = argp->exprp();
|
||||||
} else {
|
if (!pinp) continue;
|
||||||
iterate(pinp);
|
if (portp->isWritable()) {
|
||||||
}
|
m_setRefLvalue = VAccess::WRITE;
|
||||||
// Advance pin
|
iterate(pinp);
|
||||||
pinp = pinp->nextp();
|
m_setRefLvalue = VAccess::NOCHANGE;
|
||||||
}
|
} else {
|
||||||
|
iterate(pinp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -874,7 +874,7 @@ class LinkParseVisitor final : public VNVisitor {
|
|||||||
itemp->v3fatalSrc("Incorrect direction");
|
itemp->v3fatalSrc("Incorrect direction");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pushDeletep(itemp->unlinkFrBack());
|
VL_DO_DANGLING(pushDeletep(itemp->unlinkFrBack()), itemp);
|
||||||
}
|
}
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -81,7 +81,7 @@ class V3List final {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VL_ATTR_ALWINLINE
|
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"););
|
UDEBUGONLY(UASSERT(fallbackp, "Prefetch fallback pointer must be non nullptr"););
|
||||||
// This compiles to a branchless prefetch with cmove, with the address always valid
|
// This compiles to a branchless prefetch with cmove, with the address always valid
|
||||||
VL_PREFETCH_RW(elementp ? elementp : fallbackp);
|
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
|
T_Base* m_currp; // Currently iterated element, or 'nullptr' for 'end()' iterator
|
||||||
|
|
||||||
VL_ATTR_ALWINLINE
|
VL_ATTR_ALWINLINE
|
||||||
SimpleItertatorImpl(T_Base* elementp)
|
explicit SimpleItertatorImpl(T_Base* elementp)
|
||||||
: m_currp{elementp} {}
|
: m_currp{elementp} {}
|
||||||
|
|
||||||
VL_ATTR_ALWINLINE
|
VL_ATTR_ALWINLINE
|
||||||
@@ -159,7 +159,7 @@ class V3List final {
|
|||||||
|
|
||||||
ListType& m_list; // The proxied list
|
ListType& m_list; // The proxied list
|
||||||
|
|
||||||
UnlinkableProxy(ListType& list)
|
explicit UnlinkableProxy(ListType& list)
|
||||||
: m_list{list} {}
|
: m_list{list} {}
|
||||||
|
|
||||||
// Unlinkable iterator class template. This only supports enough for range based for loops.
|
// 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
|
T_Base* m_nextp; // Next element after current, or 'nullptr' for 'end()' iterator
|
||||||
|
|
||||||
VL_ATTR_ALWINLINE
|
VL_ATTR_ALWINLINE
|
||||||
UnlinkableItertatorImpl(T_Base* elementp)
|
explicit UnlinkableItertatorImpl(T_Base* elementp)
|
||||||
: m_currp{elementp}
|
: m_currp{elementp}
|
||||||
, m_nextp{toLinks(m_currp).m_nextp} {}
|
, m_nextp{toLinks(m_currp).m_nextp} {}
|
||||||
VL_ATTR_ALWINLINE
|
VL_ATTR_ALWINLINE
|
||||||
UnlinkableItertatorImpl(std::nullptr_t)
|
explicit UnlinkableItertatorImpl(std::nullptr_t)
|
||||||
: m_currp{nullptr}
|
: m_currp{nullptr}
|
||||||
, m_nextp{nullptr} {}
|
, m_nextp{nullptr} {}
|
||||||
|
|
||||||
|
|||||||
+8
-3
@@ -63,9 +63,14 @@ class LocalizeVisitor final : public VNVisitor {
|
|||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
bool isOptimizable(AstVarScope* nodep) {
|
bool isOptimizable(AstVarScope* nodep) {
|
||||||
return !nodep->user1() || // Not marked as not optimizable, or ...
|
// Don't want to malloc/free the backing store all the time
|
||||||
(nodep->varp()->varType() == VVarType::BLOCKTEMP
|
if (VN_IS(nodep->dtypep(), NBACommitQueueDType)) return false;
|
||||||
&& m_accessors(nodep).size() == 1); // .. a block temp used in a single CFunc
|
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) {
|
static bool existsNonLeaf(const std::unordered_set<AstCFunc*>& funcps) {
|
||||||
|
|||||||
+1
-1
@@ -1891,7 +1891,7 @@ V3Number& V3Number::opSub(const V3Number& lhs, const V3Number& rhs) {
|
|||||||
NUM_ASSERT_OP_ARGS2(lhs, rhs);
|
NUM_ASSERT_OP_ARGS2(lhs, rhs);
|
||||||
NUM_ASSERT_LOGIC_ARGS2(lhs, rhs);
|
NUM_ASSERT_LOGIC_ARGS2(lhs, rhs);
|
||||||
if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX();
|
if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX();
|
||||||
V3Number negrhs(&rhs, rhs.width());
|
V3Number negrhs(&rhs, width());
|
||||||
negrhs.opNegate(rhs);
|
negrhs.opNegate(rhs);
|
||||||
return opAdd(lhs, negrhs);
|
return opAdd(lhs, negrhs);
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-8
@@ -67,6 +67,7 @@ public:
|
|||||||
using DirMap = std::map<const string, std::set<std::string>>; // Directory listing
|
using DirMap = std::map<const string, std::set<std::string>>; // Directory listing
|
||||||
|
|
||||||
// STATE
|
// 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_allArgs; // List of every argument encountered
|
||||||
std::list<string> m_incDirUsers; // Include directories (ordered)
|
std::list<string> m_incDirUsers; // Include directories (ordered)
|
||||||
std::set<string> m_incDirUserSet; // Include directories (for removing duplicates)
|
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::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); }
|
void V3Options::addArg(const string& arg) { m_impp->m_allArgs.push_back(arg); }
|
||||||
|
|
||||||
string V3Options::allArgsString() const VL_MT_SAFE {
|
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.
|
// 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;
|
std::set<string> vFiles;
|
||||||
for (const auto& vFile : m_vFiles) vFiles.insert(vFile);
|
for (const auto& vFile : m_vFiles) vFiles.insert(vFile);
|
||||||
string out;
|
string out;
|
||||||
for (std::list<string>::const_iterator it = m_impp->m_allArgs.begin();
|
for (std::list<string>::const_iterator it = m_impp->m_lineArgs.begin();
|
||||||
it != m_impp->m_allArgs.end(); ++it) {
|
it != m_impp->m_lineArgs.end(); ++it) {
|
||||||
int skip = 0;
|
int skip = 0;
|
||||||
if (it->length() >= 2 && (*it)[0] == '-' && (*it)[1] == '-') {
|
if (it->length() >= 2 && (*it)[0] == '-' && (*it)[1] == '-') {
|
||||||
skip = 2;
|
skip = 2;
|
||||||
@@ -426,7 +429,7 @@ string V3Options::allArgsStringForHierBlock(bool forTop) const {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else { // Not an option
|
} 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++
|
|| m_cppFiles.find(*it) != m_cppFiles.end()) { // Remove C++
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -493,7 +496,10 @@ string V3Options::fileExists(const string& filename) {
|
|||||||
try {
|
try {
|
||||||
for (const auto& dirEntry : std::filesystem::directory_iterator(dir.c_str()))
|
for (const auto& dirEntry : std::filesystem::directory_iterator(dir.c_str()))
|
||||||
setp->insert(dirEntry.path().filename().string());
|
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
|
#else
|
||||||
if (DIR* const dirp = opendir(dir.c_str())) {
|
if (DIR* const dirp = opendir(dir.c_str())) {
|
||||||
while (struct dirent* direntp = readdir(dirp)) setp->insert(direntp->d_name);
|
while (struct dirent* direntp = readdir(dirp)) setp->insert(direntp->d_name);
|
||||||
@@ -750,6 +756,18 @@ string V3Options::getenvVERILATOR_ROOT() {
|
|||||||
return V3Os::filenameCleanup(var);
|
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() {
|
string V3Options::getStdPackagePath() {
|
||||||
return V3Os::filenameJoin(getenvVERILATOR_ROOT(), "include", "verilated_std.sv");
|
return V3Os::filenameJoin(getenvVERILATOR_ROOT(), "include", "verilated_std.sv");
|
||||||
}
|
}
|
||||||
@@ -988,6 +1006,9 @@ string V3Options::argString(int argc, char** argv) {
|
|||||||
// V3 Options Parsing
|
// V3 Options Parsing
|
||||||
|
|
||||||
void V3Options::parseOpts(FileLine* fl, int argc, char** argv) VL_MT_DISABLED {
|
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
|
// Parse all options
|
||||||
// Initial entry point from Verilator.cpp
|
// Initial entry point from Verilator.cpp
|
||||||
parseOptsList(fl, ".", argc, argv);
|
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("-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 auto setLang = [this, fl](const char* valp) {
|
||||||
const V3LangCode optval{valp};
|
const V3LangCode optval{valp};
|
||||||
if (optval.legal()) {
|
if (optval.legal()) {
|
||||||
@@ -1323,9 +1347,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
|
|||||||
DECL_OPTION("-language", CbVal, setLang);
|
DECL_OPTION("-language", CbVal, setLang);
|
||||||
DECL_OPTION("-lib-create", Set, &m_libCreate);
|
DECL_OPTION("-lib-create", Set, &m_libCreate);
|
||||||
DECL_OPTION("-lint-only", OnOff, &m_lintOnly);
|
DECL_OPTION("-lint-only", OnOff, &m_lintOnly);
|
||||||
DECL_OPTION("-l2-name", Set, &m_l2Name);
|
DECL_OPTION("-localize-max-size", Set, &m_localizeMaxSize);
|
||||||
DECL_OPTION("-no-l2name", CbCall, [this]() { m_l2Name = ""; }).undocumented(); // Historical
|
|
||||||
DECL_OPTION("-l2name", CbCall, [this]() { m_l2Name = "v"; }).undocumented(); // Historical
|
|
||||||
DECL_OPTION("-main-top-name", Set, &m_mainTopName);
|
DECL_OPTION("-main-top-name", Set, &m_mainTopName);
|
||||||
|
|
||||||
DECL_OPTION("-MAKEFLAGS", CbVal, callStrSetter(&V3Options::addMakeFlags));
|
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_pinsScBigUint = flag;
|
||||||
m_pinsBv = 513;
|
m_pinsBv = 513;
|
||||||
});
|
});
|
||||||
|
DECL_OPTION("-pins-inout-enables", OnOff, &m_pinsInoutEnables);
|
||||||
DECL_OPTION("-pins-uint8", OnOff, &m_pinsUint8);
|
DECL_OPTION("-pins-uint8", OnOff, &m_pinsUint8);
|
||||||
DECL_OPTION("-pipe-filter", Set, &m_pipeFilter);
|
DECL_OPTION("-pipe-filter", Set, &m_pipeFilter);
|
||||||
DECL_OPTION("-pp-comments", OnOff, &m_ppComments);
|
DECL_OPTION("-pp-comments", OnOff, &m_ppComments);
|
||||||
|
|||||||
+7
-1
@@ -258,6 +258,7 @@ private:
|
|||||||
bool m_main = false; // main switch: --main
|
bool m_main = false; // main switch: --main
|
||||||
bool m_outFormatOk = false; // main switch: --cc, --sc or --sp was specified
|
bool m_outFormatOk = false; // main switch: --cc, --sc or --sp was specified
|
||||||
bool m_pedantic = false; // main switch: --Wpedantic
|
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_pinsScUint = false; // main switch: --pins-sc-uint
|
||||||
bool m_pinsScBigUint = false; // main switch: --pins-sc-biguint
|
bool m_pinsScBigUint = false; // main switch: --pins-sc-biguint
|
||||||
bool m_pinsUint8 = false; // main switch: --pins-uint8
|
bool m_pinsUint8 = false; // main switch: --pins-uint8
|
||||||
@@ -308,6 +309,7 @@ private:
|
|||||||
int m_instrCountDpi = 200; // main switch: --instr-count-dpi
|
int m_instrCountDpi = 200; // main switch: --instr-count-dpi
|
||||||
bool m_jsonEditNums = true; // main switch: --no-json-edit-nums
|
bool m_jsonEditNums = true; // main switch: --no-json-edit-nums
|
||||||
bool m_jsonIds = true; // main switch: --no-json-ids
|
bool m_jsonIds = true; // main switch: --no-json-ids
|
||||||
|
int m_localizeMaxSize = 1024; // main switch: --localize-max-size
|
||||||
VOptionBool m_makeDepend; // main switch: -MMD
|
VOptionBool m_makeDepend; // main switch: -MMD
|
||||||
int m_maxNumWidth = 65536; // main switch: --max-num-width
|
int m_maxNumWidth = 65536; // main switch: --max-num-width
|
||||||
int m_moduleRecursion = 100; // main switch: --module-recursion-depth
|
int m_moduleRecursion = 100; // main switch: --module-recursion-depth
|
||||||
@@ -398,6 +400,7 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
// METHODS
|
// METHODS
|
||||||
|
void addLineArg(const string& arg);
|
||||||
void addArg(const string& arg);
|
void addArg(const string& arg);
|
||||||
void addDefine(const string& defline, bool allowPlus) VL_MT_DISABLED;
|
void addDefine(const string& defline, bool allowPlus) VL_MT_DISABLED;
|
||||||
void addFuture(const string& flag);
|
void addFuture(const string& flag);
|
||||||
@@ -509,6 +512,7 @@ public:
|
|||||||
bool outFormatOk() const { return m_outFormatOk; }
|
bool outFormatOk() const { return m_outFormatOk; }
|
||||||
bool keepTempFiles() const { return (V3Error::debugDefault() != 0); }
|
bool keepTempFiles() const { return (V3Error::debugDefault() != 0); }
|
||||||
bool pedantic() const { return m_pedantic; }
|
bool pedantic() const { return m_pedantic; }
|
||||||
|
bool pinsInoutEnables() const { return m_pinsInoutEnables; }
|
||||||
bool pinsScUint() const { return m_pinsScUint; }
|
bool pinsScUint() const { return m_pinsScUint; }
|
||||||
bool pinsScBigUint() const { return m_pinsScBigUint; }
|
bool pinsScBigUint() const { return m_pinsScBigUint; }
|
||||||
bool pinsUint8() const { return m_pinsUint8; }
|
bool pinsUint8() const { return m_pinsUint8; }
|
||||||
@@ -544,6 +548,7 @@ public:
|
|||||||
int ifDepth() const { return m_ifDepth; }
|
int ifDepth() const { return m_ifDepth; }
|
||||||
int inlineMult() const { return m_inlineMult; }
|
int inlineMult() const { return m_inlineMult; }
|
||||||
int instrCountDpi() const { return m_instrCountDpi; }
|
int instrCountDpi() const { return m_instrCountDpi; }
|
||||||
|
int localizeMaxSize() const { return m_localizeMaxSize; }
|
||||||
bool jsonEditNums() const { return m_jsonEditNums; }
|
bool jsonEditNums() const { return m_jsonEditNums; }
|
||||||
bool jsonIds() const { return m_jsonIds; }
|
bool jsonIds() const { return m_jsonIds; }
|
||||||
VOptionBool makeDepend() const { return m_makeDepend; }
|
VOptionBool makeDepend() const { return m_makeDepend; }
|
||||||
@@ -695,7 +700,7 @@ public:
|
|||||||
string allArgsString() const VL_MT_SAFE; ///< Return all passed arguments as simple string
|
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
|
// Return options for child hierarchical blocks when forTop==false, otherwise returns args for
|
||||||
// the top module.
|
// 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 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 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;
|
void parseOptsFile(FileLine* fl, const string& filename, bool rel) VL_MT_DISABLED;
|
||||||
@@ -713,6 +718,7 @@ public:
|
|||||||
static string getenvSYSTEMC_INCLUDE();
|
static string getenvSYSTEMC_INCLUDE();
|
||||||
static string getenvSYSTEMC_LIBDIR();
|
static string getenvSYSTEMC_LIBDIR();
|
||||||
static string getenvVERILATOR_ROOT();
|
static string getenvVERILATOR_ROOT();
|
||||||
|
static string getenvVERILATOR_SOLVER();
|
||||||
static string getStdPackagePath();
|
static string getStdPackagePath();
|
||||||
static string getSupported(const string& var);
|
static string getSupported(const string& var);
|
||||||
static bool systemCSystemWide();
|
static bool systemCSystemWide();
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ class OrderMoveDomScope final {
|
|||||||
|
|
||||||
struct Hash final {
|
struct Hash final {
|
||||||
size_t operator()(const DomScopeMapKey& key) const {
|
size_t operator()(const DomScopeMapKey& key) const {
|
||||||
|
// cppcheck-suppress unreadVariable // cppcheck bug
|
||||||
V3Hash hash{reinterpret_cast<uint64_t>(key.m_domainp)};
|
V3Hash hash{reinterpret_cast<uint64_t>(key.m_domainp)};
|
||||||
hash += reinterpret_cast<uint64_t>(key.m_scopep);
|
hash += reinterpret_cast<uint64_t>(key.m_scopep);
|
||||||
return hash.value();
|
return hash.value();
|
||||||
@@ -199,7 +200,7 @@ class OrderMoveGraphSerializer final {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// CONSTRUCTOR
|
// CONSTRUCTOR
|
||||||
OrderMoveGraphSerializer(OrderMoveGraph& moveGraph) {
|
explicit OrderMoveGraphSerializer(OrderMoveGraph& moveGraph) {
|
||||||
// Set V3GraphVertex::user() to the number of incoming edges (upstream dependencies)
|
// Set V3GraphVertex::user() to the number of incoming edges (upstream dependencies)
|
||||||
for (V3GraphVertex& vtx : moveGraph.vertices()) {
|
for (V3GraphVertex& vtx : moveGraph.vertices()) {
|
||||||
const uint32_t nDeps = vtx.inEdges().size();
|
const uint32_t nDeps = vtx.inEdges().size();
|
||||||
|
|||||||
+2
-21
@@ -86,26 +86,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
|||||||
// Environment
|
// Environment
|
||||||
|
|
||||||
string V3Os::getenvStr(const string& envvar, const string& defaultValue) {
|
string V3Os::getenvStr(const string& envvar, const string& defaultValue) {
|
||||||
string ret = "";
|
return VString::escapeStringForPath(VlOs::getenvStr(envvar, defaultValue));
|
||||||
#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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void V3Os::setenvStr(const string& envvar, const string& value, const string& why) {
|
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
|
// setenv() replaced by putenv() in Solaris environment. Prototype is different
|
||||||
// putenv() requires NAME=VALUE format
|
// putenv() requires NAME=VALUE format
|
||||||
const string vareq = envvar + "=" + value;
|
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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ AstNodeDType* V3ParseGrammar::createArray(AstNodeDType* basep, AstNodeRange* nra
|
|||||||
arrayp = new AstUnpackArrayDType{rangep->fileline(), VFlagChildDType{}, arrayp,
|
arrayp = new AstUnpackArrayDType{rangep->fileline(), VFlagChildDType{}, arrayp,
|
||||||
rangep};
|
rangep};
|
||||||
} else if (VN_IS(nrangep, UnsizedRange)) {
|
} 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);
|
VL_DO_DANGLING(nrangep->deleteTree(), nrangep);
|
||||||
} else if (VN_IS(nrangep, BracketRange)) {
|
} else if (VN_IS(nrangep, BracketRange)) {
|
||||||
const AstBracketRange* const arangep = VN_AS(nrangep, BracketRange);
|
const AstBracketRange* const arangep = VN_AS(nrangep, BracketRange);
|
||||||
|
|||||||
+31
-25
@@ -688,43 +688,43 @@ string V3PreProcImp::defineSubst(VDefineRef* refp) {
|
|||||||
{ // Parse substitution define using arguments
|
{ // Parse substitution define using arguments
|
||||||
string argName;
|
string argName;
|
||||||
bool quote = false;
|
bool quote = false;
|
||||||
|
bool triquote = false;
|
||||||
bool backslashesc = false; // In \.....{space} block
|
bool backslashesc = false; // In \.....{space} block
|
||||||
// Note we go through the loop once more at the nullptr end-of-string
|
// 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)) {
|
for (const char* cp = value.c_str(); (*cp) || argName != ""; cp = (*cp ? cp + 1 : cp)) {
|
||||||
// UINFO(4, "CH "<<*cp<<" an "<<argName<<endl);
|
// UINFO(4, "CH "<<*cp<<" an "<<argName<<endl);
|
||||||
if (!quote && *cp == '\\') {
|
if (!quote && !triquote && *cp == '\\') {
|
||||||
backslashesc = true;
|
backslashesc = true;
|
||||||
} else if (std::isspace(*cp)) {
|
} else if (std::isspace(*cp)) {
|
||||||
backslashesc = false;
|
backslashesc = false;
|
||||||
}
|
}
|
||||||
// We don't check for quotes; some simulators expand even inside quotes
|
if (!quote && !triquote) {
|
||||||
if (std::isalpha(*cp) || *cp == '_'
|
if (std::isalpha(*cp) || *cp == '_'
|
||||||
|| *cp == '$' // Won't replace system functions, since no $ in argValueByName
|
|| *cp == '$' // Won't replace system functions, since no $ in argValueByName
|
||||||
|| (argName != "" && (std::isdigit(*cp) || *cp == '$'))) {
|
|| (argName != "" && (std::isdigit(*cp) || *cp == '$'))) {
|
||||||
argName += *cp;
|
argName += *cp;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (argName != "") {
|
if (argName != "") {
|
||||||
// Found a possible variable substitution
|
// Found a possible variable substitution
|
||||||
const auto iter = argValueByName.find(argName);
|
const auto iter = argValueByName.find(argName);
|
||||||
if (iter != argValueByName.end()) {
|
if (iter != argValueByName.end()) {
|
||||||
// Substitute
|
// Substitute
|
||||||
const string subst = iter->second;
|
const string subst = iter->second;
|
||||||
if (subst == "") {
|
if (subst == "") {
|
||||||
// Normally `` is removed later, but with no token after, we're otherwise
|
// Normally `` is removed later, but with no token after, we're
|
||||||
// stuck, so remove proceeding ``
|
// otherwise stuck, so remove proceeding ``
|
||||||
if (out.size() >= 2 && out.substr(out.size() - 2) == "``") {
|
if (out.size() >= 2 && out.substr(out.size() - 2) == "``") {
|
||||||
out = out.substr(0, out.size() - 2);
|
out = out.substr(0, out.size() - 2);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out += subst;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
out += subst;
|
out += argName;
|
||||||
}
|
}
|
||||||
} else {
|
argName = "";
|
||||||
out += argName;
|
|
||||||
}
|
}
|
||||||
argName = "";
|
|
||||||
}
|
|
||||||
if (!quote) {
|
|
||||||
// Check for `` only after we've detected end-of-argname
|
// Check for `` only after we've detected end-of-argname
|
||||||
if (cp[0] == '`' && cp[1] == '`') {
|
if (cp[0] == '`' && cp[1] == '`') {
|
||||||
if (backslashesc) {
|
if (backslashesc) {
|
||||||
@@ -772,6 +772,12 @@ string V3PreProcImp::defineSubst(VDefineRef* refp) {
|
|||||||
out += cp[0];
|
out += cp[0];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (cp[0] == '"' && cp[1] == '"' && cp[2] == '"') {
|
||||||
|
triquote = !triquote;
|
||||||
|
out += "\"\"\"";
|
||||||
|
cp += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (*cp == '"') quote = !quote;
|
if (*cp == '"') quote = !quote;
|
||||||
if (*cp) out += *cp;
|
if (*cp) out += *cp;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -86,7 +86,7 @@ class PremitVisitor final : public VNVisitor {
|
|||||||
// Extract into constant pool.
|
// Extract into constant pool.
|
||||||
const bool merge = v3Global.opt.fMergeConstPool();
|
const bool merge = v3Global.opt.fMergeConstPool();
|
||||||
varp = v3Global.rootp()->constPoolp()->findConst(constp, merge)->varp();
|
varp = v3Global.rootp()->constPoolp()->findConst(constp, merge)->varp();
|
||||||
nodep->deleteTree();
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||||
++m_extractedToConstPool;
|
++m_extractedToConstPool;
|
||||||
} else {
|
} else {
|
||||||
// Keep as local temporary.
|
// Keep as local temporary.
|
||||||
@@ -183,9 +183,11 @@ class PremitVisitor final : public VNVisitor {
|
|||||||
|
|
||||||
void visit(AstWhile* nodep) override {
|
void visit(AstWhile* nodep) override {
|
||||||
UINFO(4, " WHILE " << nodep << endl);
|
UINFO(4, " WHILE " << nodep << endl);
|
||||||
|
// cppcheck-suppress shadowVariable // Also restored below
|
||||||
START_STATEMENT_OR_RETURN(nodep);
|
START_STATEMENT_OR_RETURN(nodep);
|
||||||
iterateAndNextNull(nodep->precondsp());
|
iterateAndNextNull(nodep->precondsp());
|
||||||
{
|
{
|
||||||
|
// cppcheck-suppress shadowVariable // Also restored above
|
||||||
VL_RESTORER(m_inWhileCondp);
|
VL_RESTORER(m_inWhileCondp);
|
||||||
m_inWhileCondp = nodep;
|
m_inWhileCondp = nodep;
|
||||||
iterateAndNextNull(nodep->condp());
|
iterateAndNextNull(nodep->condp());
|
||||||
|
|||||||
+225
-17
@@ -39,24 +39,28 @@ class RandomizeMarkVisitor final : public VNVisitorConst {
|
|||||||
// NODE STATE
|
// NODE STATE
|
||||||
// Cleared on Netlist
|
// Cleared on Netlist
|
||||||
// AstClass::user1() -> bool. Set true to indicate needs randomize processing
|
// 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;
|
const VNUser1InUse m_inuser1;
|
||||||
|
|
||||||
using DerivedSet = std::unordered_set<AstClass*>;
|
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
|
BaseToDerivedMap m_baseToDerivedMap; // Mapping from base classes to classes that extend them
|
||||||
AstClass* m_classp = nullptr; // Current class
|
AstClass* m_classp = nullptr; // Current class
|
||||||
|
AstConstraintExpr* m_constraintExprp = nullptr; // Current constraint expression
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
void markMembers(AstClass* nodep) {
|
void markMembers(const AstClass* nodep) {
|
||||||
for (auto* classp = nodep; classp;
|
for (const AstClass* classp = nodep; classp;
|
||||||
classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr) {
|
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 member is rand and of class type, mark its class
|
||||||
if (VN_IS(memberp, Var) && VN_AS(memberp, Var)->isRand()) {
|
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)) {
|
= VN_CAST(memberp->dtypep()->skipRefp(), ClassRefDType)) {
|
||||||
auto* const rclassp = classRefp->classp();
|
AstClass* const rclassp = classRefp->classp();
|
||||||
if (!rclassp->user1()) {
|
if (!rclassp->user1()) {
|
||||||
rclassp->user1(true);
|
rclassp->user1(true);
|
||||||
markMembers(rclassp);
|
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);
|
const auto it = m_baseToDerivedMap.find(nodep);
|
||||||
if (it != m_baseToDerivedMap.end()) {
|
if (it != m_baseToDerivedMap.end()) {
|
||||||
for (auto* classp : it->second) {
|
for (auto* classp : it->second) {
|
||||||
if (!classp->user1p()) {
|
if (!classp->user1()) {
|
||||||
classp->user1(true);
|
classp->user1(true);
|
||||||
markMembers(classp);
|
markMembers(classp);
|
||||||
markDerived(classp);
|
markDerived(classp);
|
||||||
@@ -92,7 +96,7 @@ class RandomizeMarkVisitor final : public VNVisitorConst {
|
|||||||
iterateChildrenConst(nodep);
|
iterateChildrenConst(nodep);
|
||||||
if (nodep->extendsp()) {
|
if (nodep->extendsp()) {
|
||||||
// Save pointer to derived class
|
// Save pointer to derived class
|
||||||
AstClass* const basep = nodep->extendsp()->classp();
|
const AstClass* const basep = nodep->extendsp()->classp();
|
||||||
m_baseToDerivedMap[basep].insert(nodep);
|
m_baseToDerivedMap[basep].insert(nodep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,6 +115,22 @@ class RandomizeMarkVisitor final : public VNVisitorConst {
|
|||||||
if (nodep->name() != "randomize") return;
|
if (nodep->name() != "randomize") return;
|
||||||
if (m_classp) m_classp->user1(true);
|
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); }
|
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
||||||
|
|
||||||
@@ -123,6 +143,143 @@ public:
|
|||||||
~RandomizeMarkVisitor() override = default;
|
~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
|
// Visitor that defines a randomize method where needed
|
||||||
|
|
||||||
@@ -130,10 +287,15 @@ class RandomizeVisitor final : public VNVisitor {
|
|||||||
// NODE STATE
|
// NODE STATE
|
||||||
// Cleared on Netlist
|
// Cleared on Netlist
|
||||||
// AstClass::user1() -> bool. Set true to indicate needs randomize processing
|
// 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
|
// AstEnumDType::user2() -> AstVar*. Pointer to table with enum values
|
||||||
// AstClass::user3() -> AstFunc*. Pointer to randomize() method of a class
|
// 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)
|
// VNUser1InUse m_inuser1; (Allocated for use in RandomizeMarkVisitor)
|
||||||
const VNUser2InUse m_inuser2;
|
const VNUser2InUse m_inuser2;
|
||||||
|
const VNUser3InUse m_inuser3;
|
||||||
|
const VNUser4InUse m_inuser4;
|
||||||
|
|
||||||
// STATE
|
// STATE
|
||||||
VMemberMap m_memberMap; // Member names cached for fast lookup
|
VMemberMap m_memberMap; // Member names cached for fast lookup
|
||||||
@@ -147,9 +309,9 @@ class RandomizeVisitor final : public VNVisitor {
|
|||||||
AstVar* enumValueTabp(AstEnumDType* nodep) {
|
AstVar* enumValueTabp(AstEnumDType* nodep) {
|
||||||
if (nodep->user2p()) return VN_AS(nodep->user2p(), Var);
|
if (nodep->user2p()) return VN_AS(nodep->user2p(), Var);
|
||||||
UINFO(9, "Construct Venumvaltab " << nodep << endl);
|
UINFO(9, "Construct Venumvaltab " << nodep << endl);
|
||||||
AstNodeArrayDType* const vardtypep
|
AstNodeArrayDType* const vardtypep = new AstUnpackArrayDType{
|
||||||
= new AstUnpackArrayDType{nodep->fileline(), nodep->dtypep(),
|
nodep->fileline(), nodep->dtypep(),
|
||||||
new AstRange{nodep->fileline(), nodep->itemCount(), 0}};
|
new AstRange{nodep->fileline(), static_cast<int>(nodep->itemCount()), 0}};
|
||||||
AstInitArray* const initp = new AstInitArray{nodep->fileline(), vardtypep, nullptr};
|
AstInitArray* const initp = new AstInitArray{nodep->fileline(), vardtypep, nullptr};
|
||||||
v3Global.rootp()->typeTablep()->addTypesp(vardtypep);
|
v3Global.rootp()->typeTablep()->addTypesp(vardtypep);
|
||||||
AstVar* const varp
|
AstVar* const varp
|
||||||
@@ -274,6 +436,12 @@ class RandomizeVisitor final : public VNVisitor {
|
|||||||
funcp->addStmtsp(callp->makeStmt());
|
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
|
// VISITORS
|
||||||
void visit(AstNodeModule* nodep) override {
|
void visit(AstNodeModule* nodep) override {
|
||||||
@@ -315,20 +483,28 @@ class RandomizeVisitor final : public VNVisitor {
|
|||||||
beginValp = baseRandCallp;
|
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};
|
if (!beginValp) beginValp = new AstConst{fl, AstConst::WidthedValue{}, 32, 1};
|
||||||
|
|
||||||
funcp->addStmtsp(new AstAssign{fl, new AstVarRef{fl, fvarp, VAccess::WRITE}, beginValp});
|
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);
|
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();
|
const AstNodeDType* const dtypep = memberp->dtypep()->skipRefp();
|
||||||
if (VN_IS(dtypep, BasicDType) || VN_IS(dtypep, StructDType)) {
|
if (VN_IS(dtypep, BasicDType) || VN_IS(dtypep, StructDType)) {
|
||||||
AstVar* const randcVarp = newRandcVarsp(memberVarp);
|
AstVar* const randcVarp = newRandcVarsp(memberVarp);
|
||||||
AstVarRef* const refp = new AstVarRef{fl, memberVarp, VAccess::WRITE};
|
AstVarRef* const refp = new AstVarRef{fl, memberVarp, VAccess::WRITE};
|
||||||
AstNodeStmt* const stmtp = newRandStmtsp(fl, refp, randcVarp);
|
AstNodeStmt* const stmtp = newRandStmtsp(fl, refp, randcVarp);
|
||||||
funcp->addStmtsp(stmtp);
|
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) {
|
if (classRefp->classp() == nodep) {
|
||||||
memberp->v3warn(
|
memberp->v3warn(
|
||||||
E_UNSUPPORTED,
|
E_UNSUPPORTED,
|
||||||
@@ -358,8 +534,40 @@ class RandomizeVisitor final : public VNVisitor {
|
|||||||
nodep->user1(false);
|
nodep->user1(false);
|
||||||
}
|
}
|
||||||
void visit(AstConstraint* nodep) override {
|
void visit(AstConstraint* nodep) override {
|
||||||
nodep->v3warn(CONSTRAINTIGN, "Constraint ignored (unsupported)");
|
AstNodeFTask* const newp = VN_AS(m_memberMap.findMember(m_modp, "new"), NodeFTask);
|
||||||
if (!v3Global.opt.xmlOnly()) VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
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 {
|
void visit(AstRandCase* nodep) override {
|
||||||
// RANDCASE
|
// RANDCASE
|
||||||
|
|||||||
@@ -283,6 +283,7 @@ void transformForks(AstNetlist* const netlistp) {
|
|||||||
// STATE
|
// STATE
|
||||||
bool m_inClass = false; // Are we in a class?
|
bool m_inClass = false; // Are we in a class?
|
||||||
bool m_beginHasAwaits = false; // Does the current begin have awaits?
|
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
|
AstFork* m_forkp = nullptr; // Current fork
|
||||||
AstCFunc* m_funcp = nullptr; // Current function
|
AstCFunc* m_funcp = nullptr; // Current function
|
||||||
|
|
||||||
@@ -344,7 +345,13 @@ void transformForks(AstNetlist* const netlistp) {
|
|||||||
}
|
}
|
||||||
void visit(AstCFunc* nodep) override {
|
void visit(AstCFunc* nodep) override {
|
||||||
m_funcp = nodep;
|
m_funcp = nodep;
|
||||||
|
m_awaitMoved = false;
|
||||||
iterateChildren(nodep);
|
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;
|
m_funcp = nullptr;
|
||||||
}
|
}
|
||||||
void visit(AstVar* nodep) override {
|
void visit(AstVar* nodep) override {
|
||||||
@@ -399,6 +406,8 @@ void transformForks(AstNetlist* const netlistp) {
|
|||||||
if (!m_beginHasAwaits) {
|
if (!m_beginHasAwaits) {
|
||||||
// co_return at the end (either that or a co_await is required in a coroutine
|
// 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"});
|
newfuncp->addStmtsp(new AstCStmt{nodep->fileline(), "co_return;\n"});
|
||||||
|
} else {
|
||||||
|
m_awaitMoved = true;
|
||||||
}
|
}
|
||||||
remapLocals(newfuncp, callp);
|
remapLocals(newfuncp, callp);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -128,11 +128,11 @@ private:
|
|||||||
if (writesToVirtIface(nodep)) {
|
if (writesToVirtIface(nodep)) {
|
||||||
// Convert to always, as we have to assign the trigger var
|
// Convert to always, as we have to assign the trigger var
|
||||||
FileLine* const flp = nodep->fileline();
|
FileLine* const flp = nodep->fileline();
|
||||||
AstAlwaysPost* const postp = new AstAlwaysPost{flp, nullptr, nullptr};
|
AstAlwaysPost* const postp = new AstAlwaysPost{flp};
|
||||||
nodep->replaceWith(postp);
|
nodep->replaceWith(postp);
|
||||||
postp->addStmtsp(
|
postp->addStmtsp(
|
||||||
new AstAssign{flp, nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack()});
|
new AstAssign{flp, nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack()});
|
||||||
nodep->deleteTree();
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void visit(AstNodeIf* nodep) override {
|
void visit(AstNodeIf* nodep) override {
|
||||||
|
|||||||
@@ -167,7 +167,9 @@ class SenExprBuilder final {
|
|||||||
case VEdgeType::ET_CHANGED:
|
case VEdgeType::ET_CHANGED:
|
||||||
case VEdgeType::ET_HYBRID: //
|
case VEdgeType::ET_HYBRID: //
|
||||||
if (VN_IS(senp->dtypep()->skipRefp(), UnpackArrayDType)) {
|
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();
|
resultp->dtypeSetBit();
|
||||||
return {resultp, true};
|
return {resultp, true};
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -200,7 +200,7 @@ class SliceVisitor final : public VNVisitor {
|
|||||||
leOffset};
|
leOffset};
|
||||||
} else if (VN_IS(nodep, ArraySel) || VN_IS(nodep, NodeVarRef) || VN_IS(nodep, NodeSel)
|
} 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, CMethodHard) || VN_IS(nodep, MemberSel)
|
||||||
|| VN_IS(nodep, ExprStmt)) {
|
|| VN_IS(nodep, ExprStmt) || VN_IS(nodep, StructSel)) {
|
||||||
UINFO(9, " cloneSel(" << elements << "," << elemIdx << ") " << nodep << endl);
|
UINFO(9, " cloneSel(" << elements << "," << elemIdx << ") " << nodep << endl);
|
||||||
const int leOffset = !arrayp->rangep()->ascending()
|
const int leOffset = !arrayp->rangep()->ascending()
|
||||||
? arrayp->rangep()->elementsConst() - 1 - elemIdx
|
? arrayp->rangep()->elementsConst() - 1 - elemIdx
|
||||||
|
|||||||
+1
-1
@@ -819,7 +819,7 @@ class RemovePlaceholdersVisitor final : public VNVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (emptyOrCommentOnly) {
|
if (emptyOrCommentOnly) {
|
||||||
pushDeletep(nodep->unlinkFrBack());
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||||
++m_emptyAlways;
|
++m_emptyAlways;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -93,8 +93,7 @@ class SplitAsCleanVisitor final : public VNVisitor {
|
|||||||
m_keepStmt = true;
|
m_keepStmt = true;
|
||||||
} else {
|
} else {
|
||||||
UINFO(6, " Delete STMT " << nodep << endl);
|
UINFO(6, " Delete STMT " << nodep << endl);
|
||||||
nodep->unlinkFrBack();
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||||
pushDeletep(nodep);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If something below matches, the upper statement remains too.
|
// If something below matches, the upper statement remains too.
|
||||||
|
|||||||
+6
-5
@@ -79,7 +79,7 @@ public:
|
|||||||
: m_fl{fl} {}
|
: m_fl{fl} {}
|
||||||
|
|
||||||
~TableBuilder() {
|
~TableBuilder() {
|
||||||
if (m_initp) m_initp->deleteTree();
|
if (m_initp) VL_DO_DANGLING(m_initp->deleteTree(), m_initp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setTableSize(AstNodeDType* elemDType, unsigned size) {
|
void setTableSize(AstNodeDType* elemDType, unsigned size) {
|
||||||
@@ -89,16 +89,17 @@ public:
|
|||||||
// Create data type
|
// Create data type
|
||||||
const int width = elemDType->width();
|
const int width = elemDType->width();
|
||||||
AstNodeDType* const subDTypep
|
AstNodeDType* const subDTypep
|
||||||
= elemDType->isString()
|
= elemDType->isString() || elemDType->isDouble()
|
||||||
? elemDType
|
? elemDType
|
||||||
: v3Global.rootp()->findBitDType(width, width, VSigning::UNSIGNED);
|
: v3Global.rootp()->findBitDType(width, width, VSigning::UNSIGNED);
|
||||||
AstUnpackArrayDType* const tableDTypep = new AstUnpackArrayDType{
|
AstUnpackArrayDType* const tableDTypep = new AstUnpackArrayDType{
|
||||||
m_fl, subDTypep, new AstRange{m_fl, static_cast<int>(size), 0}};
|
m_fl, subDTypep, new AstRange{m_fl, static_cast<int>(size), 0}};
|
||||||
v3Global.rootp()->typeTablep()->addTypesp(tableDTypep);
|
v3Global.rootp()->typeTablep()->addTypesp(tableDTypep);
|
||||||
// Create table initializer (with default value 0)
|
// Create table initializer (with default value 0)
|
||||||
AstConst* const defaultp = elemDType->isString()
|
AstConst* const defaultp
|
||||||
? new AstConst{m_fl, AstConst::String{}, ""}
|
= elemDType->isString() ? new AstConst{m_fl, AstConst::String{}, ""}
|
||||||
: new AstConst{m_fl, AstConst::WidthedValue{}, width, 0};
|
: 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};
|
m_initp = new AstInitArray{m_fl, tableDTypep, defaultp};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+32
-17
@@ -305,6 +305,9 @@ struct TaskDpiUtils final {
|
|||||||
if (portp->basicp() && portp->basicp()->keyword() == VBasicDTypeKwd::CHANDLE) {
|
if (portp->basicp() && portp->basicp()->keyword() == VBasicDTypeKwd::CHANDLE) {
|
||||||
frstmt = "VL_CVT_VP_Q(" + frName;
|
frstmt = "VL_CVT_VP_Q(" + frName;
|
||||||
ket = ")";
|
ket = ")";
|
||||||
|
} else if (portp->basicp() && portp->basicp()->keyword() == VBasicDTypeKwd::STRING) {
|
||||||
|
frstmt = "VL_CVT_N_CSTR(" + frName;
|
||||||
|
ket = ")";
|
||||||
} else if ((portp->basicp() && portp->basicp()->isDpiPrimitive())) {
|
} else if ((portp->basicp() && portp->basicp()->isDpiPrimitive())) {
|
||||||
frstmt = frName;
|
frstmt = frName;
|
||||||
} else {
|
} else {
|
||||||
@@ -694,7 +697,7 @@ class TaskVisitor final : public VNVisitor {
|
|||||||
// differ we may get C compilation problems later
|
// differ we may get C compilation problems later
|
||||||
const std::string dpiType = portp->dpiArgType(false, false);
|
const std::string dpiType = portp->dpiArgType(false, false);
|
||||||
dpiproto += dpiType;
|
dpiproto += dpiType;
|
||||||
const std::string vType = portp->dtypep()->prettyDTypeName();
|
const std::string vType = portp->dtypep()->prettyDTypeName(false);
|
||||||
if (!portp->isDpiOpenArray() && dpiType != vType) {
|
if (!portp->isDpiOpenArray() && dpiType != vType) {
|
||||||
dpiproto += " /* " + vType + " */ ";
|
dpiproto += " /* " + vType + " */ ";
|
||||||
}
|
}
|
||||||
@@ -1589,14 +1592,13 @@ public:
|
|||||||
const char* const V3Task::s_dpiTemporaryVarSuffix = "__Vcvt";
|
const char* const V3Task::s_dpiTemporaryVarSuffix = "__Vcvt";
|
||||||
|
|
||||||
V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp,
|
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
|
// Output list will be in order of the port declaration variables (so
|
||||||
// func calls are made right in C)
|
// func calls are made right in C)
|
||||||
// Missing pin/expr? We return (pinvar, nullptr)
|
// Missing pin/expr? We return (pinvar, nullptr)
|
||||||
// Extra pin/expr? We clean it up
|
// Extra pin/expr? We clean it up
|
||||||
UINFO(9, "taskConnects " << nodep << endl);
|
UINFO(9, "taskConnects " << nodep << endl);
|
||||||
std::map<const std::string, int> nameToIndex;
|
std::map<const std::string, int> nameToIndex;
|
||||||
std::set<const AstVar*> argWrap; // Which ports are defaulted, forcing arg wrapper creation
|
|
||||||
V3TaskConnects tconnects;
|
V3TaskConnects tconnects;
|
||||||
UASSERT_OBJ(nodep->taskp(), nodep, "unlinked");
|
UASSERT_OBJ(nodep->taskp(), nodep, "unlinked");
|
||||||
|
|
||||||
@@ -1608,7 +1610,7 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp,
|
|||||||
if (portp->isIO()) {
|
if (portp->isIO()) {
|
||||||
tconnects.emplace_back(portp, static_cast<AstArg*>(nullptr));
|
tconnects.emplace_back(portp, static_cast<AstArg*>(nullptr));
|
||||||
nameToIndex.emplace(portp->name(), tpinnum); // For name based connections
|
nameToIndex.emplace(portp->name(), tpinnum); // For name based connections
|
||||||
tpinnum++;
|
++tpinnum;
|
||||||
if (portp->attrSFormat()) {
|
if (portp->attrSFormat()) {
|
||||||
sformatp = portp;
|
sformatp = portp;
|
||||||
} else if (sformatp) {
|
} else if (sformatp) {
|
||||||
@@ -1630,17 +1632,19 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp,
|
|||||||
// By name
|
// By name
|
||||||
const auto it = nameToIndex.find(argp->name());
|
const auto it = nameToIndex.find(argp->name());
|
||||||
if (it == nameToIndex.end()) {
|
if (it == nameToIndex.end()) {
|
||||||
pinp->v3error("No such argument " << argp->prettyNameQ() << " in function call to "
|
if (makeChanges) {
|
||||||
<< nodep->taskp()->prettyTypeName());
|
pinp->v3error("No such argument " << argp->prettyNameQ()
|
||||||
// We'll just delete it; seems less error prone than making a false argument
|
<< " in function call to "
|
||||||
VL_DO_DANGLING(pinp->unlinkFrBack()->deleteTree(), pinp);
|
<< 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 {
|
} else {
|
||||||
if (tconnects[it->second].second) {
|
if (tconnects[it->second].second && makeChanges) {
|
||||||
pinp->v3error("Duplicate argument " << argp->prettyNameQ()
|
pinp->v3error("Duplicate argument " << argp->prettyNameQ()
|
||||||
<< " in function call to "
|
<< " in function call to "
|
||||||
<< nodep->taskp()->prettyTypeName());
|
<< nodep->taskp()->prettyTypeName());
|
||||||
}
|
}
|
||||||
argp->name(""); // Can forget name as will add back in pin order
|
|
||||||
tconnects[it->second].second = argp;
|
tconnects[it->second].second = argp;
|
||||||
reorganize = true;
|
reorganize = true;
|
||||||
}
|
}
|
||||||
@@ -1649,8 +1653,8 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp,
|
|||||||
if (sformatp) {
|
if (sformatp) {
|
||||||
tconnects.emplace_back(sformatp, static_cast<AstArg*>(nullptr));
|
tconnects.emplace_back(sformatp, static_cast<AstArg*>(nullptr));
|
||||||
tconnects[ppinnum].second = argp;
|
tconnects[ppinnum].second = argp;
|
||||||
tpinnum++;
|
++tpinnum;
|
||||||
} else {
|
} else if (makeChanges) {
|
||||||
pinp->v3error("Too many arguments in function call to "
|
pinp->v3error("Too many arguments in function call to "
|
||||||
<< nodep->taskp()->prettyTypeName());
|
<< nodep->taskp()->prettyTypeName());
|
||||||
// We'll just delete it; seems less error prone than making a false argument
|
// 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;
|
tconnects[ppinnum].second = argp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ppinnum++;
|
++ppinnum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!makeChanges) return tconnects;
|
||||||
|
|
||||||
// Connect missing ones
|
// Connect missing ones
|
||||||
|
std::set<const AstVar*> argWrap; // Which ports are defaulted, forcing arg wrapper creation
|
||||||
for (int i = 0; i < tpinnum; ++i) {
|
for (int i = 0; i < tpinnum; ++i) {
|
||||||
AstVar* const portp = tconnects[i].first;
|
AstVar* const portp = tconnects[i].first;
|
||||||
if (!tconnects[i].second || !tconnects[i].second->exprp()) {
|
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) {
|
if (reorganize) {
|
||||||
// To simplify downstream, put argument list back into pure pinnumber ordering
|
// To simplify downstream, put argument list back into pure pinnumber ordering
|
||||||
while (nodep->pinsp()) {
|
while (nodep->pinsp()) {
|
||||||
@@ -1839,10 +1851,13 @@ AstNodeFTask* V3Task::taskConnectWrapNew(AstNodeFTask* taskp, const string& newn
|
|||||||
newPortp->funcLocal(true);
|
newPortp->funcLocal(true);
|
||||||
newTaskp->addStmtsp(newPortp);
|
newTaskp->addStmtsp(newPortp);
|
||||||
// Runtime-assign it to the default
|
// Runtime-assign it to the default
|
||||||
AstAssign* const newAssignp = new AstAssign{
|
if (!VN_IS(valuep, EmptyQueue)) {
|
||||||
valuep->fileline(), new AstVarRef{valuep->fileline(), newPortp, VAccess::WRITE},
|
AstAssign* const newAssignp
|
||||||
valuep->cloneTree(true)};
|
= new AstAssign{valuep->fileline(),
|
||||||
newTaskp->addStmtsp(newAssignp);
|
new AstVarRef{valuep->fileline(), newPortp, VAccess::WRITE},
|
||||||
|
valuep->cloneTree(true)};
|
||||||
|
newTaskp->addStmtsp(newAssignp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
oldNewVars.emplace(portp, newPortp);
|
oldNewVars.emplace(portp, newPortp);
|
||||||
const VAccess pinAccess = portp->isWritable() ? VAccess::WRITE : VAccess::READ;
|
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
Reference in New Issue
Block a user