mirror of
https://github.com/verilator/verilator.git
synced 2026-10-03 16:44:11 +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?
|
||||
|
||||
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_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
|
||||
project(Verilator
|
||||
VERSION 5.024
|
||||
VERSION 5.026
|
||||
HOMEPAGE_URL https://verilator.org
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
@@ -8,6 +8,73 @@ The changes in each Verilator version are described below. The
|
||||
contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
|
||||
Verilator 5.026 2024-06-15
|
||||
==========================
|
||||
|
||||
**Major:**
|
||||
|
||||
* Support constrained randomization with external solvers (#4947). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Support `$psprintf` system function (#4314) (#5169). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Support 2D dynamic array initialization (#4700) (#5122). [Valentin Atepalikhin]
|
||||
* Support __en/__out signals on top level inout ports (#4812) (#4856). [Paul Wright]
|
||||
* Support empty queue as dynarray default value (#5055). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Support vpiInertialDelay (#5087). [Todd Strader]
|
||||
* Support NBAs to arrays inside loops (#5092). [Geza Lore]
|
||||
* Support parsing and otherwise ignoring inline constraints (#5126). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Support `inout` clocking items (#5160). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Support StructSel in unpacked array assignments (#5176). [Geza Lore]
|
||||
* Add error on zero width select (#5028).
|
||||
* Add CITATION.cff (#5057) (#5058). [Gijs Burghoorn]
|
||||
* Add VPI eval needed tracking (#5065). [Todd Strader]
|
||||
* Add `--localize-max-size` option and optimization (#5072).
|
||||
* Add parameterless assert control system tasks (#5010). [Bartłomiej Chmiel]
|
||||
* Add traceCapable indication to model header (#5053). [Vito Gamberini]
|
||||
* Add increasing of stack size when possible (#5071) (#5104). [Yinan Xu]
|
||||
* Add assertion on reusing VerilatedContext (#5167).
|
||||
* Improve DFG regularization in cyclic graphs (#5142). [Geza Lore]
|
||||
* Improve VerilatedVpiPutHolder storage requirements (#5144). [Kaleb Barrett]
|
||||
* Fix coroutines without awaits to have a co_return (#4208) (#5175). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Fix missing flex include path variable (#4970) (#4971). [Christopher Taylor]
|
||||
* Fix missing parameters with comma to be errors (#4979) (#5012). [Paul Swirhun]
|
||||
* Fix 'experimental/coroutine' file not found on MacOS (#5030) (#5031) (#5151). [Paul Bowen-Huggett]
|
||||
* Fix bound queue printing (#5032). [Aleksander Kiryk, Antmicro Ltd.]
|
||||
* Fix consecutive zero-delays (#5038). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Fix attempted to destroy locked thread pool error (#5040). [Bartłomiej Chmiel, Antmicro Ltd.]
|
||||
* Fix `$system` with string argument (#5042).
|
||||
* Fix width extension on delays (#5043).
|
||||
* Fix `$typename` on array.min and others (#5049). [Gökçe Aydos]
|
||||
* Fix `make $(info)` which cannot be silenced (#5059). [Gökçe Aydos]
|
||||
* Fix CMake builds to export VERILATOR_ROOT (#5063). [Michael Bikovitsky]
|
||||
* Fix false ASSIGNIN on functions with explicit port map (#5069).
|
||||
* Fix 4-state value support for $readmem (#5070) (#5078). [Ethan Sifferman]
|
||||
* Fix DFG assertion with SystemC (#5076). [Geza Lore]
|
||||
* Fix `$typename` string to be more standard (#5082) (#5083). [Andrew Nolte]
|
||||
* Fix missed optimization in V3Delayed (#5089). [Geza Lore]
|
||||
* Fix macro expansion in strings per 1800-2023 (#5094). [Geza Lore]
|
||||
* Fix width extension of unpacked array select (#5095). [Varun Koyyalagunta]
|
||||
* Fix MacOS missing <type_traits> header (#5096) (#5097). [Vito Gamberini]
|
||||
* Fix assertion failure in V3Gate (#5101). [Yutetsu TAKATSUKASA]
|
||||
* Fix aliases for forced port signals (#5105). [Geza Lore]
|
||||
* Fix tracing interface functions (#5108). [Alex Solomatnikov]
|
||||
* Fix method calls parsing in constraints (#5110). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Fix vpiInertialDelay for memories (#5113). [Todd Strader]
|
||||
* Fix hierarchical compilation with nested -F (#5114) (#5124). [Alex Solomatnikov]
|
||||
* Fix references to ports in forks (#5123). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Fix output C++ type error on change detect of I/O arrays (#5125) (#5131). [Pawel Jewstafjew]
|
||||
* Fix x-valued parameters with `--x-assign unique` (#5129). [Ethan Sifferman]
|
||||
* Fix overflow of string on VPI reads (#5145) (#5146). [Kaleb Barrett]
|
||||
* Fix VerilatedVpiPutHolder class (#5156). [Kaleb Barrett]
|
||||
* Fix extending out-of-range select (#5159) (#5164). [Geza Lore]
|
||||
* Fix radix in width warnings (#5166). [Geza Lore]
|
||||
* Fix SystemC BITS_PER_DIGIT in VL_ASSIGN_SBW (#5170). [Bartłomiej Chmiel, Antmicro Ltd.]
|
||||
* Fix non-constant replication in concats (#5171). [Arkadiusz Kozdra, Antmicro Ltd.]
|
||||
* Fix table optimization when applied on real data type (#5172) (#5173). [Arthur Rosa]
|
||||
* Fix DPI import of null C-string (#5179).
|
||||
|
||||
|
||||
Verilator 5.024 2024-04-05
|
||||
==========================
|
||||
|
||||
@@ -180,6 +247,7 @@ Verilator 5.020 2024-01-01
|
||||
* Fix dynamic NBA conditions (#4773). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Fix `V3Fork` stage to run only if `--timing` is set (#4778). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Fix max multiply width and add runtime assertions if too small. (#4781)
|
||||
* Fix select value too wide (#5148) (#5153). [Dercury]
|
||||
|
||||
|
||||
Verilator 5.018 2023-10-30
|
||||
|
||||
@@ -91,6 +91,7 @@ datarootdir = @datarootdir@
|
||||
CFG_WITH_CCWARN = @CFG_WITH_CCWARN@
|
||||
CFG_WITH_DEFENV = @CFG_WITH_DEFENV@
|
||||
CFG_WITH_LONGTESTS = @CFG_WITH_LONGTESTS@
|
||||
CFG_WITH_SOLVER = @CFG_WITH_SOLVER@
|
||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||
|
||||
#### End of system configuration section. ####
|
||||
|
||||
+15
-14
@@ -1,21 +1,23 @@
|
||||
.. Github doesn't render images unless absolute URL
|
||||
.. Do not know of a conditional tag, "only: github" nor "github display" works
|
||||
|
||||
.. image:: https://img.shields.io/badge/Website-Verilator.org-181717.svg
|
||||
|badge1| |badge2| |badge3| |badge4| |badge5| |badge6| |badge7| |badge8|
|
||||
|
||||
.. |badge1| image:: https://img.shields.io/badge/Website-Verilator.org-181717.svg
|
||||
:target: https://verilator.org
|
||||
.. image:: https://img.shields.io/badge/License-LGPL%20v3-blue.svg
|
||||
.. |badge2| image:: https://img.shields.io/badge/License-LGPL%20v3-blue.svg
|
||||
:target: https://www.gnu.org/licenses/lgpl-3.0
|
||||
.. image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg
|
||||
.. |badge3| image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg
|
||||
:target: https://opensource.org/licenses/Artistic-2.0
|
||||
.. image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages
|
||||
.. |badge4| image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages
|
||||
:target: https://repology.org/project/verilator/versions
|
||||
.. image:: https://img.shields.io/docker/pulls/verilator/verilator
|
||||
.. |badge5| image:: https://img.shields.io/docker/pulls/verilator/verilator
|
||||
:target: https://hub.docker.com/r/verilator/verilator
|
||||
.. image:: https://api.codacy.com/project/badge/Grade/fa78caa433c84a4ab9049c43e9debc6f
|
||||
.. |badge6| image:: https://api.codacy.com/project/badge/Grade/fa78caa433c84a4ab9049c43e9debc6f
|
||||
:target: https://www.codacy.com/gh/verilator/verilator
|
||||
.. image:: https://codecov.io/gh/verilator/verilator/branch/master/graph/badge.svg
|
||||
.. |badge7| image:: https://codecov.io/gh/verilator/verilator/branch/master/graph/badge.svg
|
||||
:target: https://codecov.io/gh/verilator/verilator
|
||||
.. image:: https://github.com/verilator/verilator/workflows/build/badge.svg
|
||||
.. |badge8| image:: https://github.com/verilator/verilator/workflows/build/badge.svg
|
||||
:target: https://github.com/verilator/verilator/actions?query=workflow%3Abuild
|
||||
|
||||
|
||||
@@ -85,12 +87,11 @@ thread is about 100 times faster than interpreted Verilog simulators such
|
||||
as `Icarus Verilog`_. Another 2-10x speedup might be gained from
|
||||
multithreading (yielding 200-1000x total over interpreted simulators).
|
||||
|
||||
Verilator has typically similar or better performance versus
|
||||
closed-source Verilog simulators (e.g., Carbon Design Systems Carbonator,
|
||||
Modelsim/Questa, Cadence Incisive/NC-Verilog, Synopsys VCS, VTOC, and
|
||||
Pragmatic CVer/CVC). But, Verilator is open-sourced, so you can spend on
|
||||
computes rather than licenses. Thus, Verilator gives you the best
|
||||
simulation cycles/dollar.
|
||||
Verilator has typically similar or better performance versus closed-source
|
||||
Verilog simulators (e.g., Aldec Riviera-Pro, Cadence Incisive/NC-Verilog,
|
||||
Mentor ModelSim/Questa, Synopsys VCS, VTOC, and Pragmatic CVer/CVC). But,
|
||||
Verilator is open-sourced, so you can spend on computes rather than
|
||||
licenses. Thus, Verilator gives you the best simulation cycles/dollar.
|
||||
|
||||
|
||||
Installation & Documentation
|
||||
|
||||
@@ -387,6 +387,7 @@ detailed descriptions of these arguments.
|
||||
--lib-create <name> Create a DPI library
|
||||
+libext+<ext>+[ext]... Extensions for finding modules
|
||||
--lint-only Lint, but do not make output
|
||||
--localize-max-size <value> Tune localize optimization variable size
|
||||
--make <build-tool> Generate scripts for specified build tool
|
||||
-MAKEFLAGS <flags> Arguments to pass to make during --build
|
||||
--main Generate C++ main() file
|
||||
@@ -406,6 +407,7 @@ detailed descriptions of these arguments.
|
||||
--output-split-ctrace <statements> Split tracing functions
|
||||
-P Disable line numbers and blanks with -E
|
||||
--pins-bv <bits> Specify types for top-level ports
|
||||
--pins-inout-enables Specify that __en and __out signals be created for inouts
|
||||
--pins-sc-biguint Specify types for top-level ports
|
||||
--pins-sc-uint Specify types for top-level ports
|
||||
--pins-uint8 Specify types for top-level ports
|
||||
|
||||
@@ -154,9 +154,9 @@ verilator_coverage - Verilator coverage analyzer
|
||||
|
||||
verilator_coverage --annotate <obj>
|
||||
|
||||
verilator_coverage -write merged.dat -read <datafiles>...
|
||||
verilator_coverage -write merged.dat <datafiles>...
|
||||
|
||||
verilator_coverage -write-info merged.info -read <datafiles>...
|
||||
verilator_coverage -write-info merged.info <datafiles>...
|
||||
|
||||
Verilator_coverage processes Verilated model-generated coverage reports.
|
||||
|
||||
|
||||
+4
-4
@@ -92,8 +92,8 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
||||
sudo apt-get update ||
|
||||
sudo apt-get update
|
||||
# libfl-dev needed for internal coverage's test runs
|
||||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq ||
|
||||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq
|
||||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq z3 ||
|
||||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq z3
|
||||
# Required for test_regress/t/t_dist_attributes.pl
|
||||
if [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
|
||||
sudo apt-get install python3-clang mold ||
|
||||
@@ -106,10 +106,10 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
||||
elif [ "$CI_OS_NAME" = "osx" ]; then
|
||||
brew update
|
||||
# brew cask install gtkwave # fst2vcd hangs at launch, so don't bother
|
||||
brew install ccache perl jq
|
||||
brew install ccache perl jq z3
|
||||
elif [ "$CI_OS_NAME" = "freebsd" ]; then
|
||||
# fst2vcd fails with "Could not open '<input file>', exiting."
|
||||
sudo pkg install -y ccache gmake perl5 python3 jq
|
||||
sudo pkg install -y ccache gmake perl5 python3 jq z3
|
||||
else
|
||||
fatal "Unknown os: '$CI_OS_NAME'"
|
||||
fi
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
# DESCRIPTION: Verilator: CI Windows Power Shell - Compile Verilator
|
||||
#
|
||||
# Copyright 2024 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
################################################################################
|
||||
|
||||
Set-PSDebug -Trace 1
|
||||
|
||||
if (-Not (Test-Path $PWD/../.ccache/win_bison.exe)) {
|
||||
git clone --depth 1 https://github.com/lexxmark/winflexbison
|
||||
cd winflexbison
|
||||
@@ -8,6 +20,7 @@ if (-Not (Test-Path $PWD/../.ccache/win_bison.exe)) {
|
||||
cmake --install . --prefix $PWD/../../../.ccache
|
||||
cd ../..
|
||||
}
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. --install-prefix $PWD/../install
|
||||
|
||||
+17
-1
@@ -1,3 +1,16 @@
|
||||
# DESCRIPTION: Verilator: CI Windows Power Shell - Verilate a test
|
||||
#
|
||||
# Copyright 2024 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
################################################################################
|
||||
|
||||
|
||||
Set-PSDebug -Trace 1
|
||||
|
||||
cd install
|
||||
$Env:VERILATOR_ROOT=$PWD
|
||||
cd examples/cmake_tracing_c
|
||||
@@ -5,6 +18,9 @@ mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
cmake --build . --config Release -j 3
|
||||
Release/example.exe
|
||||
|
||||
# TODO put this back in, see issue# 5163
|
||||
# Release/example.exe
|
||||
|
||||
cd ..
|
||||
Remove-Item -path build -recurse
|
||||
|
||||
@@ -43,6 +43,7 @@ RUN apt-get update \
|
||||
perl \
|
||||
python3 \
|
||||
wget \
|
||||
z3 \
|
||||
zlib1g \
|
||||
zlib1g-dev \
|
||||
&& apt-get clean \
|
||||
|
||||
+24
-2
@@ -5,12 +5,12 @@
|
||||
# General Public License Version 3 or the Perl Artistic License Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
# When releasing, also update header of Changes file, and CmakeLists.txt,
|
||||
# When releasing, also update header of Changes file, and CMakeLists.txt,
|
||||
# and commit using "devel release" or "Version bump" message
|
||||
# Then 'make maintainer-dist'
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[5.024 2024-04-05],
|
||||
AC_INIT([Verilator],[5.026 2024-06-15],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
|
||||
@@ -134,6 +134,28 @@ AC_ARG_ENABLE([longtests],
|
||||
AC_SUBST(CFG_WITH_LONGTESTS)
|
||||
AC_MSG_RESULT($CFG_WITH_LONGTESTS)
|
||||
|
||||
AC_CHECK_PROG(HAVE_Z3,z3,yes)
|
||||
AC_CHECK_PROG(HAVE_CVC5,cvc5,yes)
|
||||
AC_CHECK_PROG(HAVE_CVC4,cvc4,yes)
|
||||
|
||||
# Special Substitutions - CFG_WITH_SOLVER
|
||||
AC_MSG_CHECKING(for SMT solver)
|
||||
AC_ARG_WITH([solver],
|
||||
[AS_HELP_STRING([--with-solver='z3 --in'],
|
||||
[set default SMT solver for constrained randomization])],
|
||||
[CFG_WITH_SOLVER="${withval}"],
|
||||
[CFG_WITH_SOLVER=no
|
||||
if test "x$HAVE_Z3" = "xyes"; then
|
||||
CFG_WITH_SOLVER="z3 --in"
|
||||
elif test "x$HAVE_CVC5" = "xyes"; then
|
||||
CFG_WITH_SOLVER="cvc5 --incremental"
|
||||
elif test "x$HAVE_CVC4" = "xyes"; then
|
||||
CFG_WITH_SOLVER="cvc4 --lang=smt2 --incremental"
|
||||
fi]
|
||||
)
|
||||
AC_SUBST(CFG_WITH_SOLVER)
|
||||
AC_MSG_RESULT($CFG_WITH_SOLVER)
|
||||
|
||||
# Compiler flags (ensure they are not empty to avoid configure defaults)
|
||||
CFLAGS="$CFLAGS "
|
||||
CPPFLAGS="$CPPFLAGS "
|
||||
|
||||
@@ -9,20 +9,24 @@ Adrien Le Masle
|
||||
Ahmed El-Mahmoudy
|
||||
Aleksander Kiryk
|
||||
Alex Chadwick
|
||||
Alex Solomatnikov
|
||||
Àlex Torregrosa
|
||||
Aliaksei Chapyzhenka
|
||||
Ameya Vikram Singh
|
||||
Andrea Calabrese
|
||||
Andreas Kuster
|
||||
Andrei Kostovski
|
||||
Andrew Miloradovsky
|
||||
Andrew Nolte
|
||||
Anthony Donlon
|
||||
Arkadiusz Kozdra
|
||||
Arthur Rosa
|
||||
Aylon Chaim Porat
|
||||
Bartłomiej Chmiel
|
||||
Cameron Kirk
|
||||
Chih-Mao Chen
|
||||
Chris Randall
|
||||
Christopher Taylor
|
||||
Chuxuan Wang
|
||||
Chykon
|
||||
Conor McCullough
|
||||
@@ -33,6 +37,7 @@ David Ledger
|
||||
David Metz
|
||||
David Stanford
|
||||
David Turner
|
||||
Dercury
|
||||
Don Williamson
|
||||
Drew Ranck
|
||||
Drew Taussig
|
||||
@@ -51,6 +56,7 @@ G-A. Kamendje
|
||||
Garrett Smith
|
||||
Geza Lore
|
||||
Gianfranco Costamagna
|
||||
Gijs Burghoorn
|
||||
Glen Gibb
|
||||
Gökçe Aydos
|
||||
Graham Rushton
|
||||
@@ -127,6 +133,7 @@ Marshal Qiao
|
||||
Martin Schmidt
|
||||
Martin Stadler
|
||||
Matthew Ballance
|
||||
Michael Bikovitsky
|
||||
Michael Killough
|
||||
Michaël Lefebvre
|
||||
Michal Czyz
|
||||
@@ -141,8 +148,10 @@ Nathan Myers
|
||||
Nolan Poe
|
||||
Oleh Maksymenko
|
||||
Patrick Stewart
|
||||
Paul Bowen-Huggett
|
||||
Paul Swirhun
|
||||
Paul Wright
|
||||
Pawel Jewstafjew
|
||||
Pawel Sagan
|
||||
Pengcheng Xu
|
||||
Peter Debacker
|
||||
@@ -185,6 +194,7 @@ Tudor Timi
|
||||
Tymoteusz Blazejczyk
|
||||
Udi Finkelstein
|
||||
Unai Martinez-Corral
|
||||
Valentin Atepalikhin
|
||||
Varun Koyyalagunta
|
||||
Vassilis Papaefstathiou
|
||||
Veripool API Bot
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.. comment: generated by t_lint_multidriven_bad
|
||||
.. code-block::
|
||||
|
||||
%Warning-MULTIDRIVEN: example.v:1:22 Signal has multiple driving blocks with different clocking: 'out2'
|
||||
%Warning-MULTIDRIVEN: example.v:1:22 Signal has multiple driving blocks with different clocking: 't.mem'
|
||||
example.v:1:7 ... Location of first driving block
|
||||
example.v:1:7 ... Location of other driving block
|
||||
|
||||
@@ -407,6 +407,16 @@ only a couple of instructions.
|
||||
For signal callbacks to work the main loop of the program must call
|
||||
:code:`VerilatedVpi::callValueCbs()`.
|
||||
|
||||
Verilator also tracks when the model state has been modified via the VPI with
|
||||
an :code:`evalNeeded` flag. This flag can be checked with :code:`VerilatedVpi::evalNeeded()`
|
||||
and it can be cleared with :code:`VerilatedVpi::clearEvalNeeded()`. Used together
|
||||
it is possible to skip :code:`eval()` calls if no model state has been changed
|
||||
since the last :code:`eval()`.
|
||||
|
||||
Any data written via :code:`vpi_put_value` with :code:`vpiInertialDelay` will
|
||||
be deferred for later. These delayed values can be flushed to the model with
|
||||
:code:`VerilatedVpi::doInertialPuts()`.
|
||||
|
||||
|
||||
.. _VPI Example:
|
||||
|
||||
|
||||
@@ -118,6 +118,12 @@ associated programs.
|
||||
|
||||
See :ref:`Installation` for more details.
|
||||
|
||||
.. option:: VERILATOR_SOLVER
|
||||
|
||||
If set, the command to run as a constrained randomization backend, such
|
||||
as :command:`cvc4 --lang=smt2 --incremental`. If not specified, it will use
|
||||
the one supplied or found during configure, or :command:`z3 --in` if empty.
|
||||
|
||||
.. option:: VERILATOR_VALGRIND
|
||||
|
||||
If set, the command to run when using the :vlopt:`--valgrind` option, such as
|
||||
|
||||
@@ -817,6 +817,11 @@ Summary:
|
||||
If the design is not to be completely Verilated, see also the
|
||||
:vlopt:`--bbox-sys` and :vlopt:`--bbox-unsup` options.
|
||||
|
||||
.. option:: --localize-max-size <value>
|
||||
|
||||
Rarely needed. Set the maximum variable size in bytes for it to be
|
||||
subject to localizing-to-stack optimization. Defaults to 1024.
|
||||
|
||||
.. option:: --make <build-tool>
|
||||
|
||||
Generates a script for the specified build tool.
|
||||
@@ -985,6 +990,13 @@ Summary:
|
||||
:option:`/*verilator&32;sc_bv*/` metacomment to select specific ports to
|
||||
be sc_bv.
|
||||
|
||||
.. option:: --pins-inout-enables
|
||||
|
||||
Specifies that the __en and __out outputs will always be created for
|
||||
inouts in the top-level module. The __en variable has a one in a bit
|
||||
position to indicate the corresponding bit of the __out variable has
|
||||
a value being driven from within the Verilated model.
|
||||
|
||||
.. option:: --pins-sc-uint
|
||||
|
||||
Specifies SystemC inputs/outputs greater than 2 bits wide should use
|
||||
@@ -1732,9 +1744,9 @@ Summary:
|
||||
|
||||
.. note::
|
||||
|
||||
This option applies only to values explicitly written as X
|
||||
in modules (not classes) in the Verilog source code. Initial values
|
||||
of clocks are set to 0 unless `--x-initial-edge` is
|
||||
This option applies only to values explicitly written as X in modules
|
||||
(not classes, nor parameters) in the Verilog source code. Initial
|
||||
values of clocks are set to 0 unless `--x-initial-edge` is
|
||||
specified. Initial values of all other state holding variables are
|
||||
controlled with `--x-initial`.
|
||||
|
||||
|
||||
@@ -37,9 +37,9 @@ verilator_coverage Example Usage
|
||||
|
||||
verilator_coverage --annotate <obj>
|
||||
|
||||
verilator_coverage -write merged.dat -read <datafiles>...
|
||||
verilator_coverage -write merged.dat <datafiles>...
|
||||
|
||||
verilator_coverage -write-info merged.info -read <datafiles>...
|
||||
verilator_coverage -write-info merged.info <datafiles>...
|
||||
|
||||
|
||||
verilator_coverage Arguments
|
||||
|
||||
+14
-2
@@ -152,6 +152,17 @@ To make use of Verilator FST tracing you will want `GTKwave
|
||||
required at Verilator build time.
|
||||
|
||||
|
||||
Install Z3
|
||||
^^^^^^^^^^
|
||||
|
||||
In order to use constrained randomization the `Z3 Theorem Prover
|
||||
<https://github.com/z3prover/z3#readme>`__ must be installed, however this is
|
||||
not required at Verilator build time. There are other compatible SMT solvers,
|
||||
like CVC5/CVC4, but they are not guaranteed to work. Since different solvers are
|
||||
faster for different scenarios, the solver to use at run-time can be specified
|
||||
by the environment variable :option:`VERILATOR_SOLVER`.
|
||||
|
||||
|
||||
.. _Obtain Sources:
|
||||
|
||||
Obtain Sources
|
||||
@@ -193,8 +204,9 @@ Eventual Installation Options
|
||||
Before configuring the build, you must decide how you're going to
|
||||
eventually install Verilator onto your system. Verilator will be compiling
|
||||
the current value of the environment variables :option:`VERILATOR_ROOT`,
|
||||
:option:`SYSTEMC_INCLUDE`, and :option:`SYSTEMC_LIBDIR` as defaults into
|
||||
the executable, so they must be correct before configuring.
|
||||
:option:`VERILATOR_SOLVER`, :option:`SYSTEMC_INCLUDE`, and
|
||||
:option:`SYSTEMC_LIBDIR` as defaults into the executable, so they must be
|
||||
correct before configuring.
|
||||
|
||||
These are the installation options:
|
||||
|
||||
|
||||
+30
-9
@@ -210,22 +210,35 @@ List Of Warnings
|
||||
|
||||
.. option:: BLKLOOPINIT
|
||||
|
||||
.. TODO better example
|
||||
|
||||
This indicates that the initialization of an array needs to use
|
||||
non-delayed assignments. This is done in the interest of speed; if
|
||||
delayed assignments were used, the simulator would have to copy large
|
||||
arrays every cycle. (In smaller loops, loop unrolling allows the
|
||||
delayed assignment to work, though it's a bit slower than a non-delayed
|
||||
assignment.) Here's an example
|
||||
Indicates certain constructs where non-blocking assignments to unpacked
|
||||
arrays (memories) are not supported inside loops. These typically appear in
|
||||
initialization/reset code:
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
always @(posedge clk)
|
||||
if (~reset_l)
|
||||
for (i=0; i<`ARRAY_SIZE; i++)
|
||||
array[i] = 0; // Non-delayed for verilator
|
||||
array[i] <= 0; // Non-blocking assignment inside loop
|
||||
else
|
||||
array[address] <= data;
|
||||
|
||||
While this is supported in typical synthesizeable code (including the
|
||||
example above), some complicated cases are not supported. Namely:
|
||||
|
||||
1. If the above loop is inside a suspendable process or fork statement.
|
||||
|
||||
2. If the variable is also the target of a '<=' non-blocking assignment
|
||||
in a suspendable process or fork statement (in addition to a synthesizable
|
||||
loop).
|
||||
|
||||
3. If the element type of the array is a compound type.
|
||||
|
||||
4. In versions before 5.026, any delayed assignment to an array.
|
||||
|
||||
It might slightly improve run-time performance if you change the
|
||||
non-blocking assignment inside the loop into a blocking assignment
|
||||
(that is: use '=' instead of '<='), if possible.
|
||||
|
||||
This message is only seen on large or complicated loops because
|
||||
Verilator generally unrolls small loops. You may want to try increasing
|
||||
@@ -1204,6 +1217,14 @@ List Of Warnings
|
||||
:vlopt:`--no-timing` option.
|
||||
|
||||
|
||||
.. option:: NONSTD
|
||||
|
||||
Warns when a non-standard language feature is used that has a standard
|
||||
equivalent, which might behave differently in corner cases. For example
|
||||
:code:`$psprintf` system function is replaced by its standard equivalent
|
||||
:code:`$sformatf`.
|
||||
|
||||
|
||||
.. option:: NULLPORT
|
||||
|
||||
Warns that a null port was detected in the module definition port
|
||||
|
||||
@@ -1031,6 +1031,91 @@ the evaluation process records a bitmask of variables that might have
|
||||
changed; if clear, checking those signals for changes may be skipped.
|
||||
|
||||
|
||||
Constrained randomization
|
||||
-------------------------
|
||||
|
||||
Because general constrained randomization is a co-NP-hard problem, not all
|
||||
cases are implemented in Verilator, and an external specialized SMT solver is
|
||||
used for any non-obvious ones.
|
||||
|
||||
The ``randomize()`` method spawns an SMT solver in a sub-process. Then the
|
||||
solver gets a setup query, then the definition of variables, then all the
|
||||
constraints (SMT assertions) about the variables. Since the solver has no
|
||||
information about the class' PRNG state, if the problem is satisfiable,
|
||||
the solution space is further constrained by adding extra random constraints,
|
||||
and querying the values satisfying the problem statement.
|
||||
The constraint is currently constructed as fixing a simple xor of randomly
|
||||
chosen bits of the variables being randomized.
|
||||
|
||||
The runtime classes used for handling the randomization are defined in
|
||||
``verilated_random.h`` and ``verilated_random.cpp``.
|
||||
|
||||
|
||||
``VlSubprocess``
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Subprocess handle, responsible for keeping track of the resources like child
|
||||
PID, read and write file descriptors, and presenting them as a C++ iostream.
|
||||
|
||||
|
||||
``VlRandomizer``
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Randomizer class, responsible for keeping track of variables and constraints,
|
||||
and communicating with the solver subprocess.
|
||||
|
||||
The solver gets the constraints in `SMT-LIB2
|
||||
<https://smtlib.cs.uiowa.edu/>`__ textual format in the following syntax:
|
||||
|
||||
::
|
||||
|
||||
(set-info :smt-lib-version 2.0)
|
||||
(set-option :produce-models true)
|
||||
(set-logic QF_BV)
|
||||
|
||||
(declare-fun v () (_ BitVec 16))
|
||||
(declare-fun w () (_ BitVec 64))
|
||||
(declare-fun x () (_ BitVec 48))
|
||||
(declare-fun z () (_ BitVec 24))
|
||||
(declare-fun t () (_ BitVec 23))
|
||||
(assert (or (= v #x0003) (= v #x0008)))
|
||||
(assert (= w #x0000000000000009))
|
||||
(assert (or (or (= x #x000000000001) (= x #x000000000002)) (or (= x #x000000000004) (= x #x000000000009))))
|
||||
(assert (bvult ((_ zero_extend 8) z) #x00000015))
|
||||
(assert (bvugt ((_ zero_extend 8) z) #x0000000d))
|
||||
|
||||
(check-sat)
|
||||
|
||||
The solver responds with either ``sat`` or ``unsat``. Then the initial solution
|
||||
is queried with:
|
||||
|
||||
::
|
||||
|
||||
(get-value (v w x z t ))
|
||||
|
||||
The solver then responds with e.g.:
|
||||
|
||||
::
|
||||
|
||||
((v #x0008)
|
||||
(w #x0000000000000005)
|
||||
(x #x000000000002)
|
||||
(z #x000010)
|
||||
(t #b00000000000000000000000))
|
||||
|
||||
And then a follow-up query (or a series thereof) is asked, and the solver gets
|
||||
reset, so that it can be reused by subsequent randomization attempts:
|
||||
|
||||
::
|
||||
|
||||
(assert (= (bvxor (bvxor <...> (bvxor ((_ extract 21 21) z) ((_ extract 39 39) x)) ((_ extract 5 5) w)) <...> ((_ extract 10 10) w)) #b0))
|
||||
(check-sat)
|
||||
(get-value)
|
||||
...
|
||||
(reset)
|
||||
|
||||
|
||||
|
||||
Coding Conventions
|
||||
==================
|
||||
|
||||
@@ -2125,6 +2210,10 @@ VERILATOR_ROOT
|
||||
Standard path to Verilator distribution root; see primary Verilator
|
||||
documentation.
|
||||
|
||||
VERILATOR_SOLVER
|
||||
SMT solver command for constrained randomization; see primary Verilator
|
||||
documentation.
|
||||
|
||||
VERILATOR_TESTS_SITE
|
||||
Used with ``--site``, a colon-separated list of directories with tests to
|
||||
be added to testlist.
|
||||
|
||||
@@ -40,6 +40,7 @@ Brej
|
||||
Briquet
|
||||
Brownlee
|
||||
Buckenmaier
|
||||
Burghoorn
|
||||
Bybell
|
||||
CLang
|
||||
CMake
|
||||
@@ -131,6 +132,7 @@ Gernot
|
||||
Gerst
|
||||
Gielda
|
||||
Gigerl
|
||||
Gijs
|
||||
Gileadi
|
||||
Giri
|
||||
Gisselquist
|
||||
@@ -164,6 +166,7 @@ Homebrew
|
||||
Hornung
|
||||
Hossell
|
||||
Hsu
|
||||
Huggett
|
||||
Hutt
|
||||
Hyperthreading
|
||||
Ibrahim
|
||||
@@ -842,6 +845,7 @@ parallelized
|
||||
param
|
||||
parameterization
|
||||
parameterized
|
||||
parameterless
|
||||
params
|
||||
parens
|
||||
pawel
|
||||
@@ -885,6 +889,7 @@ profcfunc
|
||||
profiler
|
||||
prototyptes
|
||||
ps
|
||||
psprintf
|
||||
pthread
|
||||
ptr
|
||||
pulldown
|
||||
@@ -968,6 +973,7 @@ submakes
|
||||
submodule
|
||||
submodules
|
||||
substring
|
||||
suspendable
|
||||
sv
|
||||
svBitVal
|
||||
svBitVecVal
|
||||
@@ -978,6 +984,7 @@ svLogicVal
|
||||
svdpi
|
||||
svgGetTimeUnit
|
||||
swrite
|
||||
synthesizeable
|
||||
sys
|
||||
systemc
|
||||
tenghtt
|
||||
@@ -996,6 +1003,7 @@ toolchain
|
||||
topcell
|
||||
toplevel
|
||||
toupper
|
||||
traceCapable
|
||||
traceEverOn
|
||||
tran
|
||||
treei
|
||||
@@ -1060,6 +1068,7 @@ vluint
|
||||
vpi
|
||||
vpiConstType
|
||||
vpiDefName
|
||||
vpiInertialDelay
|
||||
vpiInstance
|
||||
vpiLeftRange
|
||||
vpiModule
|
||||
|
||||
@@ -47,12 +47,12 @@ int main(int argc, char** argv) {
|
||||
|
||||
// Simulate until $finish
|
||||
while (!contextp->gotFinish()) {
|
||||
contextp->timeInc(1);
|
||||
top->clk = ~top->clk & 0x1;
|
||||
top->eval();
|
||||
#if VM_TRACE
|
||||
if (tfp) tfp->dump(contextp->time());
|
||||
#endif
|
||||
contextp->timeInc(1);
|
||||
}
|
||||
|
||||
// Final model cleanup
|
||||
|
||||
@@ -347,8 +347,9 @@ return(NULL);
|
||||
|
||||
static void *fstMmap2(size_t __len, int __fd, fst_off_t __off)
|
||||
{
|
||||
DWORD64 len64 = __len; /* Must be 64-bit for shift below */
|
||||
HANDLE handle = CreateFileMapping((HANDLE)_get_osfhandle(__fd), NULL,
|
||||
PAGE_READWRITE, (DWORD)(__len >> 32),
|
||||
PAGE_READWRITE, (DWORD)(len64 >> 32),
|
||||
(DWORD)__len, NULL);
|
||||
if (!handle) { return NULL; }
|
||||
|
||||
|
||||
+87
-52
@@ -82,6 +82,12 @@
|
||||
|
||||
#include "verilated_trace.h"
|
||||
|
||||
#ifdef VM_SOLVER_DEFAULT
|
||||
#define VL_SOLVER_DEFAULT VM_SOLVER_DEFAULT
|
||||
#else
|
||||
#define VL_SOLVER_DEFAULT "z3 --in"
|
||||
#endif
|
||||
|
||||
// Max characters in static char string for VL_VALUE_STRING
|
||||
constexpr unsigned VL_VALUE_STRING_MAX_WIDTH = 8192;
|
||||
|
||||
@@ -1171,8 +1177,8 @@ static char* _vl_vsss_read_bin(FILE* fp, int& floc, const WDataInP fromp, const
|
||||
static void _vl_vsss_setbit(WDataOutP iowp, int obits, int lsb, int nbits, IData ld) VL_MT_SAFE {
|
||||
for (; nbits && lsb < obits; nbits--, lsb++, ld >>= 1) VL_ASSIGNBIT_WI(lsb, iowp, ld & 1);
|
||||
}
|
||||
static void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp,
|
||||
size_t posstart, size_t posend) VL_MT_SAFE {
|
||||
void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp, size_t posstart,
|
||||
size_t posend) VL_MT_SAFE {
|
||||
// Read in base "2^^baseLog2" digits from strp[posstart..posend-1] into owp of size obits.
|
||||
VL_ZERO_W(obits, owp);
|
||||
int lsb = 0;
|
||||
@@ -1749,7 +1755,10 @@ IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE {
|
||||
IData VL_SYSTEM_IW(int lhswords, const WDataInP lhsp) VL_MT_SAFE {
|
||||
char filenamez[VL_VALUE_STRING_MAX_CHARS + 1];
|
||||
_vl_vint_to_string(lhswords * VL_EDATASIZE, filenamez, lhsp);
|
||||
const int code = std::system(filenamez); // Yes, std::system() is threadsafe
|
||||
return VL_SYSTEM_IN(filenamez);
|
||||
}
|
||||
IData VL_SYSTEM_IN(const std::string& lhs) VL_MT_SAFE {
|
||||
const int code = std::system(lhs.c_str()); // Yes, std::system() is threadsafe
|
||||
return code >> 8; // Want exit status
|
||||
}
|
||||
|
||||
@@ -2037,22 +2046,26 @@ bool VlReadMem::get(QData& addrr, std::string& valuer) {
|
||||
if (VL_UNLIKELY(!m_fp)) return false;
|
||||
valuer = "";
|
||||
// Prep for reading
|
||||
bool indata = false;
|
||||
bool ignore_to_eol = false;
|
||||
bool ignore_to_cmt = false;
|
||||
bool reading_addr = false;
|
||||
int lastc = ' ';
|
||||
bool inData = false;
|
||||
bool ignoreToEol = false;
|
||||
bool ignoreToComment = false;
|
||||
bool readingAddress = false;
|
||||
int lastCh = ' ';
|
||||
// Read the data
|
||||
// We process a character at a time, as then we don't need to deal
|
||||
// with changing buffer sizes dynamically, etc.
|
||||
while (true) {
|
||||
int c = std::fgetc(m_fp);
|
||||
if (VL_UNLIKELY(c == EOF)) break;
|
||||
const bool chIs4StateBin
|
||||
= c == '0' || c == '1' || c == 'x' || c == 'X' || c == 'z' || c == 'Z';
|
||||
const bool chIs2StateHex = std::isxdigit(c);
|
||||
const bool chIs4StateHex = std::isxdigit(c) || chIs4StateBin;
|
||||
// printf("%d: Got '%c' Addr%lx IN%d IgE%d IgC%d\n",
|
||||
// m_linenum, c, m_addr, indata, ignore_to_eol, ignore_to_cmt);
|
||||
// m_linenum, c, m_addr, inData, ignoreToEol, ignoreToComment);
|
||||
// See if previous data value has completed, and if so return
|
||||
if (c == '_') continue; // Ignore _ e.g. inside a number
|
||||
if (indata && !std::isxdigit(c) && c != 'x' && c != 'X') {
|
||||
if (inData && !chIs4StateHex) {
|
||||
// printf("Got data @%lx = %s\n", m_addr, valuer.c_str());
|
||||
ungetc(c, m_fp);
|
||||
addrr = m_addr;
|
||||
@@ -2062,50 +2075,46 @@ bool VlReadMem::get(QData& addrr, std::string& valuer) {
|
||||
// Parse line
|
||||
if (c == '\n') {
|
||||
++m_linenum;
|
||||
ignore_to_eol = false;
|
||||
reading_addr = false;
|
||||
ignoreToEol = false;
|
||||
readingAddress = false;
|
||||
} else if (c == '\t' || c == ' ' || c == '\r' || c == '\f') {
|
||||
reading_addr = false;
|
||||
readingAddress = false;
|
||||
}
|
||||
// Skip // comments and detect /* comments
|
||||
else if (ignore_to_cmt && lastc == '*' && c == '/') {
|
||||
ignore_to_cmt = false;
|
||||
reading_addr = false;
|
||||
} else if (!ignore_to_eol && !ignore_to_cmt) {
|
||||
if (lastc == '/' && c == '*') {
|
||||
ignore_to_cmt = true;
|
||||
} else if (lastc == '/' && c == '/') {
|
||||
ignore_to_eol = true;
|
||||
else if (ignoreToComment && lastCh == '*' && c == '/') {
|
||||
ignoreToComment = false;
|
||||
readingAddress = false;
|
||||
} else if (!ignoreToEol && !ignoreToComment) {
|
||||
if (lastCh == '/' && c == '*') {
|
||||
ignoreToComment = true;
|
||||
} else if (lastCh == '/' && c == '/') {
|
||||
ignoreToEol = true;
|
||||
} else if (c == '/') { // Part of /* or //
|
||||
} else if (c == '#') {
|
||||
ignore_to_eol = true;
|
||||
ignoreToEol = true;
|
||||
} else if (c == '@') {
|
||||
reading_addr = true;
|
||||
readingAddress = true;
|
||||
m_anyAddr = true;
|
||||
m_addr = 0;
|
||||
}
|
||||
// Check for hex or binary digits as file format requests
|
||||
else if (std::isxdigit(c) || (!reading_addr && (c == 'x' || c == 'X'))) {
|
||||
} else if (readingAddress && chIs2StateHex) {
|
||||
c = std::tolower(c);
|
||||
const int value
|
||||
= (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c - 'a' + 10)) : (c - '0'));
|
||||
if (reading_addr) {
|
||||
// Decode @ addresses
|
||||
m_addr = (m_addr << 4) + value;
|
||||
} else {
|
||||
indata = true;
|
||||
valuer += static_cast<char>(c);
|
||||
// printf(" Value width=%d @%x = %c\n", width, m_addr, c);
|
||||
if (VL_UNLIKELY(value > 1 && !m_hex)) {
|
||||
VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
|
||||
"$readmemb (binary) file contains hex characters");
|
||||
}
|
||||
const int addressValue = (c >= 'a') ? (c - 'a' + 10) : (c - '0');
|
||||
m_addr = (m_addr << 4) + addressValue;
|
||||
} else if (readingAddress && chIs4StateHex) {
|
||||
VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
|
||||
"$readmem address contains 4-state characters");
|
||||
} else if (chIs4StateHex) {
|
||||
inData = true;
|
||||
valuer += static_cast<char>(c);
|
||||
if (VL_UNLIKELY(!m_hex && !chIs4StateBin)) {
|
||||
VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
|
||||
"$readmemb (binary) file contains hex characters");
|
||||
}
|
||||
} else {
|
||||
VL_FATAL_MT(m_filename.c_str(), m_linenum, "", "$readmem file syntax error");
|
||||
}
|
||||
}
|
||||
lastc = c;
|
||||
lastCh = c;
|
||||
}
|
||||
|
||||
if (VL_UNLIKELY(m_end != ~0ULL && m_addr <= m_end && !m_anyAddr)) {
|
||||
@@ -2114,7 +2123,7 @@ bool VlReadMem::get(QData& addrr, std::string& valuer) {
|
||||
}
|
||||
|
||||
addrr = m_addr;
|
||||
return indata; // EOF
|
||||
return inData; // EOF
|
||||
}
|
||||
void VlReadMem::setData(void* valuep, const std::string& rhs) {
|
||||
const QData shift = m_hex ? 4ULL : 1ULL;
|
||||
@@ -2122,8 +2131,9 @@ void VlReadMem::setData(void* valuep, const std::string& rhs) {
|
||||
// Shift value in
|
||||
for (const auto& i : rhs) {
|
||||
const char c = std::tolower(i);
|
||||
const int value
|
||||
= (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c - 'a' + 10)) : (c - '0'));
|
||||
const int value = (c == 'x' || c == 'z') ? VL_RAND_RESET_I(m_hex ? 4 : 1)
|
||||
: (c >= 'a') ? (c - 'a' + 10)
|
||||
: (c - '0');
|
||||
if (m_bits <= 8) {
|
||||
CData* const datap = reinterpret_cast<CData*>(valuep);
|
||||
if (!innum) *datap = 0;
|
||||
@@ -2457,6 +2467,7 @@ VerilatedContext::VerilatedContext()
|
||||
m_ns.m_coverageFilename = "coverage.dat";
|
||||
m_ns.m_profExecFilename = "profile_exec.dat";
|
||||
m_ns.m_profVltFilename = "profile.vlt";
|
||||
m_ns.m_solverProgram = VlOs::getenvStr("VERILATOR_SOLVER", VL_SOLVER_DEFAULT);
|
||||
m_fdps.resize(31);
|
||||
std::fill(m_fdps.begin(), m_fdps.end(), static_cast<FILE*>(nullptr));
|
||||
m_fdFreeMct.resize(30);
|
||||
@@ -2567,6 +2578,14 @@ std::string VerilatedContext::profVltFilename() const VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
return m_ns.m_profVltFilename;
|
||||
}
|
||||
void VerilatedContext::solverProgram(const std::string& flag) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_ns.m_solverProgram = flag;
|
||||
}
|
||||
std::string VerilatedContext::solverProgram() const VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
return m_ns.m_solverProgram;
|
||||
}
|
||||
void VerilatedContext::quiet(bool flag) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_quiet = flag;
|
||||
@@ -2645,8 +2664,16 @@ void VerilatedContext::addModel(VerilatedModel* modelp) {
|
||||
m_ns.m_cpuTimeStart.start();
|
||||
m_ns.m_wallTimeStart.start();
|
||||
}
|
||||
threadPoolp(); // Ensure thread pool is created, so m_threads cannot change any more
|
||||
|
||||
// We look for time passing, as opposed to post-eval(), as embedded
|
||||
// models might get added inside initial blocks.
|
||||
if (VL_UNLIKELY(time()))
|
||||
VL_FATAL_MT(
|
||||
"", 0, "",
|
||||
"Adding model when time is non-zero. ... Suggest check time(), or for restarting"
|
||||
" model use a new VerilatedContext");
|
||||
|
||||
threadPoolp(); // Ensure thread pool is created, so m_threads cannot change any more
|
||||
m_threadsInModels += modelp->threads();
|
||||
if (VL_UNLIKELY(modelp->threads() > m_threads)) {
|
||||
std::ostringstream msg;
|
||||
@@ -2882,7 +2909,7 @@ void VerilatedContext::statsPrintSummary() VL_MT_UNSAFE {
|
||||
VL_PRINTF("- Verilator: %s at %s; walltime %0.3f s; speed %s/s\n", endwhy.c_str(),
|
||||
simtime.c_str(), walltime, simtimePerf.c_str());
|
||||
const double modelMB = VlOs::memUsageBytes() / 1024.0 / 1024.0;
|
||||
VL_PRINTF("- Verilator: cpu %0.3f s on %d threads; alloced %0.0f MB\n", cputime,
|
||||
VL_PRINTF("- Verilator: cpu %0.3f s on %u threads; alloced %0.0f MB\n", cputime,
|
||||
threadsInModels(), modelMB);
|
||||
}
|
||||
|
||||
@@ -2933,7 +2960,6 @@ void VerilatedContext::trace(VerilatedTraceBaseC* tfp, int levels, int options)
|
||||
VL_FATAL_MT("", 0, "",
|
||||
"Testbench C call to 'VerilatedContext::trace()' must not be called"
|
||||
" after 'VerilatedTrace*::open()'\n");
|
||||
return;
|
||||
}
|
||||
{
|
||||
// Legacy usage may call {modela}->trace(...) then {modelb}->trace(...)
|
||||
@@ -3126,12 +3152,21 @@ void Verilated::stackCheck(QData needSize) VL_MT_UNSAFE {
|
||||
if (haveSize == RLIM_INFINITY) haveSize = 0;
|
||||
}
|
||||
// VL_PRINTF_MT("-Info: stackCheck(%" PRIu64 ") have %" PRIu64 "\n", needSize, haveSize);
|
||||
// Check for 1.5x need, but suggest 2x so small model increase won't cause warning
|
||||
// if the user follows the suggestions
|
||||
if (VL_UNLIKELY(haveSize && needSize && haveSize < (needSize + needSize / 2))) {
|
||||
VL_PRINTF_MT("%%Warning: System has stack size %" PRIu64 " kb"
|
||||
" which may be too small; suggest 'ulimit -s %" PRIu64 "' or larger\n",
|
||||
haveSize / 1024, (needSize * 2) / 1024);
|
||||
// Check and request for 1.5x need. This is automated so the user doesn't need to do anything.
|
||||
QData requestSize = needSize + needSize / 2;
|
||||
if (VL_UNLIKELY(haveSize && needSize && haveSize < requestSize)) {
|
||||
// Try to increase the stack limit to the requested size
|
||||
rlim.rlim_cur = requestSize;
|
||||
if (
|
||||
#ifdef _VL_TEST_RLIMIT_FAIL
|
||||
true ||
|
||||
#endif
|
||||
setrlimit(RLIMIT_STACK, &rlim)) {
|
||||
VL_PRINTF_MT("%%Warning: System has stack size %" PRIu64 " kb"
|
||||
" which may be too small; failed to request more"
|
||||
" using 'ulimit -s %" PRIu64 "'\n",
|
||||
haveSize / 1024, requestSize);
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)needSize; // Unused argument
|
||||
|
||||
@@ -396,6 +396,7 @@ protected:
|
||||
std::string m_coverageFilename; // +coverage+file filename
|
||||
std::string m_profExecFilename; // +prof+exec+file filename
|
||||
std::string m_profVltFilename; // +prof+vlt filename
|
||||
std::string m_solverProgram; // SMT solver program
|
||||
VlOs::DeltaCpuTime m_cpuTimeStart{false}; // CPU time, starts when create first model
|
||||
VlOs::DeltaWallTime m_wallTimeStart{false}; // Wall time, starts when create first model
|
||||
std::vector<traceBaseModelCb_t> m_traceBaseModelCbs; // Callbacks to traceRegisterModel
|
||||
@@ -624,6 +625,10 @@ public:
|
||||
void profVltFilename(const std::string& flag) VL_MT_SAFE;
|
||||
std::string profVltFilename() const VL_MT_SAFE;
|
||||
|
||||
// Internal: SMT solver program
|
||||
void solverProgram(const std::string& flag) VL_MT_SAFE;
|
||||
std::string solverProgram() const VL_MT_SAFE;
|
||||
|
||||
// Internal: Find scope
|
||||
const VerilatedScope* scopeFind(const char* namep) const VL_MT_SAFE;
|
||||
const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE;
|
||||
|
||||
@@ -225,7 +225,6 @@ endif
|
||||
# 1. Extract object files from .a
|
||||
# 2. Create a new archive from extracted .o and given .o
|
||||
%.a: | %.verilator_deplist.tmp
|
||||
$(info Archive $(AR) -rcs $@ $^)
|
||||
$(foreach L, $(filter-out %.a,$^), $(shell echo $L >>[email protected]_deplist.tmp))
|
||||
@if test $(words $(filter %.a,$^)) -eq 0; then \
|
||||
$(RM) -f $@; \
|
||||
|
||||
@@ -115,6 +115,9 @@ extern void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp,
|
||||
extern WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP const lwp, WDataInP const rwp,
|
||||
bool is_modulus) VL_MT_SAFE;
|
||||
|
||||
extern void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp,
|
||||
size_t posstart, size_t posend) VL_MT_SAFE;
|
||||
|
||||
extern IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE;
|
||||
|
||||
extern void VL_FFLUSH_I(IData fdi) VL_MT_SAFE;
|
||||
@@ -152,6 +155,7 @@ extern std::string VL_STACKTRACE_N() VL_MT_SAFE;
|
||||
extern IData VL_SYSTEM_IW(int lhswords, WDataInP const lhsp) VL_MT_SAFE;
|
||||
extern IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE;
|
||||
inline IData VL_SYSTEM_II(IData lhs) VL_MT_SAFE { return VL_SYSTEM_IQ(lhs); }
|
||||
extern IData VL_SYSTEM_IN(const std::string& lhs) VL_MT_SAFE;
|
||||
|
||||
extern IData VL_TESTPLUSARGS_I(const std::string& format) VL_MT_SAFE;
|
||||
extern const char* vl_mc_scan_plusargs(const char* prefixp) VL_MT_SAFE; // PLIish
|
||||
@@ -215,6 +219,10 @@ static inline QData VL_CVT_Q_D(double lhs) VL_PURE {
|
||||
return u.q;
|
||||
}
|
||||
// clang-format on
|
||||
// Return string from DPI char*
|
||||
static inline std::string VL_CVT_N_CSTR(const char* lhsp) VL_PURE {
|
||||
return lhsp ? std::string{lhsp} : ""s;
|
||||
}
|
||||
|
||||
// Return double from lhs (numeric) unsigned
|
||||
double VL_ITOR_D_W(int lbits, WDataInP const lwp) VL_PURE;
|
||||
@@ -238,7 +246,7 @@ static inline double VL_ISTOR_D_Q(int lbits, QData lhs) VL_MT_SAFE {
|
||||
VL_SET_WQ(lwp, lhs);
|
||||
return VL_ISTOR_D_W(lbits, lwp);
|
||||
}
|
||||
// Return QData from double (numeric)
|
||||
// Return IData truncated from double (numeric)
|
||||
static inline IData VL_RTOI_I_D(double lhs) VL_PURE { return static_cast<int32_t>(VL_TRUNC(lhs)); }
|
||||
|
||||
// Sign extend such that if MSB set, we get ffff_ffff, else 0s
|
||||
@@ -504,19 +512,18 @@ static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE {
|
||||
{ (svar).write(rd); }
|
||||
#define VL_ASSIGN_SBQ(obits, svar, rd) \
|
||||
{ (svar).write(rd); }
|
||||
#define VL_SC_BITS_PER_DIGIT 30 // This comes from sc_nbdefs.h BITS_PER_DIGIT
|
||||
#define VL_ASSIGN_SBW(obits, svar, rwp) \
|
||||
{ \
|
||||
sc_dt::sc_biguint<(obits)> _butemp; \
|
||||
int32_t lsb = 0; \
|
||||
uint32_t* chunkp = _butemp.get_raw(); \
|
||||
while (lsb + VL_SC_BITS_PER_DIGIT < (obits)) { \
|
||||
while (lsb + BITS_PER_DIGIT < (obits)) { \
|
||||
static_assert(std::is_same<IData, EData>::value, "IData and EData mismatch"); \
|
||||
const uint32_t data = VL_SEL_IWII(lsb + VL_SC_BITS_PER_DIGIT + 1, (rwp).data(), lsb, \
|
||||
VL_SC_BITS_PER_DIGIT); \
|
||||
*chunkp = data & VL_MASK_E(VL_SC_BITS_PER_DIGIT); \
|
||||
const uint32_t data \
|
||||
= VL_SEL_IWII(lsb + BITS_PER_DIGIT + 1, (rwp).data(), lsb, BITS_PER_DIGIT); \
|
||||
*chunkp = data & VL_MASK_E(BITS_PER_DIGIT); \
|
||||
++chunkp; \
|
||||
lsb += VL_SC_BITS_PER_DIGIT; \
|
||||
lsb += BITS_PER_DIGIT; \
|
||||
} \
|
||||
if (lsb < (obits)) { \
|
||||
const uint32_t msb_data = VL_SEL_IWII((obits) + 1, (rwp).data(), lsb, (obits)-lsb); \
|
||||
|
||||
@@ -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()) {
|
||||
for (auto&& handle : m_zeroDelayed) handle.resume();
|
||||
m_zeroDelayed.clear();
|
||||
// First, we need to move the coroutines out of the queue, as a resumed coroutine can
|
||||
// suspend on #0 again, adding itself to the queue, which can result in reallocating the
|
||||
// queue mid-iteration.
|
||||
// We swap with the m_zeroDlyResumed field to keep the allocated buffer.
|
||||
m_zeroDlyResumed.swap(m_zeroDelayed);
|
||||
for (auto&& handle : m_zeroDlyResumed) handle.resume();
|
||||
m_zeroDlyResumed.clear();
|
||||
resumed = true;
|
||||
// We are now in the Active region, so any coroutines added to m_zeroDelayed in the
|
||||
// meantime will have to wait until the next Inactive region.
|
||||
}
|
||||
|
||||
if (!resumed) {
|
||||
|
||||
+18
-11
@@ -32,14 +32,18 @@
|
||||
// clang-format off
|
||||
// Some preprocessor magic to support both Clang and GCC coroutines with both libc++ and libstdc++
|
||||
#if defined _LIBCPP_VERSION // libc++
|
||||
# if __clang_major__ > 13 // Clang > 13 warns that coroutine types in std::experimental are deprecated
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-experimental-coroutine"
|
||||
# if defined(__has_include) && !__has_include(<coroutine>) && __has_include(<experimental/coroutine>)
|
||||
# if __clang_major__ > 13 // Clang > 13 warns that coroutine types in std::experimental are deprecated
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-experimental-coroutine"
|
||||
# endif
|
||||
# include <experimental/coroutine>
|
||||
namespace std {
|
||||
using namespace experimental; // Bring std::experimental into the std namespace
|
||||
}
|
||||
# else
|
||||
# include <coroutine>
|
||||
# endif
|
||||
# include <experimental/coroutine>
|
||||
namespace std {
|
||||
using namespace experimental; // Bring std::experimental into the std namespace
|
||||
}
|
||||
#else
|
||||
# if defined __clang__ && defined __GLIBCXX__ && !defined __cpp_impl_coroutine
|
||||
# define __cpp_impl_coroutine 1 // Clang doesn't define this, but it's needed for libstdc++
|
||||
@@ -165,6 +169,9 @@ class VlDelayScheduler final {
|
||||
VerilatedContext& m_context;
|
||||
VlDelayedCoroutineQueue m_queue; // Coroutines to be restored at a certain simulation time
|
||||
std::vector<VlCoroutineHandle> m_zeroDelayed; // Coroutines waiting for #0
|
||||
std::vector<VlCoroutineHandle> m_zeroDlyResumed; // Coroutines that waited for #0 and are
|
||||
// to be resumed. Kept as a field to avoid
|
||||
// reallocation.
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -193,9 +200,9 @@ public:
|
||||
VlProcessRef process; // Data of the suspended process, null if not needed
|
||||
VlDelayedCoroutineQueue& queue;
|
||||
std::vector<VlCoroutineHandle>& queueZeroDelay;
|
||||
uint64_t delay;
|
||||
VlDelayPhase phase;
|
||||
VlFileLineDebug fileline;
|
||||
const uint64_t delay;
|
||||
const VlDelayPhase phase;
|
||||
const VlFileLineDebug fileline;
|
||||
|
||||
bool await_ready() const { return false; } // Always suspend
|
||||
void await_suspend(std::coroutine_handle<> coro) {
|
||||
@@ -208,7 +215,7 @@ public:
|
||||
void await_resume() const {}
|
||||
};
|
||||
|
||||
VlDelayPhase phase = (delay == 0) ? VlDelayPhase::INACTIVE : VlDelayPhase::ACTIVE;
|
||||
const VlDelayPhase phase = (delay == 0) ? VlDelayPhase::INACTIVE : VlDelayPhase::ACTIVE;
|
||||
#ifdef VL_DEBUG
|
||||
if (phase == VlDelayPhase::INACTIVE) {
|
||||
VL_WARN_MT(filename, lineno, VL_UNKNOWN,
|
||||
|
||||
@@ -30,8 +30,9 @@
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <type_traits>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <deque>
|
||||
@@ -294,7 +295,7 @@ private:
|
||||
uint64_t m_timeLastDump = 0; // Last time we did a dump
|
||||
bool m_didSomeDump = false; // Did at least one dump (i.e.: m_timeLastDump is valid)
|
||||
VerilatedContext* m_contextp = nullptr; // The context used by the traced models
|
||||
std::unordered_set<const VerilatedModel*> m_models; // The collection of models being traced
|
||||
std::set<const VerilatedModel*> m_models; // The collection of models being traced
|
||||
|
||||
void addCallbackRecord(std::vector<CallbackRecord>& cbVec, CallbackRecord&& cbRec)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
|
||||
@@ -633,12 +633,13 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addModel(VerilatedModel* modelp)
|
||||
|
||||
// Validate
|
||||
if (!newModel) { // LCOV_EXCL_START
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"The same model has already been added to this trace file");
|
||||
VL_FATAL_MT(
|
||||
__FILE__, __LINE__, "",
|
||||
"The same model has already been added to this trace file or VerilatedContext");
|
||||
}
|
||||
if (VL_UNCOVERABLE(m_contextp && contextp != m_contextp)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"A trace file instance can only handle models from the same context");
|
||||
"A trace file instance can only handle models from the same VerilatedContext");
|
||||
}
|
||||
if (VL_UNCOVERABLE(m_didSomeDump)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
|
||||
+204
-5
@@ -36,7 +36,6 @@
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
//=========================================================================
|
||||
@@ -415,8 +414,20 @@ public:
|
||||
|
||||
static int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_PURE;
|
||||
|
||||
template <std::size_t T_Words>
|
||||
struct VlWide;
|
||||
|
||||
// Type trait to check if a type is VlWide
|
||||
template <typename>
|
||||
struct VlIsVlWide : public std::false_type {};
|
||||
|
||||
template <std::size_t T_Words>
|
||||
struct VlIsVlWide<VlWide<T_Words>> : public std::true_type {};
|
||||
|
||||
template <std::size_t T_Words>
|
||||
struct VlWide final {
|
||||
static constexpr size_t Words = T_Words;
|
||||
|
||||
// MEMBERS
|
||||
// This should be the only data member, otherwise generated static initializers need updating
|
||||
EData m_storage[T_Words]; // Contents of the packed array
|
||||
@@ -891,8 +902,8 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template <class T_Value>
|
||||
std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
|
||||
template <class T_Value, size_t T_MaxSize>
|
||||
std::string VL_TO_STRING(const VlQueue<T_Value, T_MaxSize>& obj) {
|
||||
return obj.to_string();
|
||||
}
|
||||
|
||||
@@ -1309,7 +1320,11 @@ public:
|
||||
// Similar to 'neq' above, *this = that used for change detection
|
||||
void assign(const VlUnpacked<T_Value, T_Depth>& that) { *this = that; }
|
||||
bool operator==(const VlUnpacked<T_Value, T_Depth>& that) const { return !neq(that); }
|
||||
bool operator!=(const VlUnpacked<T_Value, T_Depth>& that) { return neq(that); }
|
||||
bool operator!=(const VlUnpacked<T_Value, T_Depth>& that) const { return neq(that); }
|
||||
// interface to C style arrays (used in ports), see issue #5125
|
||||
bool neq(const T_Value that[T_Depth]) const { return neq(*this, that); }
|
||||
void assign(const T_Value that[T_Depth]) { std::copy_n(that, T_Depth, m_storage); }
|
||||
void operator=(const T_Value that[T_Depth]) { assign(that); }
|
||||
|
||||
// inside (set membership operator)
|
||||
bool inside(const T_Value& value) const {
|
||||
@@ -1386,7 +1401,7 @@ public:
|
||||
VlQueue<T_Key> unique_index(Func with_func) const {
|
||||
VlQueue<T_Key> out;
|
||||
IData index = 0;
|
||||
std::unordered_set<T_Value> saw;
|
||||
std::set<T_Value> saw;
|
||||
for (const auto& i : m_storage) {
|
||||
const auto i_mapped = with_func(index, i);
|
||||
auto it = saw.find(i_mapped);
|
||||
@@ -1499,6 +1514,15 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T_Val, std::size_t T_Dep>
|
||||
static bool neq(const VlUnpacked<T_Val, T_Dep>& a, const T_Val b[T_Dep]) {
|
||||
for (size_t i = 0; i < T_Dep; ++i) {
|
||||
// Recursive 'neq', in case T_Val is also a VlUnpacked<_, _>
|
||||
if (neq(a.m_storage[i], b[i])) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T_Other> //
|
||||
static bool neq(const T_Other& a, const T_Other& b) {
|
||||
// Base case (T_Other is not VlUnpacked<_, _>), fall back on !=
|
||||
@@ -1511,6 +1535,181 @@ std::string VL_TO_STRING(const VlUnpacked<T_Value, T_Depth>& obj) {
|
||||
return obj.to_string();
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Helper to apply the given indices to a target expression
|
||||
|
||||
template <size_t Curr, size_t Rank, typename T_Target>
|
||||
struct VlApplyIndices final {
|
||||
VL_ATTR_ALWINLINE
|
||||
static auto& apply(T_Target& target, const size_t* indicesp) {
|
||||
return VlApplyIndices<Curr + 1, Rank, decltype(target[indicesp[Curr]])>::apply(
|
||||
target[indicesp[Curr]], indicesp);
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t Rank, typename T_Target>
|
||||
struct VlApplyIndices<Rank, Rank, T_Target> final {
|
||||
VL_ATTR_ALWINLINE
|
||||
static T_Target& apply(T_Target& target, const size_t*) { return target; }
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// Commit queue for NBAs - currently only for unpacked arrays
|
||||
//
|
||||
// This data-structure is used to handle non-blocking assignments
|
||||
// that might execute a variable number of times in a single
|
||||
// evaluation. It has 2 operations:
|
||||
// - 'enqueue' will add an update to the queue
|
||||
// - 'commit' will apply all enqueued updates to the target variable,
|
||||
// in the order they were enqueued. This ensures the last NBA
|
||||
// takes effect as it is expected.
|
||||
// There are 2 specializations of this class below:
|
||||
// - A version when a partial element update is not required,
|
||||
// e.g, to handle:
|
||||
// logic [31:0] array[N];
|
||||
// for (int i = 0 ; i < N ; ++i) array[i] <= x;
|
||||
// Here 'enqueue' takes the RHS ('x'), and the array indices ('i')
|
||||
// as arguments.
|
||||
// - A different version when a partial element update is required,
|
||||
// e.g. for:
|
||||
// logic [31:0] array[N];
|
||||
// for (int i = 0 ; i < N ; ++i) array[i][3:1] <= y;
|
||||
// Here 'enqueue' takes one additional argument, which is a bitmask
|
||||
// derived from the bit selects (_[3:1]), which masks the bits that
|
||||
// need to be updated, and additionally the RHS is widened to a full
|
||||
// element size, with the bits inserted into the masked region.
|
||||
template <typename T_Target, // Type of the variable this commit queue updates
|
||||
bool Partial, // Whether partial element updates are necessary
|
||||
// The following we could figure out from 'T_Target using type traits, but passing
|
||||
// explicitly to avoid template expansion, as Verilator already knows them
|
||||
typename T_Element, // Non-array leaf element type of T_Target array
|
||||
std::size_t T_Rank // Rank of T_Target (i.e.: how many dimensions it has)
|
||||
>
|
||||
class VlNBACommitQueue;
|
||||
|
||||
// Specialization for whole element updates only
|
||||
template <typename T_Target, typename T_Element, std::size_t T_Rank>
|
||||
class VlNBACommitQueue<T_Target, /* Partial: */ false, T_Element, T_Rank> final {
|
||||
// TYPES
|
||||
struct Entry final {
|
||||
T_Element value;
|
||||
size_t indices[T_Rank];
|
||||
};
|
||||
|
||||
// STATE
|
||||
std::vector<Entry> m_pending; // Pending updates, in program order
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
VlNBACommitQueue() = default;
|
||||
VL_UNCOPYABLE(VlNBACommitQueue);
|
||||
|
||||
// METHODS
|
||||
template <typename... Args>
|
||||
void enqueue(const T_Element& value, Args... indices) {
|
||||
m_pending.emplace_back(Entry{value, {indices...}});
|
||||
}
|
||||
|
||||
// Note: T_Commit might be different from T_Target. Specifically, when the signal is a
|
||||
// top-level IO port, T_Commit will be a native C array, while T_Target, will be a VlUnpacked
|
||||
template <typename T_Commit>
|
||||
void commit(T_Commit& target) {
|
||||
if (m_pending.empty()) return;
|
||||
for (const Entry& entry : m_pending) {
|
||||
VlApplyIndices<0, T_Rank, T_Commit>::apply(target, entry.indices) = entry.value;
|
||||
}
|
||||
m_pending.clear();
|
||||
}
|
||||
};
|
||||
|
||||
// With partial element updates
|
||||
template <typename T_Target, typename T_Element, std::size_t T_Rank>
|
||||
class VlNBACommitQueue<T_Target, /* Partial: */ true, T_Element, T_Rank> final {
|
||||
// TYPES
|
||||
struct Entry final {
|
||||
T_Element value;
|
||||
T_Element mask;
|
||||
size_t indices[T_Rank];
|
||||
};
|
||||
|
||||
// STATE
|
||||
std::vector<Entry> m_pending; // Pending updates, in program order
|
||||
|
||||
// STATIC METHODS
|
||||
|
||||
// Binary & | ~ for elements to use for masking in partial updates. Sorry for the templates.
|
||||
template <typename T>
|
||||
VL_ATTR_ALWINLINE static typename std::enable_if<!VlIsVlWide<T>::value, T>::type
|
||||
bAnd(const T& a, const T& b) {
|
||||
return a & b;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
VL_ATTR_ALWINLINE static typename std::enable_if<VlIsVlWide<T>::value, T>::type
|
||||
bAnd(const T& a, const T& b) {
|
||||
T result;
|
||||
for (size_t i = 0; i < T::Words; ++i) {
|
||||
result.m_storage[i] = a.m_storage[i] & b.m_storage[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
VL_ATTR_ALWINLINE static typename std::enable_if<!VlIsVlWide<T>::value, T>::type
|
||||
bOr(const T& a, const T& b) {
|
||||
return a | b;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
VL_ATTR_ALWINLINE static typename std::enable_if<VlIsVlWide<T>::value, T>::type //
|
||||
bOr(const T& a, const T& b) {
|
||||
T result;
|
||||
for (size_t i = 0; i < T::Words; ++i) {
|
||||
result.m_storage[i] = a.m_storage[i] | b.m_storage[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
VL_ATTR_ALWINLINE static typename std::enable_if<!VlIsVlWide<T>::value, T>::type
|
||||
bNot(const T& a) {
|
||||
return ~a;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
VL_ATTR_ALWINLINE static typename std::enable_if<VlIsVlWide<T>::value, T>::type
|
||||
bNot(const T& a) {
|
||||
T result;
|
||||
for (size_t i = 0; i < T::Words; ++i) result.m_storage[i] = ~a.m_storage[i];
|
||||
return result;
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
VlNBACommitQueue() = default;
|
||||
VL_UNCOPYABLE(VlNBACommitQueue);
|
||||
|
||||
// METHODS
|
||||
template <typename... Args>
|
||||
void enqueue(const T_Element& value, const T_Element& mask, Args... indices) {
|
||||
m_pending.emplace_back(Entry{value, mask, {indices...}});
|
||||
}
|
||||
|
||||
// Note: T_Commit might be different from T_Target. Specifically, when the signal is a
|
||||
// top-level IO port, T_Commit will be a native C array, while T_Target, will be a VlUnpacked
|
||||
template <typename T_Commit>
|
||||
void commit(T_Commit& target) {
|
||||
if (m_pending.empty()) return;
|
||||
for (const Entry& entry : m_pending) { //
|
||||
auto& ref = VlApplyIndices<0, T_Rank, T_Commit>::apply(target, entry.indices);
|
||||
// Maybe inefficient, but it works for now ...
|
||||
const auto oldValue = ref;
|
||||
ref = bOr(bAnd(entry.value, entry.mask), bAnd(oldValue, bNot(entry.mask)));
|
||||
}
|
||||
m_pending.clear();
|
||||
}
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// Object that VlDeleter is capable of deleting
|
||||
|
||||
|
||||
+222
-81
@@ -31,11 +31,14 @@
|
||||
#include "verilated.h"
|
||||
#include "verilated_imp.h"
|
||||
|
||||
#include "vltstd/vpi_user.h"
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -630,6 +633,146 @@ public:
|
||||
void invalidate() { m_id = 0; }
|
||||
};
|
||||
|
||||
class VerilatedVpiPutHolder final {
|
||||
VerilatedVpioVar m_var;
|
||||
s_vpi_value m_value;
|
||||
union Storage {
|
||||
char init = 0; // to ensure trivial constructor
|
||||
std::string str;
|
||||
std::vector<s_vpi_vecval> vec;
|
||||
~Storage() noexcept {/* handled by VerilatedVpiPutHolder */};
|
||||
} m_storage{};
|
||||
|
||||
public:
|
||||
VerilatedVpiPutHolder(const VerilatedVpioVar* vop, p_vpi_value valuep)
|
||||
: m_var{vop} {
|
||||
m_value.format = valuep->format;
|
||||
switch (valuep->format) {
|
||||
case vpiBinStrVal: // FALLTHRU
|
||||
case vpiOctStrVal: // FALLTHRU
|
||||
case vpiDecStrVal: // FALLTHRU
|
||||
case vpiHexStrVal: // FALLTHRU
|
||||
case vpiStringVal: {
|
||||
new (&m_storage.str) std::string{valuep->value.str};
|
||||
m_value.value.str = const_cast<char*>(m_storage.str.c_str());
|
||||
break;
|
||||
}
|
||||
case vpiScalarVal: {
|
||||
m_value.value.scalar = valuep->value.scalar;
|
||||
break;
|
||||
}
|
||||
case vpiIntVal: {
|
||||
m_value.value.integer = valuep->value.integer;
|
||||
break;
|
||||
}
|
||||
case vpiRealVal: {
|
||||
m_value.value.real = valuep->value.real;
|
||||
break;
|
||||
}
|
||||
case vpiVectorVal: {
|
||||
size_t words = 0;
|
||||
switch (vop->varp()->vltype()) {
|
||||
case VLVT_UINT8:
|
||||
case VLVT_UINT16:
|
||||
case VLVT_UINT32: {
|
||||
words = 1;
|
||||
break;
|
||||
}
|
||||
case VLVT_UINT64: {
|
||||
words = 2;
|
||||
break;
|
||||
}
|
||||
case VLVT_WDATA: {
|
||||
words = VL_WORDS_I(vop->varp()->packed().elements());
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
new (&m_storage.vec)
|
||||
std::vector<s_vpi_vecval>{valuep->value.vector, &valuep->value.vector[words]};
|
||||
m_value.value.vector = m_storage.vec.data();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerilatedVpiPutHolder(VerilatedVpiPutHolder const& o)
|
||||
: m_var{o.m_var}
|
||||
, m_value{o.m_value} {
|
||||
switch (m_value.format) {
|
||||
case vpiBinStrVal: // FALLTHRU
|
||||
case vpiOctStrVal: // FALLTHRU
|
||||
case vpiDecStrVal: // FALLTHRU
|
||||
case vpiHexStrVal: // FALLTHRU
|
||||
case vpiStringVal: {
|
||||
new (&m_storage.str) std::string{o.m_storage.str};
|
||||
break;
|
||||
}
|
||||
case vpiVectorVal: {
|
||||
new (&m_storage.vec) std::vector<s_vpi_vecval>{o.m_storage.vec};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerilatedVpiPutHolder(VerilatedVpiPutHolder&& o) noexcept
|
||||
: m_var{std::move(o.m_var)}
|
||||
, m_value{std::move(o.m_value)} {
|
||||
switch (m_value.format) {
|
||||
case vpiBinStrVal: // FALLTHRU
|
||||
case vpiOctStrVal: // FALLTHRU
|
||||
case vpiDecStrVal: // FALLTHRU
|
||||
case vpiHexStrVal: // FALLTHRU
|
||||
case vpiStringVal: {
|
||||
new (&m_storage.str) std::string{std::move(o.m_storage.str)};
|
||||
break;
|
||||
}
|
||||
case vpiVectorVal: {
|
||||
new (&m_storage.vec) std::vector<s_vpi_vecval>{std::move(o.m_storage.vec)};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~VerilatedVpiPutHolder() noexcept {
|
||||
switch (m_value.format) {
|
||||
case vpiBinStrVal: // FALLTHRU
|
||||
case vpiOctStrVal: // FALLTHRU
|
||||
case vpiDecStrVal: // FALLTHRU
|
||||
case vpiHexStrVal: // FALLTHRU
|
||||
case vpiStringVal: m_storage.str.~basic_string(); break;
|
||||
case vpiVectorVal: m_storage.vec.~vector(); break;
|
||||
}
|
||||
}
|
||||
|
||||
VerilatedVpioVar* varp() { return &m_var; }
|
||||
p_vpi_value valuep() { return &m_value; }
|
||||
|
||||
static bool canInertialDelay(p_vpi_value valuep) {
|
||||
switch (valuep->format) {
|
||||
case vpiBinStrVal: // FALLTHRU
|
||||
case vpiOctStrVal: // FALLTHRU
|
||||
case vpiDecStrVal: // FALLTHRU
|
||||
case vpiHexStrVal: // FALLTHRU
|
||||
case vpiStringVal: {
|
||||
if (VL_UNLIKELY(!valuep->value.str)) return false;
|
||||
break;
|
||||
}
|
||||
case vpiScalarVal: // FALLTHRU
|
||||
case vpiIntVal: // FALLTHRU
|
||||
case vpiRealVal: break;
|
||||
case vpiVectorVal: {
|
||||
if (VL_UNLIKELY(!valuep->value.vector)) return false;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct VerilatedVpiTimedCbsCmp final {
|
||||
// Ordering sets keyed by time, then callback unique id
|
||||
bool operator()(const std::pair<QData, uint64_t>& a,
|
||||
@@ -652,9 +795,11 @@ class VerilatedVpiImp final {
|
||||
std::array<VpioCbList, CB_ENUM_MAX_VALUE> m_cbCurrentLists;
|
||||
VpioFutureCbs m_futureCbs; // Time based callbacks for future timestamps
|
||||
VpioFutureCbs m_nextCbs; // cbNextSimTime callbacks
|
||||
std::list<VerilatedVpiPutHolder> m_inertialPuts; // Pending vpi puts due to vpiInertialDelay
|
||||
VerilatedVpiError* m_errorInfop = nullptr; // Container for vpi error info
|
||||
VerilatedAssertOneThread m_assertOne; // Assert only called from single thread
|
||||
uint64_t m_nextCallbackId = 1; // Id to identify callback
|
||||
bool m_evalNeeded = false; // Model has had signals updated via vpi_put_value()
|
||||
|
||||
static VerilatedVpiImp& s() { // Singleton
|
||||
static VerilatedVpiImp s_s;
|
||||
@@ -776,7 +921,7 @@ public:
|
||||
assertOneCheck();
|
||||
VpioCbList& cbObjList = s().m_cbCurrentLists[cbValueChange];
|
||||
bool called = false;
|
||||
std::unordered_set<VerilatedVpioVar*> update; // set of objects to update after callbacks
|
||||
std::set<VerilatedVpioVar*> update; // set of objects to update after callbacks
|
||||
if (cbObjList.empty()) return called;
|
||||
const auto last = std::prev(cbObjList.end()); // prevent looping over newly added elements
|
||||
for (auto it = cbObjList.begin(); true;) {
|
||||
@@ -813,6 +958,17 @@ public:
|
||||
}
|
||||
static void dumpCbs() VL_MT_UNSAFE_ONE;
|
||||
static VerilatedVpiError* error_info() VL_MT_UNSAFE_ONE; // getter for vpi error info
|
||||
static void evalNeeded(bool evalNeeded) { s().m_evalNeeded = evalNeeded; }
|
||||
static bool evalNeeded() { return s().m_evalNeeded; }
|
||||
static void inertialDelay(const VerilatedVpioVar* vop, p_vpi_value valuep) {
|
||||
s().m_inertialPuts.emplace_back(vop, valuep);
|
||||
}
|
||||
static void doInertialPuts() {
|
||||
for (auto& it : s().m_inertialPuts) {
|
||||
vpi_put_value(it.varp()->castVpiHandle(), it.valuep(), nullptr, vpiNoDelay);
|
||||
}
|
||||
s().m_inertialPuts.clear();
|
||||
}
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
@@ -915,6 +1071,11 @@ PLI_INT32 VerilatedVpioReasonCb::dovpi_remove_cb() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void VerilatedVpi::clearEvalNeeded() VL_MT_UNSAFE_ONE { VerilatedVpiImp::evalNeeded(false); }
|
||||
bool VerilatedVpi::evalNeeded() VL_MT_UNSAFE_ONE { return VerilatedVpiImp::evalNeeded(); }
|
||||
|
||||
void VerilatedVpi::doInertialPuts() VL_MT_UNSAFE_ONE { VerilatedVpiImp::doInertialPuts(); }
|
||||
|
||||
//======================================================================
|
||||
// VerilatedVpiImp implementation
|
||||
|
||||
@@ -2194,13 +2355,28 @@ bool vl_check_format(const VerilatedVar* varp, const p_vpi_value valuep, const c
|
||||
return status;
|
||||
}
|
||||
|
||||
static void vl_strprintf(std::string& buffer, char const* fmt, ...) {
|
||||
va_list args, args_copy;
|
||||
va_start(args, fmt);
|
||||
buffer.clear();
|
||||
// Make copy of args since we may need to call VL_VSNPRINTF more than once
|
||||
va_copy(args_copy, args);
|
||||
// Try VL_VSNPRINTF in existing buffer
|
||||
const int result
|
||||
= VL_VSNPRINTF(const_cast<char*>(buffer.data()), buffer.capacity(), fmt, args_copy);
|
||||
va_end(args_copy);
|
||||
const int required = result + 1; // Returned size doesn't include NUL terminator
|
||||
// If there wasn't enough space, reallocate and try again
|
||||
if (buffer.capacity() < required) {
|
||||
buffer.reserve(required * 2);
|
||||
VL_VSNPRINTF(const_cast<char*>(buffer.data()), buffer.capacity(), fmt, args);
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
const char* fullname) {
|
||||
if (!vl_check_format(varp, valuep, fullname, true)) return;
|
||||
// Maximum required size is for binary string, one byte per bit plus null termination
|
||||
static thread_local char t_outStr[VL_VALUE_STRING_MAX_WORDS * VL_EDATASIZE + 1];
|
||||
// cppcheck-suppress variableScope
|
||||
static const thread_local int t_outStrSz = sizeof(t_outStr) - 1;
|
||||
// string data type is dynamic and may vary in size during simulation
|
||||
static thread_local std::string t_outDynamicStr;
|
||||
// We used to presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal
|
||||
@@ -2244,43 +2420,21 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
return;
|
||||
}
|
||||
} else if (valuep->format == vpiBinStrVal) {
|
||||
valuep->value.str = t_outStr;
|
||||
int bits = varp->packed().elements();
|
||||
t_outDynamicStr.resize(bits);
|
||||
const CData* datap = (reinterpret_cast<CData*>(varDatap));
|
||||
int i;
|
||||
if (bits > t_outStrSz) {
|
||||
// limit maximum size of output to size of buffer to prevent overrun.
|
||||
VL_VPI_WARNING_(
|
||||
__FILE__, __LINE__,
|
||||
"%s: Truncating string value of %s for %s"
|
||||
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
|
||||
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
|
||||
VL_VALUE_STRING_MAX_WORDS, bits);
|
||||
bits = t_outStrSz;
|
||||
}
|
||||
for (i = 0; i < bits; ++i) {
|
||||
for (size_t i = 0; i < bits; ++i) {
|
||||
const char val = (datap[i >> 3] >> (i & 7)) & 1;
|
||||
t_outStr[bits - i - 1] = val ? '1' : '0';
|
||||
t_outDynamicStr[bits - i - 1] = val ? '1' : '0';
|
||||
}
|
||||
t_outStr[i] = '\0';
|
||||
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
|
||||
return;
|
||||
} else if (valuep->format == vpiOctStrVal) {
|
||||
valuep->value.str = t_outStr;
|
||||
int chars = (varp->packed().elements() + 2) / 3;
|
||||
t_outDynamicStr.resize(chars);
|
||||
const int bytes = VL_BYTES_I(varp->packed().elements());
|
||||
const CData* datap = (reinterpret_cast<CData*>(varDatap));
|
||||
int i;
|
||||
if (chars > t_outStrSz) {
|
||||
// limit maximum size of output to size of buffer to prevent overrun.
|
||||
VL_VPI_WARNING_(
|
||||
__FILE__, __LINE__,
|
||||
"%s: Truncating string value of %s for %s"
|
||||
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
|
||||
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
|
||||
VL_VALUE_STRING_MAX_WORDS, chars);
|
||||
chars = t_outStrSz;
|
||||
}
|
||||
for (i = 0; i < chars; ++i) {
|
||||
for (size_t i = 0; i < chars; ++i) {
|
||||
const div_t idx = div(i * 3, 8);
|
||||
int val = datap[idx.quot];
|
||||
if ((idx.quot + 1) < bytes) {
|
||||
@@ -2299,46 +2453,31 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
val &= (1 << rem) - 1;
|
||||
}
|
||||
}
|
||||
t_outStr[chars - i - 1] = '0' + (val & 7);
|
||||
t_outDynamicStr[chars - i - 1] = '0' + (val & 7);
|
||||
}
|
||||
t_outStr[i] = '\0';
|
||||
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
|
||||
return;
|
||||
} else if (valuep->format == vpiDecStrVal) {
|
||||
valuep->value.str = t_outStr;
|
||||
// outStrSz does not include nullptr termination so add one
|
||||
if (varp->vltype() == VLVT_UINT8) {
|
||||
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%hhu",
|
||||
static_cast<unsigned char>(*(reinterpret_cast<CData*>(varDatap))));
|
||||
return;
|
||||
vl_strprintf(t_outDynamicStr, "%hhu",
|
||||
static_cast<unsigned char>(*(reinterpret_cast<CData*>(varDatap))));
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%hu",
|
||||
static_cast<unsigned short>(*(reinterpret_cast<SData*>(varDatap))));
|
||||
return;
|
||||
vl_strprintf(t_outDynamicStr, "%hu",
|
||||
static_cast<unsigned short>(*(reinterpret_cast<SData*>(varDatap))));
|
||||
} else if (varp->vltype() == VLVT_UINT32) {
|
||||
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%u",
|
||||
static_cast<unsigned int>(*(reinterpret_cast<IData*>(varDatap))));
|
||||
return;
|
||||
vl_strprintf(t_outDynamicStr, "%u",
|
||||
static_cast<unsigned int>(*(reinterpret_cast<IData*>(varDatap))));
|
||||
} else if (varp->vltype() == VLVT_UINT64) {
|
||||
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%llu",
|
||||
static_cast<unsigned long long>(*(reinterpret_cast<QData*>(varDatap))));
|
||||
return;
|
||||
vl_strprintf(t_outDynamicStr, "%llu",
|
||||
static_cast<unsigned long long>(*(reinterpret_cast<QData*>(varDatap))));
|
||||
}
|
||||
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
|
||||
return;
|
||||
} else if (valuep->format == vpiHexStrVal) {
|
||||
valuep->value.str = t_outStr;
|
||||
int chars = (varp->packed().elements() + 3) >> 2;
|
||||
t_outDynamicStr.resize(chars);
|
||||
const CData* datap = (reinterpret_cast<CData*>(varDatap));
|
||||
int i;
|
||||
if (chars > t_outStrSz) {
|
||||
// limit maximum size of output to size of buffer to prevent overrun.
|
||||
VL_VPI_WARNING_(
|
||||
__FILE__, __LINE__,
|
||||
"%s: Truncating string value of %s for %s"
|
||||
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
|
||||
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
|
||||
VL_VALUE_STRING_MAX_WORDS, chars);
|
||||
chars = t_outStrSz;
|
||||
}
|
||||
for (i = 0; i < chars; ++i) {
|
||||
for (size_t i = 0; i < chars; ++i) {
|
||||
char val = (datap[i >> 1] >> ((i & 1) << 2)) & 15;
|
||||
if (i == (chars - 1)) {
|
||||
// most significant char, mask off nonexistent bits when vector
|
||||
@@ -2349,9 +2488,9 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
val &= (1 << rem) - 1;
|
||||
}
|
||||
}
|
||||
t_outStr[chars - i - 1] = "0123456789abcdef"[static_cast<int>(val)];
|
||||
t_outDynamicStr[chars - i - 1] = "0123456789abcdef"[static_cast<int>(val)];
|
||||
}
|
||||
t_outStr[i] = '\0';
|
||||
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
|
||||
return;
|
||||
} else if (valuep->format == vpiStringVal) {
|
||||
if (varp->vltype() == VLVT_STRING) {
|
||||
@@ -2364,26 +2503,15 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
valuep->value.str = t_outStr;
|
||||
int bytes = VL_BYTES_I(varp->packed().elements());
|
||||
t_outDynamicStr.resize(bytes);
|
||||
const CData* datap = (reinterpret_cast<CData*>(varDatap));
|
||||
int i;
|
||||
if (bytes > t_outStrSz) {
|
||||
// limit maximum size of output to size of buffer to prevent overrun.
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__,
|
||||
"%s: Truncating string value of %s for %s"
|
||||
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than "
|
||||
"required (%d)",
|
||||
__func__, VerilatedVpiError::strFromVpiVal(valuep->format),
|
||||
fullname, t_outStrSz, VL_VALUE_STRING_MAX_WORDS, bytes);
|
||||
bytes = t_outStrSz;
|
||||
}
|
||||
for (i = 0; i < bytes; ++i) {
|
||||
for (size_t i = 0; i < bytes; ++i) {
|
||||
const char val = datap[bytes - i - 1];
|
||||
// other simulators replace [leading?] zero chars with spaces, replicate here.
|
||||
t_outStr[i] = val ? val : ' ';
|
||||
t_outDynamicStr[i] = val ? val : ' ';
|
||||
}
|
||||
t_outStr[i] = '\0';
|
||||
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
|
||||
return;
|
||||
}
|
||||
} else if (valuep->format == vpiIntVal) {
|
||||
@@ -2432,7 +2560,7 @@ void vpi_get_value(vpiHandle object, p_vpi_value valuep) {
|
||||
}
|
||||
|
||||
vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_p*/,
|
||||
PLI_INT32 /*flags*/) {
|
||||
PLI_INT32 flags) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_put_value %p %p\n", object, valuep););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
VL_VPI_ERROR_RESET_();
|
||||
@@ -2440,11 +2568,24 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__, "Ignoring vpi_put_value with nullptr value pointer");
|
||||
return nullptr;
|
||||
}
|
||||
PLI_INT32 delay_mode = flags & 0xfff;
|
||||
if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) {
|
||||
if (delay_mode == vpiInertialDelay) {
|
||||
if (!VerilatedVpiPutHolder::canInertialDelay(valuep)) {
|
||||
VL_VPI_WARNING_(
|
||||
__FILE__, __LINE__,
|
||||
"%s: Unsupported p_vpi_value as requested for '%s' with vpiInertialDelay",
|
||||
__func__, vop->fullname());
|
||||
return nullptr;
|
||||
}
|
||||
VerilatedVpiImp::inertialDelay(vop, valuep);
|
||||
return object;
|
||||
}
|
||||
VL_DEBUG_IF_PLI(
|
||||
VL_DBG_MSGF("- vpi: vpi_put_value name=%s fmt=%d vali=%d\n", vop->fullname(),
|
||||
valuep->format, valuep->value.integer);
|
||||
VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n", vop->varp()->datap(), vop->varDatap()););
|
||||
VerilatedVpiImp::evalNeeded(true);
|
||||
|
||||
if (VL_UNLIKELY(!vop->varp()->isPublicRW())) {
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__,
|
||||
|
||||
@@ -54,6 +54,13 @@ public:
|
||||
static QData cbNextDeadline() VL_MT_UNSAFE_ONE;
|
||||
/// Debug dump of callbacks
|
||||
static void dumpCbs() VL_MT_UNSAFE_ONE;
|
||||
/// Checks VPI dirty state (i.e. whether vpi_put_value() has
|
||||
/// been called since the last clearEvalNeeded())
|
||||
static bool evalNeeded() VL_MT_UNSAFE_ONE;
|
||||
/// Clears VPI dirty state (see evalNeeded())
|
||||
static void clearEvalNeeded() VL_MT_UNSAFE_ONE;
|
||||
/// Perform inertially delayed puts
|
||||
static void doInertialPuts() VL_MT_UNSAFE_ONE;
|
||||
|
||||
// Self test, for internal use only
|
||||
static void selfTest() VL_MT_UNSAFE_ONE;
|
||||
|
||||
@@ -614,8 +614,13 @@ static inline double VL_ROUND(double n) {
|
||||
//=========================================================================
|
||||
// Time and performance
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace VlOs {
|
||||
|
||||
/// Get environment variable
|
||||
extern std::string getenvStr(const std::string& envvar,
|
||||
const std::string& defaultValue) VL_MT_SAFE;
|
||||
extern uint64_t memUsageBytes() VL_MT_SAFE; ///< Return memory usage in bytes, or 0 if unknown
|
||||
|
||||
// Internal: Record CPU time, starting point on construction, and current delta from that
|
||||
|
||||
@@ -99,5 +99,31 @@ uint64_t memUsageBytes() VL_MT_SAFE {
|
||||
#endif
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// VlOs::getenvStr implementation
|
||||
|
||||
std::string getenvStr(const std::string& envvar, const std::string& defaultValue) VL_MT_SAFE {
|
||||
std::string ret;
|
||||
#if defined(_MSC_VER)
|
||||
// Note: MinGW does not offer _dupenv_s
|
||||
const char* envvalue = nullptr;
|
||||
_dupenv_s((char**)&envvalue, nullptr, envvar.c_str());
|
||||
if (envvalue != nullptr) {
|
||||
const std::string result{envvalue};
|
||||
free((void*)envvalue);
|
||||
ret = result;
|
||||
} else {
|
||||
ret = defaultValue;
|
||||
}
|
||||
#else
|
||||
if (const char* const envvalue = getenv(envvar.c_str())) {
|
||||
ret = envvalue;
|
||||
} else {
|
||||
ret = defaultValue;
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
} //namespace VlOs
|
||||
|
||||
@@ -480,6 +480,7 @@ set_target_properties(${verilator} PROPERTIES
|
||||
MSVC_RUNTIME_LIBRARY MultiThreaded$<IF:$<CONFIG:Release>,,DebugDLL>
|
||||
#JOB_POOL_LINK one_job # Linking takes lots of resources
|
||||
INTERPROCEDURAL_OPTIMIZATION_RELEASE $<IF:MINGW,FALSE,TRUE>
|
||||
INCLUDE_DIRECTORIES ${FLEX_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
add_dependencies(${verilator}
|
||||
|
||||
@@ -56,6 +56,7 @@ pkgdatadir = @pkgdatadir@
|
||||
# Compile options
|
||||
CFG_WITH_CCWARN = @CFG_WITH_CCWARN@
|
||||
CFG_WITH_DEFENV = @CFG_WITH_DEFENV@
|
||||
CFG_WITH_SOLVER = @CFG_WITH_SOLVER@
|
||||
CPPFLAGS += @CPPFLAGS@
|
||||
CFLAGS += @CFLAGS@
|
||||
CXXFLAGS += @CXXFLAGS@
|
||||
@@ -125,6 +126,13 @@ ifeq ($(CFG_WITH_DEFENV),yes)
|
||||
else
|
||||
CPPFLAGS += -DDEFENV_VERILATOR_ROOT=\"$(VERILATOR_ROOT)\"
|
||||
endif
|
||||
ifneq ($(CFG_WITH_SOLVER),no)
|
||||
CPPFLAGS += -DDEFENV_VERILATOR_SOLVER='"$(CFG_WITH_SOLVER)"'
|
||||
else
|
||||
ifneq ($(VERILATOR_SOLVER),)
|
||||
CPPFLAGS += -DDEFENV_VERILATOR_SOLVER='"$(VERILATOR_SOLVER)"'
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
HEADERS = $(wildcard V*.h v*.h)
|
||||
|
||||
+2
-2
@@ -68,7 +68,7 @@ class ActiveTopVisitor final : public VNVisitor {
|
||||
UINFO(4, " ACTIVE " << nodep << endl);
|
||||
// Remove duplicate clocks and such; sensesp() may change!
|
||||
V3Const::constifyExpensiveEdit(nodep);
|
||||
AstSenTree* const sensesp = nodep->sensesp();
|
||||
AstSenTree* sensesp = nodep->sensesp();
|
||||
UASSERT_OBJ(sensesp, nodep, "nullptr");
|
||||
if (sensesp->sensesp() && sensesp->sensesp()->isNever()) {
|
||||
// Never executing. Kill it.
|
||||
@@ -106,7 +106,7 @@ class ActiveTopVisitor final : public VNVisitor {
|
||||
// There may be other references to same sense tree,
|
||||
// we'll be removing all references when we get to them,
|
||||
// but don't dangle our pointer yet!
|
||||
pushDeletep(sensesp);
|
||||
VL_DO_DANGLING(pushDeletep(sensesp), sensesp);
|
||||
}
|
||||
nodep->sensesp(wantp);
|
||||
}
|
||||
|
||||
+49
-1
@@ -287,7 +287,7 @@ class AssertVisitor final : public VNVisitor {
|
||||
checkifp->isBoundsCheck(true); // To avoid LATCH warning
|
||||
checkifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||
nodep->replaceWith(checkifp);
|
||||
pushDeletep(nodep);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
} else {
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
@@ -518,6 +518,54 @@ class AssertVisitor final : public VNVisitor {
|
||||
iterateChildren(nodep);
|
||||
newPslAssertion(nodep, nodep->failsp());
|
||||
}
|
||||
void visit(AstAssertCtl* nodep) override {
|
||||
if (VN_IS(m_modp, Class) || VN_IS(m_modp, Iface)) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: assertcontrols in classes or interfaces");
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
}
|
||||
|
||||
iterateChildren(nodep);
|
||||
|
||||
if (const AstConst* const constp = VN_CAST(nodep->controlTypep(), Const)) {
|
||||
nodep->ctlType(constp->toSInt());
|
||||
} else if (nodep->ctlType() == VAssertCtlType::_TO_BE_EVALUATED) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: non-const assert control type expression");
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (nodep->ctlType()) {
|
||||
case VAssertCtlType::ON:
|
||||
case VAssertCtlType::OFF:
|
||||
case VAssertCtlType::KILL: {
|
||||
UINFO(9, "Generating assertctl for a module: " << m_modp << endl);
|
||||
FileLine* const fl = nodep->fileline();
|
||||
const string assertOnStmt
|
||||
= string{"vlSymsp->_vm_contextp__->assertOn("}
|
||||
+ (nodep->ctlType() == VAssertCtlType::ON ? "true" : "false") + ");\n";
|
||||
nodep->replaceWith(new AstCExpr{fl, assertOnStmt, 1});
|
||||
break;
|
||||
}
|
||||
case VAssertCtlType::LOCK:
|
||||
case VAssertCtlType::UNLOCK:
|
||||
case VAssertCtlType::PASS_ON:
|
||||
case VAssertCtlType::PASS_OFF:
|
||||
case VAssertCtlType::FAIL_ON:
|
||||
case VAssertCtlType::FAIL_OFF:
|
||||
case VAssertCtlType::NONVACUOUS_ON:
|
||||
case VAssertCtlType::VACUOUS_OFF: {
|
||||
nodep->unlinkFrBack();
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported assertcontrol control_type");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
nodep->unlinkFrBack();
|
||||
nodep->v3warn(EC_ERROR, "Bad assertcontrol control_type (IEEE 1800-2023 Table 20-5)");
|
||||
}
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
void visit(AstAssertIntrinsic* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
newPslAssertion(nodep, nodep->failsp());
|
||||
|
||||
+12
-4
@@ -158,6 +158,13 @@ private:
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstClockingItem* const nodep) override {
|
||||
// Get a ref to the sampled/driven variable
|
||||
AstVar* const varp = nodep->varp();
|
||||
if (!varp) {
|
||||
// Unused item
|
||||
pushDeletep(nodep->unlinkFrBack());
|
||||
return;
|
||||
}
|
||||
FileLine* const flp = nodep->fileline();
|
||||
V3Const::constifyEdit(nodep->skewp());
|
||||
if (!VN_IS(nodep->skewp(), Const)) {
|
||||
@@ -168,9 +175,7 @@ private:
|
||||
AstConst* const skewp = VN_AS(nodep->skewp(), Const);
|
||||
if (skewp->num().isNegative()) skewp->v3error("Skew cannot be negative");
|
||||
AstNodeExpr* const exprp = nodep->exprp();
|
||||
// Get a ref to the sampled/driven variable
|
||||
AstVar* const varp = nodep->varp()->unlinkFrBack();
|
||||
m_clockingp->addVarsp(varp);
|
||||
m_clockingp->addVarsp(varp->unlinkFrBack());
|
||||
varp->user1p(nodep);
|
||||
if (nodep->direction() == VDirection::OUTPUT) {
|
||||
AstVarRef* const skewedRefp = new AstVarRef{flp, varp, VAccess::READ};
|
||||
@@ -224,6 +229,7 @@ private:
|
||||
AstVar* const queueVarp = new AstVar{
|
||||
flp, VVarType::MODULETEMP,
|
||||
"__Vqueue__" + m_clockingp->name() + "__DOT__" + varp->name(), queueDtp};
|
||||
queueVarp->lifetime(VLifetime::STATIC);
|
||||
m_clockingp->addNextHere(queueVarp);
|
||||
// Create a process like this:
|
||||
// always queue.push(<sampled var>);
|
||||
@@ -250,7 +256,7 @@ private:
|
||||
} else {
|
||||
nodep->v3fatal("Invalid direction");
|
||||
}
|
||||
pushDeletep(nodep->unlinkFrBack());
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
void visit(AstDelay* nodep) override {
|
||||
// Only cycle delays are relevant in this stage; also only process once
|
||||
@@ -290,6 +296,8 @@ private:
|
||||
const std::string delayName = m_cycleDlyNames.get(nodep);
|
||||
AstVar* const cntVarp = new AstVar{flp, VVarType::BLOCKTEMP, delayName + "__counter",
|
||||
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
|
||||
cntVarp->lifetime(VLifetime::AUTOMATIC);
|
||||
cntVarp->funcLocal(true);
|
||||
AstBegin* const beginp = new AstBegin{flp, delayName + "__block", cntVarp, false, true};
|
||||
beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, valuep});
|
||||
beginp->addStmtsp(new AstWhile{
|
||||
|
||||
+71
-19
@@ -253,17 +253,17 @@ public:
|
||||
, m_final{false}
|
||||
, m_initial{false} {}
|
||||
class Extends {};
|
||||
VBaseOverride(Extends)
|
||||
explicit VBaseOverride(Extends)
|
||||
: m_extends{true}
|
||||
, m_final{false}
|
||||
, m_initial{false} {}
|
||||
class Final {};
|
||||
VBaseOverride(Final)
|
||||
explicit VBaseOverride(Final)
|
||||
: m_extends{false}
|
||||
, m_final{true}
|
||||
, m_initial{false} {}
|
||||
class Initial {};
|
||||
VBaseOverride(Initial)
|
||||
explicit VBaseOverride(Initial)
|
||||
: m_extends{false}
|
||||
, m_final{false}
|
||||
, m_initial{true} {}
|
||||
@@ -559,6 +559,7 @@ public:
|
||||
DYNAMIC_TRIGGER_SCHEDULER,
|
||||
FORK_SYNC,
|
||||
PROCESS_REFERENCE,
|
||||
RANDOM_GENERATOR,
|
||||
// Unsigned and two state; fundamental types
|
||||
UINT32,
|
||||
UINT64,
|
||||
@@ -592,6 +593,7 @@ public:
|
||||
"VlDynamicTriggerScheduler",
|
||||
"VlFork",
|
||||
"VlProcessRef",
|
||||
"VlRandomizer",
|
||||
"IData",
|
||||
"QData",
|
||||
"LOGIC_IMPLICIT",
|
||||
@@ -599,20 +601,13 @@ public:
|
||||
return names[m_e];
|
||||
}
|
||||
const char* dpiType() const {
|
||||
static const char* const names[] = {"%E-unk", "svBit",
|
||||
"char", "void*",
|
||||
"char", "int",
|
||||
"%E-integer", "svLogic",
|
||||
"long long", "double",
|
||||
"short", "%E-time",
|
||||
"const char*", "%E-untyped",
|
||||
"dpiScope", "const char*",
|
||||
"%E-mtaskstate", "%E-triggervec",
|
||||
"%E-dly-sched", "%E-trig-sched",
|
||||
"%E-dyn-sched", "%E-fork",
|
||||
"%E-proc-ref", "IData",
|
||||
"QData", "%E-logic-implct",
|
||||
" MAX"};
|
||||
static const char* const names[]
|
||||
= {"%E-unk", "svBit", "char", "void*", "char",
|
||||
"int", "%E-integer", "svLogic", "long long", "double",
|
||||
"short", "%E-time", "const char*", "%E-untyped", "dpiScope",
|
||||
"const char*", "%E-mtaskstate", "%E-triggervec", "%E-dly-sched", "%E-trig-sched",
|
||||
"%E-dyn-sched", "%E-fork", "%E-proc-ref", "%E-rand-gen", "IData",
|
||||
"QData", "%E-logic-implct", " MAX"};
|
||||
return names[m_e];
|
||||
}
|
||||
static void selfTest() {
|
||||
@@ -652,6 +647,7 @@ public:
|
||||
case DYNAMIC_TRIGGER_SCHEDULER: return 0; // opaque
|
||||
case FORK_SYNC: return 0; // opaque
|
||||
case PROCESS_REFERENCE: return 0; // opaque
|
||||
case RANDOM_GENERATOR: return 0; // opaque
|
||||
case UINT32: return 32;
|
||||
case UINT64: return 64;
|
||||
default: return 0;
|
||||
@@ -691,7 +687,8 @@ public:
|
||||
return (m_e == EVENT || m_e == STRING || m_e == SCOPEPTR || m_e == CHARPTR
|
||||
|| m_e == MTASKSTATE || m_e == TRIGGERVEC || m_e == DELAY_SCHEDULER
|
||||
|| m_e == TRIGGER_SCHEDULER || m_e == DYNAMIC_TRIGGER_SCHEDULER || m_e == FORK_SYNC
|
||||
|| m_e == PROCESS_REFERENCE || m_e == DOUBLE || m_e == UNTYPED);
|
||||
|| m_e == PROCESS_REFERENCE || m_e == RANDOM_GENERATOR || m_e == DOUBLE
|
||||
|| m_e == UNTYPED);
|
||||
}
|
||||
bool isDouble() const VL_MT_SAFE { return m_e == DOUBLE; }
|
||||
bool isEvent() const { return m_e == EVENT; }
|
||||
@@ -903,7 +900,7 @@ public:
|
||||
TRIWIRE,
|
||||
TRI0,
|
||||
TRI1,
|
||||
PORT, // Temp type used in parser only
|
||||
PORT, // Used in parser and V3Fork to recognize ports
|
||||
BLOCKTEMP,
|
||||
MODULETEMP,
|
||||
STMTTEMP,
|
||||
@@ -1102,6 +1099,61 @@ constexpr bool operator==(VAlwaysKwd::en lhs, const VAlwaysKwd& rhs) { return lh
|
||||
|
||||
// ######################################################################
|
||||
|
||||
class VAssertCtlType final {
|
||||
public:
|
||||
// IEEE 1800-2023 Table 20-5
|
||||
enum en : uint8_t {
|
||||
_TO_BE_EVALUATED = 0,
|
||||
LOCK = 1,
|
||||
UNLOCK = 2,
|
||||
ON = 3,
|
||||
OFF = 4,
|
||||
KILL = 5,
|
||||
PASS_ON = 6,
|
||||
PASS_OFF = 7,
|
||||
FAIL_ON = 8,
|
||||
FAIL_OFF = 9,
|
||||
NONVACUOUS_ON = 10,
|
||||
VACUOUS_OFF = 11
|
||||
};
|
||||
enum en m_e;
|
||||
VAssertCtlType()
|
||||
: m_e{_TO_BE_EVALUATED} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
constexpr VAssertCtlType(en _e)
|
||||
: m_e{_e} {}
|
||||
explicit VAssertCtlType(int _e)
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
constexpr operator en() const { return m_e; }
|
||||
const char* ascii() const {
|
||||
// IEEE 1800-2023 20.11
|
||||
static const char* const names[] = {"",
|
||||
"",
|
||||
"",
|
||||
"$asserton",
|
||||
"$assertoff",
|
||||
"$assertkill",
|
||||
"$assertpasson",
|
||||
"$assertpassoff",
|
||||
"$assertfailon",
|
||||
"$assertfailoff",
|
||||
"$assertnonvacuouson",
|
||||
"$assertvacuousoff"};
|
||||
return names[m_e];
|
||||
}
|
||||
};
|
||||
constexpr bool operator==(const VAssertCtlType& lhs, const VAssertCtlType& rhs) {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
}
|
||||
constexpr bool operator==(const VAssertCtlType& lhs, VAssertCtlType::en rhs) {
|
||||
return lhs.m_e == rhs;
|
||||
}
|
||||
constexpr bool operator==(VAssertCtlType::en lhs, const VAssertCtlType& rhs) {
|
||||
return lhs == rhs.m_e;
|
||||
}
|
||||
|
||||
// ######################################################################
|
||||
|
||||
class VCaseType final {
|
||||
public:
|
||||
enum en : uint8_t { CT_CASE, CT_CASEX, CT_CASEZ, CT_CASEINSIDE };
|
||||
|
||||
+48
-13
@@ -89,8 +89,8 @@ public:
|
||||
virtual AstNodeDType* subDTypep() const VL_MT_SAFE { return nullptr; }
|
||||
virtual bool isFourstate() const;
|
||||
// Ideally an IEEE $typename
|
||||
virtual string prettyDTypeName() const { return prettyTypeName(); }
|
||||
string prettyDTypeNameQ() const { return "'" + prettyDTypeName() + "'"; }
|
||||
virtual string prettyDTypeName(bool) const { return prettyTypeName(); }
|
||||
string prettyDTypeNameQ() const { return "'" + prettyDTypeName(false) + "'"; }
|
||||
//
|
||||
// Changing the width may confuse the data type resolution, so must clear
|
||||
// TypeTable cache after use.
|
||||
@@ -215,6 +215,7 @@ public:
|
||||
int uniqueNum() const { return m_uniqueNum; }
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string prettyDTypeName(bool) const override;
|
||||
bool isCompound() const override { return !packed(); }
|
||||
// For basicp() we reuse the size to indicate a "fake" basic type of same size
|
||||
AstBasicDType* basicp() const override {
|
||||
@@ -313,7 +314,7 @@ public:
|
||||
return asamep->subDTypep()
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
||||
}
|
||||
string prettyDTypeName() const override;
|
||||
string prettyDTypeName(bool full) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* getChild2DTypep() const override { return keyChildDTypep(); }
|
||||
@@ -387,7 +388,7 @@ public:
|
||||
return type() == samep->type() && same(samep);
|
||||
}
|
||||
string name() const override VL_MT_STABLE { return m.m_keyword.ascii(); }
|
||||
string prettyDTypeName() const override;
|
||||
string prettyDTypeName(bool full) const override;
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(dtypep() != this);
|
||||
return nullptr;
|
||||
@@ -428,6 +429,9 @@ public:
|
||||
bool isDynamicTriggerScheduler() const VL_MT_SAFE {
|
||||
return keyword() == VBasicDTypeKwd::DYNAMIC_TRIGGER_SCHEDULER;
|
||||
}
|
||||
bool isRandomGenerator() const VL_MT_SAFE {
|
||||
return keyword() == VBasicDTypeKwd::RANDOM_GENERATOR;
|
||||
}
|
||||
bool isOpaque() const VL_MT_SAFE { return keyword().isOpaque(); }
|
||||
bool isString() const VL_MT_SAFE { return keyword().isString(); }
|
||||
bool isZeroInit() const { return keyword().isZeroInit(); }
|
||||
@@ -501,7 +505,7 @@ public:
|
||||
}
|
||||
bool similarDType(const AstNodeDType* samep) const override { return same(samep); }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
string prettyDTypeName() const override { return m_name; }
|
||||
string prettyDTypeName(bool) const override { return m_name; }
|
||||
// METHODS
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||
@@ -548,6 +552,7 @@ public:
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
string prettyDTypeName(bool full) const override;
|
||||
string name() const override VL_MT_STABLE;
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||
@@ -716,7 +721,7 @@ public:
|
||||
return asamep->subDTypep()
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
||||
}
|
||||
string prettyDTypeName() const override;
|
||||
string prettyDTypeName(bool full) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE {
|
||||
@@ -806,6 +811,7 @@ public:
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
string prettyDTypeName(bool full) const override;
|
||||
// METHODS
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep()->basicp(); }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return subDTypep()->skipRefp(); }
|
||||
@@ -814,9 +820,9 @@ public:
|
||||
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); }
|
||||
int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); }
|
||||
int itemCount() const {
|
||||
size_t itemCount() const {
|
||||
size_t count = 0;
|
||||
for (AstNode* itemp = itemsp(); itemp; itemp = itemp->nextp()) count++;
|
||||
for (AstNode* itemp = itemsp(); itemp; itemp = itemp->nextp()) ++count;
|
||||
return count;
|
||||
}
|
||||
bool isCompound() const override { return false; }
|
||||
@@ -959,6 +965,30 @@ public:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
class AstNBACommitQueueDType final : public AstNodeDType {
|
||||
// @astgen ptr := m_subDTypep : AstNodeDType // Type of the corresponding variable
|
||||
const bool m_partial; // Partial element update required
|
||||
|
||||
public:
|
||||
AstNBACommitQueueDType(FileLine* fl, AstNodeDType* subDTypep, bool partial)
|
||||
: ASTGEN_SUPER_NBACommitQueueDType(fl)
|
||||
, m_partial{partial}
|
||||
, m_subDTypep{subDTypep} {
|
||||
dtypep(this);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstNBACommitQueueDType;
|
||||
|
||||
AstNodeDType* subDTypep() const override { return m_subDTypep; }
|
||||
bool partial() const { return m_partial; }
|
||||
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
|
||||
AstBasicDType* basicp() const override { return nullptr; }
|
||||
AstNodeDType* skipRefp() const override { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
int widthAlignBytes() const override { return 1; }
|
||||
int widthTotalBytes() const override { return 24; }
|
||||
bool isCompound() const override { return true; }
|
||||
};
|
||||
class AstParamTypeDType final : public AstNodeDType {
|
||||
// Parents: MODULE
|
||||
// A parameter type statement; much like a var or typedef
|
||||
@@ -1066,7 +1096,7 @@ public:
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
||||
}
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
string prettyDTypeName() const override;
|
||||
string prettyDTypeName(bool full) const override;
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE {
|
||||
return m_refDTypep ? m_refDTypep : childDTypep();
|
||||
@@ -1129,8 +1159,8 @@ public:
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
string prettyDTypeName() const override {
|
||||
return subDTypep() ? prettyName(subDTypep()->name()) : prettyName();
|
||||
string prettyDTypeName(bool full) const override {
|
||||
return subDTypep() ? prettyName(subDTypep()->prettyDTypeName(full)) : prettyName();
|
||||
}
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE {
|
||||
return subDTypep() ? subDTypep()->basicp() : nullptr;
|
||||
@@ -1262,6 +1292,11 @@ public:
|
||||
refDTypep(nullptr);
|
||||
dtypep(nullptr); // V3Width will resolve
|
||||
}
|
||||
AstUnsizedArrayDType(FileLine* fl, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_UnsizedArrayDType(fl) {
|
||||
refDTypep(dtp);
|
||||
dtypep(nullptr); // V3Width will resolve
|
||||
}
|
||||
ASTGEN_MEMBERS_AstUnsizedArrayDType;
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(!((m_refDTypep && !childDTypep()) || (!m_refDTypep && childDTypep())));
|
||||
@@ -1357,7 +1392,7 @@ public:
|
||||
inline AstPackArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstRange* rangep);
|
||||
inline AstPackArrayDType(FileLine* fl, AstNodeDType* dtp, AstRange* rangep);
|
||||
ASTGEN_MEMBERS_AstPackArrayDType;
|
||||
string prettyDTypeName() const override;
|
||||
string prettyDTypeName(bool full) const override;
|
||||
bool isCompound() const override { return false; }
|
||||
};
|
||||
class AstUnpackArrayDType final : public AstNodeArrayDType {
|
||||
@@ -1384,7 +1419,7 @@ public:
|
||||
widthFromSub(subDTypep());
|
||||
}
|
||||
ASTGEN_MEMBERS_AstUnpackArrayDType;
|
||||
string prettyDTypeName() const override;
|
||||
string prettyDTypeName(bool full) const override;
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstUnpackArrayDType* const sp = VN_DBG_AS(samep, UnpackArrayDType);
|
||||
return m_isCompound == sp->m_isCompound;
|
||||
|
||||
+47
-5
@@ -56,6 +56,7 @@ public:
|
||||
virtual string emitVerilog() = 0; /// Format string for verilog writing; see V3EmitV
|
||||
// For documentation on emitC format see EmitCFunc::emitOpName
|
||||
virtual string emitC() = 0;
|
||||
virtual string emitSMT() const { V3ERROR_NA_RETURN(""); };
|
||||
virtual string emitSimpleOperator() { return ""; } // "" means not ok to use
|
||||
virtual bool emitCheckMaxWords() { return false; } // Check VL_MULS_MAX_WORDS
|
||||
virtual bool cleanOut() const = 0; // True if output has extra upper bits zero
|
||||
@@ -2259,7 +2260,7 @@ class AstWith final : public AstNodeExpr {
|
||||
// @astgen op3 := exprp : List[AstNode]
|
||||
public:
|
||||
AstWith(FileLine* fl, AstLambdaArgRef* indexArgRefp, AstLambdaArgRef* valueArgRefp,
|
||||
AstNodeExpr* exprp)
|
||||
AstNode* exprp)
|
||||
: ASTGEN_SUPER_With(fl) {
|
||||
this->indexArgRefp(indexArgRefp);
|
||||
this->valueArgRefp(valueArgRefp);
|
||||
@@ -2282,13 +2283,13 @@ class AstWithParse final : public AstNodeExpr {
|
||||
// Replaced with AstWith
|
||||
// Parents: expr|stmt
|
||||
// Children: funcref, expr
|
||||
// @astgen op1 := funcrefp : AstNode
|
||||
// @astgen op2 := exprp : Optional[AstNodeExpr]
|
||||
// @astgen op1 := funcrefp : AstNodeExpr
|
||||
// @astgen op2 := exprsp : List[AstNode]
|
||||
public:
|
||||
AstWithParse(FileLine* fl, AstNode* funcrefp, AstNodeExpr* exprp)
|
||||
AstWithParse(FileLine* fl, AstNodeExpr* funcrefp, AstNode* exprsp)
|
||||
: ASTGEN_SUPER_WithParse(fl) {
|
||||
this->funcrefp(funcrefp);
|
||||
this->exprp(exprp);
|
||||
this->addExprsp(exprsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstWithParse;
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
@@ -2401,6 +2402,7 @@ public:
|
||||
out.opConcat(lhs, rhs);
|
||||
}
|
||||
string emitC() override { return "VL_CONCAT_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(concat %l %r)"; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
@@ -2447,6 +2449,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f/ %r)"; }
|
||||
string emitC() override { return "VL_DIV_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvudiv %l %r)"; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
@@ -2493,6 +2496,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f/ %r)"; }
|
||||
string emitC() override { return "VL_DIVS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvsdiv %l %r)"; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
@@ -2519,6 +2523,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f==? %r)"; }
|
||||
string emitC() override { return "VL_EQ_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(= %l %r)"; }
|
||||
string emitSimpleOperator() override { return "=="; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -2641,6 +2646,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f> %r)"; }
|
||||
string emitC() override { return "VL_GT_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvugt %l %r)"; }
|
||||
string emitSimpleOperator() override { return ">"; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -2711,6 +2717,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f> %r)"; }
|
||||
string emitC() override { return "VL_GTS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvsgt %l %r)"; }
|
||||
string emitSimpleOperator() override { return ""; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -2734,6 +2741,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f>= %r)"; }
|
||||
string emitC() override { return "VL_GTE_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvuge %l %r)"; }
|
||||
string emitSimpleOperator() override { return ">="; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -2804,6 +2812,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f>= %r)"; }
|
||||
string emitC() override { return "VL_GTES_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvsge %l %r)"; }
|
||||
string emitSimpleOperator() override { return ""; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -2827,6 +2836,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f&& %r)"; }
|
||||
string emitC() override { return "VL_LOGAND_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(and %l %r)"; }
|
||||
string emitSimpleOperator() override { return "&&"; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -2850,6 +2860,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f-> %r)"; }
|
||||
string emitC() override { return "VL_LOGIF_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(=> %l %r)"; }
|
||||
string emitSimpleOperator() override { return "->"; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -2873,6 +2884,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f|| %r)"; }
|
||||
string emitC() override { return "VL_LOGOR_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(or %l %r)"; }
|
||||
string emitSimpleOperator() override { return "||"; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -2896,6 +2908,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f< %r)"; }
|
||||
string emitC() override { return "VL_LT_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvult %l %r)"; }
|
||||
string emitSimpleOperator() override { return "<"; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -2966,6 +2979,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f< %r)"; }
|
||||
string emitC() override { return "VL_LTS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvslt %l %r)"; }
|
||||
string emitSimpleOperator() override { return ""; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -2989,6 +3003,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f<= %r)"; }
|
||||
string emitC() override { return "VL_LTE_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvule %l %r)"; }
|
||||
string emitSimpleOperator() override { return "<="; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -3059,6 +3074,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f<= %r)"; }
|
||||
string emitC() override { return "VL_LTES_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvsle %l %r)"; }
|
||||
string emitSimpleOperator() override { return ""; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -3082,6 +3098,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f%% %r)"; }
|
||||
string emitC() override { return "VL_MODDIV_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvurem %l %r)"; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
@@ -3104,6 +3121,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f%% %r)"; }
|
||||
string emitC() override { return "VL_MODDIVS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvsmod %l %r)"; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
@@ -3277,6 +3295,9 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%f{%r{%k%l}}"; }
|
||||
string emitC() override { return "VL_REPLICATE_%nq%lq%rq(%lw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override {
|
||||
return "((_ repeat " + cvtToStr(width() / lhsp()->width()) + ") %l)";
|
||||
}
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
@@ -3325,6 +3346,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f<< %r)"; }
|
||||
string emitC() override { return "VL_SHIFTL_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvshl %l %r)"; }
|
||||
string emitSimpleOperator() override {
|
||||
return (rhsp()->isWide() || rhsp()->isQuad()) ? "" : "<<";
|
||||
}
|
||||
@@ -3373,6 +3395,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f>> %r)"; }
|
||||
string emitC() override { return "VL_SHIFTR_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvlshr %l %r)"; }
|
||||
string emitSimpleOperator() override {
|
||||
return (rhsp()->isWide() || rhsp()->isQuad()) ? "" : ">>";
|
||||
}
|
||||
@@ -3425,6 +3448,7 @@ public:
|
||||
out.opShiftRS(lhs, rhs, lhsp()->widthMinV());
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f>>> %r)"; }
|
||||
string emitSMT() const override { return "(bvashr %l %r)"; }
|
||||
string emitC() override { return "VL_SHIFTRS_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
||||
string emitSimpleOperator() override { return ""; }
|
||||
bool cleanOut() const override { return false; }
|
||||
@@ -3476,6 +3500,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f- %r)"; }
|
||||
string emitC() override { return "VL_SUB_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvsub %l %r)"; }
|
||||
string emitSimpleOperator() override { return "-"; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return false; }
|
||||
@@ -3552,6 +3577,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f== %r)"; }
|
||||
string emitC() override { return "VL_EQ_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(= %l %r)"; }
|
||||
string emitSimpleOperator() override { return "=="; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -3668,6 +3694,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f<-> %r)"; }
|
||||
string emitC() override { return "VL_LOGEQ_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvxnor %l %r)"; }
|
||||
string emitSimpleOperator() override { return "<->"; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -3692,6 +3719,7 @@ public:
|
||||
string emitVerilog() override { return "%k(%l %f!= %r)"; }
|
||||
string emitC() override { return "VL_NEQ_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSimpleOperator() override { return "!="; }
|
||||
string emitSMT() const override { return "(not (= %l %r))"; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
@@ -3809,6 +3837,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f+ %r)"; }
|
||||
string emitC() override { return "VL_ADD_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvadd %l %r)"; }
|
||||
string emitSimpleOperator() override { return "+"; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return false; }
|
||||
@@ -3855,6 +3884,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f& %r)"; }
|
||||
string emitC() override { return "VL_AND_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvand %l %r)"; }
|
||||
string emitSimpleOperator() override { return "&"; }
|
||||
bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
|
||||
bool cleanLhs() const override { return false; }
|
||||
@@ -3878,6 +3908,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f* %r)"; }
|
||||
string emitC() override { return "VL_MUL_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvmul %l %r)"; }
|
||||
string emitSimpleOperator() override { return "*"; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -3950,6 +3981,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f| %r)"; }
|
||||
string emitC() override { return "VL_OR_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvor %l %r)"; }
|
||||
string emitSimpleOperator() override { return "|"; }
|
||||
bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
|
||||
bool cleanLhs() const override { return false; }
|
||||
@@ -3973,6 +4005,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f^ %r)"; }
|
||||
string emitC() override { return "VL_XOR_%lq(%lW, %P, %li, %ri)"; }
|
||||
string emitSMT() const override { return "(bvxor %l %r)"; }
|
||||
string emitSimpleOperator() override { return "^"; }
|
||||
bool cleanOut() const override { return false; } // Lclean && Rclean
|
||||
bool cleanLhs() const override { return false; }
|
||||
@@ -4895,6 +4928,9 @@ public:
|
||||
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAssign(lhs); }
|
||||
string emitVerilog() override { return "%l"; }
|
||||
string emitC() override { return "VL_EXTEND_%nq%lq(%nw,%lw, %P, %li)"; }
|
||||
string emitSMT() const override {
|
||||
return "((_ zero_extend " + cvtToStr(width() - lhsp()->width()) + ") %l)";
|
||||
}
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override {
|
||||
@@ -4918,6 +4954,9 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%l"; }
|
||||
string emitC() override { return "VL_EXTENDS_%nq%lq(%nw,%lw, %P, %li)"; }
|
||||
string emitSMT() const override {
|
||||
return "((_ sign_extend " + cvtToStr(width() - lhsp()->width()) + ") %l)";
|
||||
}
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override {
|
||||
@@ -5050,6 +5089,7 @@ public:
|
||||
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opLogNot(lhs); }
|
||||
string emitVerilog() override { return "%f(! %l)"; }
|
||||
string emitC() override { return "VL_LOGNOT_%nq%lq(%nw,%lw, %P, %li)"; }
|
||||
string emitSMT() const override { return "(not %l)"; }
|
||||
string emitSimpleOperator() override { return "!"; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
@@ -5080,6 +5120,7 @@ public:
|
||||
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opNegate(lhs); }
|
||||
string emitVerilog() override { return "%f(- %l)"; }
|
||||
string emitC() override { return "VL_NEGATE_%lq(%lW, %P, %li)"; }
|
||||
string emitSMT() const override { return "(bvneg %l)"; }
|
||||
string emitSimpleOperator() override { return "-"; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return false; }
|
||||
@@ -5112,6 +5153,7 @@ public:
|
||||
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opNot(lhs); }
|
||||
string emitVerilog() override { return "%f(~ %l)"; }
|
||||
string emitC() override { return "VL_NOT_%lq(%lW, %P, %li)"; }
|
||||
string emitSMT() const override { return "(bvnot %l)"; }
|
||||
string emitSimpleOperator() override { return "~"; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return false; }
|
||||
|
||||
+37
-6
@@ -956,6 +956,7 @@ class AstClockingItem final : public AstNode {
|
||||
// @astgen op2 := exprp : Optional[AstNodeExpr]
|
||||
// @astgen op3 := assignp : Optional[AstAssign]
|
||||
// @astgen op4 := varp : Optional[AstVar]
|
||||
// @astgen ptr := m_outputp : Optional[AstClockingItem]
|
||||
VDirection m_direction;
|
||||
|
||||
public:
|
||||
@@ -971,6 +972,9 @@ public:
|
||||
}
|
||||
ASTGEN_MEMBERS_AstClockingItem;
|
||||
VDirection direction() const { return m_direction; }
|
||||
AstClockingItem* outputp() const { return m_outputp; }
|
||||
void outputp(AstClockingItem* outputp) { m_outputp = outputp; }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
};
|
||||
class AstConstPool final : public AstNode {
|
||||
// Container for const static data
|
||||
@@ -1809,6 +1813,7 @@ class AstVar final : public AstNode {
|
||||
bool m_trace : 1; // Trace this variable
|
||||
bool m_isLatched : 1; // Not assigned in all control paths of combo always
|
||||
bool m_isForceable : 1; // May be forced/released externally from user C code
|
||||
bool m_isForcedByCode : 1; // May be forced/released from AstAssignForce/AstRelease
|
||||
bool m_isWrittenByDpi : 1; // This variable can be written by a DPI Export
|
||||
bool m_isWrittenBySuspendable : 1; // This variable can be written by a suspendable process
|
||||
|
||||
@@ -1854,6 +1859,7 @@ class AstVar final : public AstNode {
|
||||
m_trace = false;
|
||||
m_isLatched = false;
|
||||
m_isForceable = false;
|
||||
m_isForcedByCode = false;
|
||||
m_isWrittenByDpi = false;
|
||||
m_isWrittenBySuspendable = false;
|
||||
m_attrClocker = VVarAttrClocker::CLOCKER_UNKNOWN;
|
||||
@@ -2009,6 +2015,8 @@ public:
|
||||
void isLatched(bool flag) { m_isLatched = flag; }
|
||||
bool isForceable() const { return m_isForceable; }
|
||||
void setForceable() { m_isForceable = true; }
|
||||
void setForcedByCode() { m_isForcedByCode = true; }
|
||||
bool isForced() const { return m_isForceable || m_isForcedByCode; }
|
||||
bool isWrittenByDpi() const { return m_isWrittenByDpi; }
|
||||
void setWrittenByDpi() { m_isWrittenByDpi = true; }
|
||||
bool isWrittenBySuspendable() const { return m_isWrittenBySuspendable; }
|
||||
@@ -2434,13 +2442,11 @@ public:
|
||||
ASTGEN_MEMBERS_AstAlwaysObserved;
|
||||
};
|
||||
class AstAlwaysPost final : public AstNodeProcedure {
|
||||
// Like always but post assignments for memory assignment IFs
|
||||
// @astgen op1 := sensesp : Optional[AstSenTree] // Sensitivity list iff clocked
|
||||
// Like always but 'post' scheduled, e.g. for array NBA commits
|
||||
|
||||
public:
|
||||
AstAlwaysPost(FileLine* fl, AstSenTree* sensesp, AstNode* stmtsp)
|
||||
: ASTGEN_SUPER_AlwaysPost(fl, stmtsp) {
|
||||
this->sensesp(sensesp);
|
||||
}
|
||||
explicit AstAlwaysPost(FileLine* fl)
|
||||
: ASTGEN_SUPER_AlwaysPost(fl, nullptr) {}
|
||||
ASTGEN_MEMBERS_AstAlwaysPost;
|
||||
};
|
||||
class AstAlwaysPostponed final : public AstNodeProcedure {
|
||||
@@ -2580,6 +2586,31 @@ public:
|
||||
bool isJustOneBodyStmt() const { return stmtsp() && !stmtsp()->nextp(); }
|
||||
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
|
||||
};
|
||||
class AstAssertCtl final : public AstNodeStmt {
|
||||
// @astgen op1 := controlTypep : AstNodeExpr
|
||||
// @astgen op2 := levelp : AstNodeExpr
|
||||
// @astgen op3 := itemsp : List[AstNodeExpr]
|
||||
// Type of assertcontrol task; either known from parser or from evaluated
|
||||
// controlTypep expression.
|
||||
VAssertCtlType m_ctlType; // $assert keyword type
|
||||
|
||||
public:
|
||||
AstAssertCtl(FileLine* fl, VAssertCtlType ctlType, AstNodeExpr* levelp = nullptr,
|
||||
AstNodeExpr* itemsp = nullptr);
|
||||
AstAssertCtl(FileLine* fl, AstNodeExpr* controlTypep, AstNodeExpr* assertionTypep = nullptr,
|
||||
AstNodeExpr* directiveTypep = nullptr, AstNodeExpr* levelp = nullptr,
|
||||
AstNodeExpr* itemsp = nullptr);
|
||||
ASTGEN_MEMBERS_AstAssertCtl;
|
||||
string verilogKwd() const override { return m_ctlType.ascii(); }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() override { return false; }
|
||||
bool isOutputter() override { return true; }
|
||||
VAssertCtlType ctlType() const { return m_ctlType; }
|
||||
void ctlType(int32_t type) { m_ctlType = VAssertCtlType{type}; }
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
};
|
||||
class AstBreak final : public AstNodeStmt {
|
||||
public:
|
||||
explicit AstBreak(FileLine* fl)
|
||||
|
||||
+93
-15
@@ -788,6 +788,22 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe
|
||||
info.m_type = "VlUnpacked<" + sub.m_type;
|
||||
info.m_type += ", " + cvtToStr(adtypep->declRange().elements());
|
||||
info.m_type += ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, NBACommitQueueDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
compound = true;
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(compound, false);
|
||||
AstNodeDType* eDTypep = adtypep->subDTypep();
|
||||
unsigned rank = 0;
|
||||
while (AstUnpackArrayDType* const uaDTypep = VN_CAST(eDTypep, UnpackArrayDType)) {
|
||||
eDTypep = uaDTypep->subDTypep()->skipRefp();
|
||||
++rank;
|
||||
}
|
||||
info.m_type = "VlNBACommitQueue<";
|
||||
info.m_type += sub.m_type;
|
||||
info.m_type += adtypep->partial() ? ", true" : ", false";
|
||||
info.m_type += ", " + eDTypep->cTypeRecurse(compound, false).m_type;
|
||||
info.m_type += ", " + std::to_string(rank);
|
||||
info.m_type += ">";
|
||||
} else if (packed && (VN_IS(dtypep, PackArrayDType))) {
|
||||
const AstPackArrayDType* const adtypep = VN_CAST(dtypep, PackArrayDType);
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(false, true);
|
||||
@@ -827,6 +843,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe
|
||||
info.m_type = "VlForkSync";
|
||||
} else if (bdtypep->isProcessRef()) {
|
||||
info.m_type = "VlProcessRef";
|
||||
} else if (bdtypep->isRandomGenerator()) {
|
||||
info.m_type = "VlRandomizer";
|
||||
} else if (bdtypep->isEvent()) {
|
||||
info.m_type = v3Global.assignsEvents() ? "VlAssignableEvent" : "VlEvent";
|
||||
} else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width
|
||||
@@ -885,9 +903,10 @@ std::pair<uint32_t, uint32_t> AstNodeDType::dimensions(bool includeBasic) {
|
||||
}
|
||||
dtypep = adtypep->subDTypep();
|
||||
continue;
|
||||
} else if (const AstQueueDType* const qdtypep = VN_CAST(dtypep, QueueDType)) {
|
||||
} else if (VN_IS(dtypep, QueueDType) || VN_IS(dtypep, DynArrayDType)
|
||||
|| VN_IS(dtypep, AssocArrayDType) || VN_IS(dtypep, WildcardArrayDType)) {
|
||||
unpacked++;
|
||||
dtypep = qdtypep->subDTypep();
|
||||
dtypep = dtypep->subDTypep();
|
||||
continue;
|
||||
} else if (const AstBasicDType* const adtypep = VN_CAST(dtypep, BasicDType)) {
|
||||
if (includeBasic && (adtypep->isRanged() || adtypep->isString())) packed++;
|
||||
@@ -1442,7 +1461,32 @@ void AstAlways::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStr(str, "keyword", keyword().ascii());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
|
||||
AstAssertCtl::AstAssertCtl(FileLine* fl, VAssertCtlType ctlType, AstNodeExpr* levelp,
|
||||
AstNodeExpr* itemsp)
|
||||
: ASTGEN_SUPER_AssertCtl(fl)
|
||||
, m_ctlType{ctlType} {
|
||||
controlTypep(new AstConst{fl, ctlType});
|
||||
if (!levelp) levelp = new AstConst{fl, 0};
|
||||
this->levelp(levelp);
|
||||
addItemsp(itemsp);
|
||||
}
|
||||
AstAssertCtl::AstAssertCtl(FileLine* fl, AstNodeExpr* controlTypep, AstNodeExpr*, AstNodeExpr*,
|
||||
AstNodeExpr* levelp, AstNodeExpr* itemsp)
|
||||
: ASTGEN_SUPER_AssertCtl(fl)
|
||||
, m_ctlType{VAssertCtlType::_TO_BE_EVALUATED} {
|
||||
this->controlTypep(controlTypep);
|
||||
if (!levelp) levelp = new AstConst{fl, 0};
|
||||
this->levelp(levelp);
|
||||
addItemsp(itemsp);
|
||||
}
|
||||
void AstAssertCtl::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " [" << ctlType().ascii() << "]";
|
||||
}
|
||||
void AstAssertCtl::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStr(str, "ctlType", ctlType().ascii());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstAttrOf::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " [" << attrType().ascii() << "]";
|
||||
@@ -1463,7 +1507,7 @@ void AstBasicDType::dumpJson(std::ostream& str) const {
|
||||
}
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
string AstBasicDType::prettyDTypeName() const {
|
||||
string AstBasicDType::prettyDTypeName(bool) const {
|
||||
std::ostringstream os;
|
||||
os << keyword().ascii();
|
||||
if (isRanged() && !rangep() && keyword().width() <= 1) {
|
||||
@@ -1588,6 +1632,7 @@ void AstClassRefDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "class:" << name();
|
||||
}
|
||||
string AstClassRefDType::prettyDTypeName(bool) const { return "class{}"s + prettyName(); }
|
||||
string AstClassRefDType::name() const { return classp() ? classp()->name() : "<unlinked>"; }
|
||||
void AstNodeCoverOrAssert::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
@@ -1624,6 +1669,22 @@ void AstEnumDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "enum";
|
||||
}
|
||||
string AstEnumDType::prettyDTypeName(bool full) const {
|
||||
string result = "enum{";
|
||||
if (full) { // else shorten for error messages
|
||||
for (AstEnumItem* itemp = itemsp(); itemp; itemp = VN_AS(itemp->nextp(), EnumItem)) {
|
||||
result += itemp->prettyName() + "=";
|
||||
if (AstConst* constp = VN_CAST(itemp->valuep(), Const)) {
|
||||
result += constp->num().ascii(true, true);
|
||||
} else {
|
||||
result += "?";
|
||||
}
|
||||
result += ";";
|
||||
}
|
||||
}
|
||||
result += "}" + prettyName();
|
||||
return result;
|
||||
}
|
||||
void AstEnumItemRef::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
str << " -> ";
|
||||
@@ -1938,6 +1999,18 @@ void AstNodeUOrStructDType::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFunc(str, isFourstate);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
string AstNodeUOrStructDType::prettyDTypeName(bool full) const {
|
||||
string result = verilogKwd() + "{";
|
||||
if (full) { // else shorten for errors
|
||||
for (AstMemberDType* itemp = membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
result += itemp->subDTypep()->prettyDTypeName(full);
|
||||
result += " " + itemp->prettyName() + ";";
|
||||
}
|
||||
}
|
||||
result += "}" + prettyName();
|
||||
return result;
|
||||
}
|
||||
void AstNodeDType::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (generic()) str << " [GENERIC]";
|
||||
@@ -1977,13 +2050,13 @@ void AstNodeArrayDType::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStr(str, "declRange", cvtToStr(declRange()));
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
string AstPackArrayDType::prettyDTypeName() const {
|
||||
string AstPackArrayDType::prettyDTypeName(bool full) const {
|
||||
std::ostringstream os;
|
||||
if (const auto subp = subDTypep()) os << subp->prettyDTypeName();
|
||||
if (const auto subp = subDTypep()) os << subp->prettyDTypeName(full);
|
||||
os << declRange();
|
||||
return os.str();
|
||||
}
|
||||
string AstUnpackArrayDType::prettyDTypeName() const {
|
||||
string AstUnpackArrayDType::prettyDTypeName(bool full) const {
|
||||
std::ostringstream os;
|
||||
string ranges = cvtToStr(declRange());
|
||||
// Unfortunately we need a single $ for the first unpacked, and all
|
||||
@@ -1993,7 +2066,7 @@ string AstUnpackArrayDType::prettyDTypeName() const {
|
||||
ranges += cvtToStr(adtypep->declRange());
|
||||
subp = adtypep->subDTypep()->skipRefp();
|
||||
}
|
||||
os << subp->prettyDTypeName() << "$" << ranges;
|
||||
os << subp->prettyDTypeName(full) << "$" << ranges;
|
||||
return os.str();
|
||||
}
|
||||
std::vector<AstUnpackArrayDType*> AstUnpackArrayDType::unpackDimensions() {
|
||||
@@ -2137,20 +2210,22 @@ void AstAssocArrayDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "[assoc-" << nodeAddr(keyDTypep()) << "]";
|
||||
}
|
||||
string AstAssocArrayDType::prettyDTypeName() const {
|
||||
return subDTypep()->prettyDTypeName() + "[" + keyDTypep()->prettyDTypeName() + "]";
|
||||
string AstAssocArrayDType::prettyDTypeName(bool full) const {
|
||||
return subDTypep()->prettyDTypeName(full) + "$[" + keyDTypep()->prettyDTypeName(full) + "]";
|
||||
}
|
||||
void AstDynArrayDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "[]";
|
||||
str << "$[]";
|
||||
}
|
||||
string AstDynArrayDType::prettyDTypeName(bool full) const {
|
||||
return subDTypep()->prettyDTypeName(full) + "$[]";
|
||||
}
|
||||
string AstDynArrayDType::prettyDTypeName() const { return subDTypep()->prettyDTypeName() + "[]"; }
|
||||
void AstQueueDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "[queue]";
|
||||
}
|
||||
string AstQueueDType::prettyDTypeName() const {
|
||||
string str = subDTypep()->prettyDTypeName() + "[$";
|
||||
string AstQueueDType::prettyDTypeName(bool full) const {
|
||||
string str = subDTypep()->prettyDTypeName(full) + "$[$";
|
||||
if (boundConst()) str += ":" + cvtToStr(boundConst());
|
||||
return str + "]";
|
||||
}
|
||||
@@ -2683,6 +2758,7 @@ void AstCMethodHard::setPurity() {
|
||||
{"commit", false},
|
||||
{"delay", false},
|
||||
{"done", false},
|
||||
{"enqueue", false},
|
||||
{"erase", false},
|
||||
{"evaluate", false},
|
||||
{"evaluation", false},
|
||||
@@ -2695,6 +2771,7 @@ void AstCMethodHard::setPurity() {
|
||||
{"find_last_index", true},
|
||||
{"fire", false},
|
||||
{"first", false},
|
||||
{"hard", false},
|
||||
{"init", false},
|
||||
{"insert", false},
|
||||
{"inside", true},
|
||||
@@ -2734,7 +2811,8 @@ void AstCMethodHard::setPurity() {
|
||||
{"trigger", false},
|
||||
{"unique", true},
|
||||
{"unique_index", true},
|
||||
{"word", true}};
|
||||
{"word", true},
|
||||
{"write_var", false}};
|
||||
|
||||
auto isPureIt = isPureMethod.find(name());
|
||||
UASSERT_OBJ(isPureIt != isPureMethod.end(), this, "Unknown purity of method " + name());
|
||||
|
||||
+3
-1
@@ -113,7 +113,9 @@ static void makeToStringMiddle(AstClass* nodep) {
|
||||
std::string comma;
|
||||
for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) {
|
||||
if (const auto* const varp = VN_CAST(itemp, Var)) {
|
||||
if (!varp->isParam() && !varp->isInternal()) {
|
||||
if (!varp->isParam() && !varp->isInternal()
|
||||
&& !(varp->dtypeSkipRefp()->basicp()
|
||||
&& varp->dtypeSkipRefp()->basicp()->isRandomGenerator())) {
|
||||
string stmt = "out += \"";
|
||||
stmt += comma;
|
||||
comma = ", ";
|
||||
|
||||
+9
-5
@@ -1556,7 +1556,9 @@ class ConstVisitor final : public VNVisitor {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool operandsSameSize(AstNode* lhsp, AstNode* rhsp) { return lhsp->width() == rhsp->width(); }
|
||||
static bool operandsSameWidth(const AstNode* lhsp, const AstNode* rhsp) {
|
||||
return lhsp->width() == rhsp->width();
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// Constant Replacement functions.
|
||||
@@ -3071,8 +3073,9 @@ class ConstVisitor final : public VNVisitor {
|
||||
&& varrefp // Don't do messes with BITREFs/ARRAYREFs
|
||||
&& !varrefp->varp()->hasStrengthAssignment() // Strengths are resolved in V3Tristate
|
||||
&& !varrefp->varp()->valuep() // Not already constified
|
||||
&& !varrefp->varScopep()) { // Not scoped (or each scope may have different initial
|
||||
// value)
|
||||
&& !varrefp->varScopep() // Not scoped (or each scope may have different initial val.)
|
||||
&& !varrefp->varp()->isForced() // Not forced (not really a constant)
|
||||
) {
|
||||
// ASSIGNW (VARREF, const) -> INITIAL ( ASSIGN (VARREF, const) )
|
||||
UINFO(4, "constAssignW " << nodep << endl);
|
||||
// Make a initial assignment
|
||||
@@ -3542,7 +3545,7 @@ class ConstVisitor final : public VNVisitor {
|
||||
TREEOP ("AstMulS {$lhsp.isOne, $rhsp}", "replaceWRhs(nodep)");
|
||||
TREEOP ("AstDiv {$lhsp, $rhsp.isOne}", "replaceWLhs(nodep)");
|
||||
TREEOP ("AstDivS {$lhsp, $rhsp.isOne}", "replaceWLhs(nodep)");
|
||||
TREEOP ("AstMul {operandIsPowTwo($lhsp), operandsSameSize($lhsp,,$rhsp)}", "replaceMulShift(nodep)"); // a*2^n -> a<<n
|
||||
TREEOP ("AstMul {operandIsPowTwo($lhsp), operandsSameWidth($lhsp,,$rhsp)}", "replaceMulShift(nodep)"); // a*2^n -> a<<n
|
||||
TREEOP ("AstDiv {$lhsp, operandIsPowTwo($rhsp)}", "replaceDivShift(nodep)"); // a/2^n -> a>>n
|
||||
TREEOP ("AstModDiv{$lhsp, operandIsPowTwo($rhsp)}", "replaceModAnd(nodep)"); // a % 2^n -> a&(2^n-1)
|
||||
TREEOP ("AstPow {operandIsTwo($lhsp), !$rhsp.isZero}", "replacePowShift(nodep)"); // 2**a == 1<<a
|
||||
@@ -3736,7 +3739,8 @@ class ConstVisitor final : public VNVisitor {
|
||||
TREEOPC("AstXor {matchBitOpTree(nodep)}", "DONE");
|
||||
// Note can't simplify a extend{extends}, extends{extend}, as the sign
|
||||
// bits end up in the wrong places
|
||||
TREEOPV("AstExtend {$lhsp.castExtend}", "replaceExtend(nodep, VN_AS(nodep->lhsp(), Extend)->lhsp())");
|
||||
TREEOPV("AstExtend{operandsSameWidth(nodep,,$lhsp)}", "replaceWLhs(nodep)");
|
||||
TREEOPV("AstExtend{$lhsp.castExtend}", "replaceExtend(nodep, VN_AS(nodep->lhsp(), Extend)->lhsp())");
|
||||
TREEOPV("AstExtendS{$lhsp.castExtendS}", "replaceExtend(nodep, VN_AS(nodep->lhsp(), ExtendS)->lhsp())");
|
||||
TREEOPV("AstReplicate{$srcp, $countp.isOne, $srcp->width()==nodep->width()}", "replaceWLhs(nodep)"); // {1{lhs}}->lhs
|
||||
TREEOPV("AstReplicateN{$lhsp, $rhsp.isOne, $lhsp->width()==nodep->width()}", "replaceWLhs(nodep)"); // {1{lhs}}->lhs
|
||||
|
||||
+679
-433
File diff suppressed because it is too large
Load Diff
+1
-2
@@ -223,8 +223,7 @@ class DescopeVisitor final : public VNVisitor {
|
||||
}
|
||||
void visit(AstVarScope* nodep) override {
|
||||
// Delete the varscope when we're finished
|
||||
nodep->unlinkFrBack();
|
||||
pushDeletep(nodep);
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
|
||||
@@ -443,7 +443,7 @@ class AstToDfgVisitor final : public VNVisitor {
|
||||
// Mark variables with external references
|
||||
if (nodep->isIO() // Ports
|
||||
|| nodep->user2() // Target of a hierarchical reference
|
||||
|| nodep->isForceable() // Forceable
|
||||
|| nodep->isForced() // Forced
|
||||
) {
|
||||
getNet(nodep)->setHasExtRefs();
|
||||
}
|
||||
|
||||
+5
-1
@@ -64,6 +64,7 @@ public:
|
||||
|
||||
struct Hash final {
|
||||
size_t operator()(const KeySel& key) const {
|
||||
// cppcheck-suppress unreadVariable // cppcheck bug
|
||||
V3Hash hash{vertexHash(key.m_fromp)};
|
||||
hash += key.m_lsb;
|
||||
hash += key.m_width;
|
||||
@@ -83,6 +84,7 @@ class KeyUnary final {
|
||||
const DfgVertex* const m_source0p;
|
||||
|
||||
public:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
KeyUnary(DfgVertex* source0p)
|
||||
: m_source0p{source0p} {}
|
||||
|
||||
@@ -110,6 +112,7 @@ public:
|
||||
|
||||
struct Hash final {
|
||||
size_t operator()(const KeyBinary& key) const {
|
||||
// cppcheck-suppress unreadVariable // cppcheck bug
|
||||
V3Hash hash{vertexHash(key.m_source0p)};
|
||||
hash += vertexHash(key.m_source1p);
|
||||
return hash.value();
|
||||
@@ -137,6 +140,7 @@ public:
|
||||
|
||||
struct Hash final {
|
||||
size_t operator()(const KeyTernary& key) const {
|
||||
// cppcheck-suppress unreadVariable // cppcheck bug
|
||||
V3Hash hash{vertexHash(key.m_source0p)};
|
||||
hash += vertexHash(key.m_source1p);
|
||||
hash += vertexHash(key.m_source2p);
|
||||
@@ -348,7 +352,7 @@ class V3DfgCache final {
|
||||
inline CacheType<Vertex>& cacheForType();
|
||||
|
||||
public:
|
||||
V3DfgCache(DfgGraph& dfg)
|
||||
explicit V3DfgCache(DfgGraph& dfg)
|
||||
: m_dfg{dfg} {}
|
||||
|
||||
// Find a vertex of type 'Vertex', with the given operands, or create a new one and add it.
|
||||
|
||||
@@ -295,7 +295,8 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
|
||||
// For each cyclic component
|
||||
for (auto& component : cyclicComponents) {
|
||||
if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
|
||||
// TODO: Apply optimizations safe for cyclic graphs
|
||||
// Converting back to Ast assumes the 'regularize' pass was run, so we must run it
|
||||
V3DfgPasses::regularize(*component, ctx.m_regularizeContext);
|
||||
// Add back under the main DFG (we will convert everything back in one go)
|
||||
dfg->addGraph(*component);
|
||||
}
|
||||
|
||||
+3
-9
@@ -165,17 +165,11 @@ void V3DfgPasses::inlineVars(DfgGraph& dfg) {
|
||||
const AstVar* const astVarp = driverVarp->varp();
|
||||
// If driven from a SystemC variable
|
||||
if (astVarp->isSc()) continue;
|
||||
// If the variable is forceable
|
||||
if (astVarp->isForceable()) continue;
|
||||
// If the variable is forced
|
||||
if (astVarp->isForced()) continue;
|
||||
}
|
||||
|
||||
varp->forEachSinkEdge([=](DfgEdge& edge) {
|
||||
// If sink is a SystemC variable, don't inline that sink
|
||||
if (DfgVertexVar* const sinkVarp = edge.sinkp()->cast<DfgVarPacked>()) {
|
||||
if (sinkVarp->varp()->isSc()) return;
|
||||
}
|
||||
edge.relinkSource(driverp);
|
||||
});
|
||||
varp->forEachSinkEdge([=](DfgEdge& edge) { edge.relinkSource(driverp); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
-6
@@ -27,6 +27,7 @@
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
std::string V3DfgRegularizeContext::tmpNamePrefix(DfgGraph& dfg) {
|
||||
// cppcheck-suppress unreadVariable // cppcheck bug
|
||||
V3Hash hash{dfg.modulep()->name()};
|
||||
hash += m_label;
|
||||
std::string name = hash.toString();
|
||||
@@ -45,11 +46,15 @@ class DfgRegularize final {
|
||||
|
||||
// Return canonical variable that can be used to hold the value of this vertex
|
||||
DfgVarPacked* getCanonicalVariable(DfgVertex& vtx) {
|
||||
// First gather all existing variables fully written by this vertex
|
||||
// First gather all existing variables fully written by this vertex. Ignore SystemC
|
||||
// variables, those cannot act as canonical variables, as they cannot participate in
|
||||
// expressions or be assigned rvalues.
|
||||
std::vector<DfgVarPacked*> varVtxps;
|
||||
vtx.forEachSink([&](DfgVertex& sink) {
|
||||
if (DfgVarPacked* const varVtxp = sink.cast<DfgVarPacked>()) {
|
||||
if (varVtxp->isDrivenFullyByDfg()) varVtxps.push_back(varVtxp);
|
||||
if (varVtxp->isDrivenFullyByDfg() && !varVtxp->varp()->isSc()) {
|
||||
varVtxps.push_back(varVtxp);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -95,10 +100,22 @@ class DfgRegularize final {
|
||||
|
||||
// Ensure intermediate values used multiple times are written to variables
|
||||
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
||||
// Operations without multiple sinks need no variables
|
||||
if (!vtx.hasMultipleSinks()) continue;
|
||||
// Array selects need no variables, they are just memory references
|
||||
if (vtx.is<DfgArraySel>()) continue;
|
||||
const bool needsIntermediateVariable = [&]() {
|
||||
// Anything that drives an SC variable needs an intermediate,
|
||||
// as we can only assign simple variables to SC variables at runtime.
|
||||
const bool hasScSink = vtx.findSink<DfgVertexVar>([](const DfgVertexVar& var) { //
|
||||
return var.varp()->isSc();
|
||||
});
|
||||
if (hasScSink) return true;
|
||||
// Operations without multiple sinks need no variables
|
||||
if (!vtx.hasMultipleSinks()) return false;
|
||||
// Array selects need no variables, they are just memory references
|
||||
if (vtx.is<DfgArraySel>()) return false;
|
||||
// Otherwise needs an intermediate variable
|
||||
return true;
|
||||
}();
|
||||
|
||||
if (!needsIntermediateVariable) continue;
|
||||
|
||||
// This is an op which has multiple sinks. Ensure it is assigned to a variable.
|
||||
DfgVarPacked* const varp = getCanonicalVariable(vtx);
|
||||
|
||||
+1
-1
@@ -239,7 +239,7 @@ public:
|
||||
ASTGEN_MEMBERS_DfgVarPacked;
|
||||
|
||||
bool isDrivenFullyByDfg() const {
|
||||
return arity() == 1 && source(0)->dtypep() == dtypep() && !varp()->isForceable();
|
||||
return arity() == 1 && source(0)->dtypep() == dtypep() && !varp()->isForced();
|
||||
}
|
||||
|
||||
void addDriver(FileLine* flp, uint32_t lsb, DfgVertex* vtxp) {
|
||||
|
||||
@@ -84,6 +84,7 @@ class EmitCConstPool final : public EmitCConstInit {
|
||||
putns(varp, "extern const ");
|
||||
putns(varp, varp->dtypep()->cType(nameProtect, false, false));
|
||||
putns(varp, " = ");
|
||||
UASSERT_OBJ(varp, varp->valuep(), "Var without value");
|
||||
iterateConst(varp->valuep());
|
||||
putns(varp, ";\n");
|
||||
// Keep track of stats
|
||||
|
||||
@@ -722,6 +722,8 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP
|
||||
return "";
|
||||
} else if (basicp && basicp->isDynamicTriggerScheduler()) {
|
||||
return "";
|
||||
} else if (basicp && basicp->isRandomGenerator()) {
|
||||
return "";
|
||||
} else if (basicp) {
|
||||
const bool zeroit
|
||||
= (varp->attrFileDescr() // Zero so we don't do file IO if never $fopen
|
||||
|
||||
@@ -515,6 +515,7 @@ class EmitCHeader final : public EmitCConstInit {
|
||||
if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n");
|
||||
if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n");
|
||||
if (v3Global.usesTiming()) puts("#include \"verilated_timing.h\"\n");
|
||||
if (v3Global.useRandomizeMethods()) puts("#include \"verilated_random.h\"\n");
|
||||
|
||||
std::set<string> cuse_set;
|
||||
auto add_to_cuse_set = [&](string s) { cuse_set.insert(s); };
|
||||
|
||||
@@ -389,6 +389,7 @@ class EmitCImp final : EmitCFunc {
|
||||
} else if (varp->isParam()) {
|
||||
} else if (varp->isStatic() && varp->isConst()) {
|
||||
} else if (varp->basicp() && varp->basicp()->isTriggerVec()) {
|
||||
} else if (VN_IS(varp->dtypep(), NBACommitQueueDType)) {
|
||||
} else {
|
||||
int vects = 0;
|
||||
AstNodeDType* elementp = varp->dtypeSkipRefp();
|
||||
|
||||
@@ -85,6 +85,9 @@ class CMakeEmitter final {
|
||||
cmake_set(*of, "VERILATOR_ROOT",
|
||||
V3OutFormatter::quoteNameControls(V3Options::getenvVERILATOR_ROOT()), "PATH",
|
||||
"Path to Verilator kit (from $VERILATOR_ROOT)");
|
||||
cmake_set(*of, "VERILATOR_SOLVER",
|
||||
V3OutFormatter::quoteNameControls(V3Options::getenvVERILATOR_SOLVER()), "STRING",
|
||||
"Default SMT solver for constrained randomization (from $VERILATOR_SOLVER)");
|
||||
|
||||
*of << "\n### Compiler flags...\n";
|
||||
|
||||
@@ -160,6 +163,9 @@ class CMakeEmitter final {
|
||||
if (v3Global.usesTiming()) {
|
||||
global.emplace_back("${VERILATOR_ROOT}/include/verilated_timing.cpp");
|
||||
}
|
||||
if (v3Global.useRandomizeMethods()) {
|
||||
global.emplace_back("${VERILATOR_ROOT}/include/verilated_random.cpp");
|
||||
}
|
||||
global.emplace_back("${VERILATOR_ROOT}/include/verilated_threads.cpp");
|
||||
if (v3Global.opt.usesProfiler()) {
|
||||
global.emplace_back("${VERILATOR_ROOT}/include/verilated_profiler.cpp");
|
||||
|
||||
@@ -101,6 +101,12 @@ class EmitCModel final : public EmitCFunc {
|
||||
|
||||
puts("\n");
|
||||
ofp()->putsPrivate(false); // public:
|
||||
|
||||
puts("\n// CONSTEXPR CAPABILITIES\n");
|
||||
puts("// Verilated with --trace?\n");
|
||||
puts("static constexpr bool traceCapable = "s
|
||||
+ (v3Global.opt.trace() ? "true;\n"s : "false;\n"s));
|
||||
|
||||
// User accessible IO
|
||||
puts("\n// PORTS\n"
|
||||
"// The application code writes and reads these signals to\n"
|
||||
|
||||
@@ -106,6 +106,8 @@ public:
|
||||
}
|
||||
if (v3Global.usesProbDist()) putMakeClassEntry(of, "verilated_probdist.cpp");
|
||||
if (v3Global.usesTiming()) putMakeClassEntry(of, "verilated_timing.cpp");
|
||||
if (v3Global.useRandomizeMethods())
|
||||
putMakeClassEntry(of, "verilated_random.cpp");
|
||||
putMakeClassEntry(of, "verilated_threads.cpp");
|
||||
if (v3Global.opt.usesProfiler()) {
|
||||
putMakeClassEntry(of, "verilated_profiler.cpp");
|
||||
@@ -187,6 +189,10 @@ public:
|
||||
|
||||
of.puts("# User CFLAGS (from -CFLAGS on Verilator command line)\n");
|
||||
of.puts("VM_USER_CFLAGS = \\\n");
|
||||
const std::string solver = V3Options::getenvVERILATOR_SOLVER();
|
||||
if (v3Global.useRandomizeMethods() && solver != "")
|
||||
of.puts("\t-DVM_SOLVER_DEFAULT='\"" + V3OutFormatter::quoteNameControls(solver)
|
||||
+ "\"' \\\n");
|
||||
if (!v3Global.opt.libCreate().empty()) of.puts("\t-fPIC \\\n");
|
||||
const V3StringList& cFlags = v3Global.opt.cFlags();
|
||||
for (const string& i : cFlags) of.puts("\t" + i + " \\\n");
|
||||
|
||||
+1
-1
@@ -608,8 +608,8 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
|
||||
puts(";\n");
|
||||
}
|
||||
void visit(AstBasicDType* nodep) override {
|
||||
if (nodep->isSigned()) putfs(nodep, "signed ");
|
||||
putfs(nodep, nodep->prettyName());
|
||||
if (nodep->isSigned()) putfs(nodep, " signed");
|
||||
if (nodep->rangep()) {
|
||||
puts(" ");
|
||||
iterateAndNextConstNull(nodep->rangep());
|
||||
|
||||
+2
-1
@@ -121,6 +121,7 @@ public:
|
||||
MULTITOP, // Multiple top level modules
|
||||
NEWERSTD, // Newer language standard required
|
||||
NOLATCH, // No latch detected in always_latch block
|
||||
NONSTD, // Non-standard feature present in other sims
|
||||
NULLPORT, // Null port detected in module definition
|
||||
PINCONNECTEMPTY,// Cell pin connected by name with empty reference
|
||||
PINMISSING, // Cell pin not specified
|
||||
@@ -204,7 +205,7 @@ public:
|
||||
"IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE",
|
||||
"INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE",
|
||||
"LATCH", "LITENDIAN", "MINTYPMAXDLY", "MISINDENT", "MODDUP",
|
||||
"MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOLATCH", "NULLPORT", "PINCONNECTEMPTY",
|
||||
"MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOLATCH", "NONSTD", "NULLPORT", "PINCONNECTEMPTY",
|
||||
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PREPROCZERO", "PROCASSWIRE",
|
||||
"PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY",
|
||||
"SELRANGE", "SHORTREAL", "SIDEEFFECT", "SPLITVAR",
|
||||
|
||||
+6
-17
@@ -64,15 +64,6 @@ class ForceConvertVisitor final : public VNVisitor {
|
||||
m_rdVarp->addNext(m_enVarp);
|
||||
m_rdVarp->addNext(m_valVarp);
|
||||
varp->addNextHere(m_rdVarp);
|
||||
|
||||
if (varp->isPrimaryIO()) {
|
||||
varp->v3warn(
|
||||
E_UNSUPPORTED,
|
||||
"Unsupported: Force/Release on primary input/output net "
|
||||
<< varp->prettyNameQ() << "\n"
|
||||
<< varp->warnMore()
|
||||
<< "... Suggest assign it to/from a temporary net and force/release that");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -173,13 +164,12 @@ class ForceConvertVisitor final : public VNVisitor {
|
||||
|
||||
void visit(AstAssignForce* nodep) override {
|
||||
// The AstAssignForce node will be removed for sure
|
||||
VNRelinker relinker;
|
||||
nodep->unlinkFrBack(&relinker);
|
||||
pushDeletep(nodep);
|
||||
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstNodeExpr* const lhsp = nodep->lhsp(); // The LValue we are forcing
|
||||
AstNodeExpr* const rhsp = nodep->rhsp(); // The value we are forcing it to
|
||||
VNRelinker relinker;
|
||||
nodep->unlinkFrBack(&relinker);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
|
||||
// Set corresponding enable signals to ones
|
||||
V3Number ones{lhsp, isRangedDType(lhsp) ? lhsp->width() : 1};
|
||||
@@ -208,13 +198,12 @@ class ForceConvertVisitor final : public VNVisitor {
|
||||
}
|
||||
|
||||
void visit(AstRelease* nodep) override {
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstNodeExpr* const lhsp = nodep->lhsp(); // The LValue we are releasing
|
||||
// The AstRelease node will be removed for sure
|
||||
VNRelinker relinker;
|
||||
nodep->unlinkFrBack(&relinker);
|
||||
pushDeletep(nodep);
|
||||
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstNodeExpr* const lhsp = nodep->lhsp(); // The LValue we are releasing
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
|
||||
// Set corresponding enable signals to zero
|
||||
V3Number zero{lhsp, isRangedDType(lhsp) ? lhsp->width() : 1};
|
||||
|
||||
@@ -616,6 +616,7 @@ class ForkVisitor final : public VNVisitor {
|
||||
if (m_forkDepth && !nodep->varp()->isFuncLocal() && nodep->varp()->isClassMember()) return;
|
||||
|
||||
if (m_forkDepth && (m_forkLocalsp.count(nodep->varp()) == 0)
|
||||
&& nodep->varp()->varType() != VVarType::PORT // Basically static, so it's safe
|
||||
&& !nodep->varp()->lifetime().isStatic()) {
|
||||
if (nodep->access().isWriteOrRW()
|
||||
&& (!nodep->isClassHandleValue() || nodep->user2())) {
|
||||
|
||||
+9
-2
@@ -425,8 +425,8 @@ class GateClkDecomp final {
|
||||
void visit(GateVarVertex* vVtxp, int offset) {
|
||||
AstVarScope* const vscp = vVtxp->varScp();
|
||||
|
||||
// Can't propagate if this variable is forceable
|
||||
if (vscp->varp()->isForceable()) return;
|
||||
// Can't propagate if this variable might be forced
|
||||
if (vscp->varp()->isForced()) return;
|
||||
|
||||
// Check that we haven't been here before
|
||||
if (vscp->user2SetOnce()) return;
|
||||
@@ -656,6 +656,9 @@ public:
|
||||
return m_substitutionp;
|
||||
}
|
||||
const std::vector<AstVarScope*>& readVscps() const { return m_readVscps; }
|
||||
bool varAssigned(const AstVarScope* scopep) const {
|
||||
return m_lhsVarRef && (m_lhsVarRef->varScopep() == scopep);
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -772,6 +775,8 @@ class GateInline final {
|
||||
|
||||
// Was it ok?
|
||||
if (!okVisitor.isSimple()) continue;
|
||||
// If the varScope is already removed from logicp, no need to try substitution.
|
||||
if (!okVisitor.varAssigned(vVtxp->varScp())) continue;
|
||||
|
||||
// Does it read multiple source variables?
|
||||
if (okVisitor.readVscps().size() > 1) {
|
||||
@@ -822,6 +827,8 @@ class GateInline final {
|
||||
|
||||
if (debug() >= 9) dstVtxp->nodep()->dumpTree(" inside: ");
|
||||
|
||||
UASSERT_OBJ(logicp != dstVtxp->nodep(), logicp,
|
||||
"Circular logic should have been rejected by okVisitor");
|
||||
recordSubstitution(vscp, substp, dstVtxp->nodep());
|
||||
|
||||
// If the new replacement referred to a signal,
|
||||
|
||||
+2
-4
@@ -98,8 +98,6 @@ static void V3HierWriteCommonInputs(const V3HierBlock* hblockp, std::ostream* of
|
||||
if (hblockp) topModuleFile = hblockp->vFileIfNecessary();
|
||||
if (!forCMake) {
|
||||
if (!topModuleFile.empty()) *of << topModuleFile << "\n";
|
||||
const V3StringList& vFiles = v3Global.opt.vFiles();
|
||||
for (const string& i : vFiles) *of << i << "\n";
|
||||
}
|
||||
const V3StringSet& libraryFiles = v3Global.opt.libraryFiles();
|
||||
for (const string& i : libraryFiles) {
|
||||
@@ -227,7 +225,7 @@ void V3HierBlock::writeCommandArgsFile(bool forCMake) const {
|
||||
for (const string& opt : commandOpts) *of << opt << "\n";
|
||||
*of << hierBlockArgs().front() << "\n";
|
||||
for (const auto& hierblockp : m_children) *of << hierblockp->hierBlockArgs().front() << "\n";
|
||||
*of << v3Global.opt.allArgsStringForHierBlock(false) << "\n";
|
||||
*of << v3Global.opt.allArgsStringForHierBlock(false, forCMake) << "\n";
|
||||
}
|
||||
|
||||
string V3HierBlock::commandArgsFileName(bool forCMake) const {
|
||||
@@ -424,7 +422,7 @@ void V3HierBlockPlan::writeCommandArgsFiles(bool forCMake) const {
|
||||
}
|
||||
*of << "--threads " << cvtToStr(v3Global.opt.threads()) << "\n";
|
||||
*of << (v3Global.opt.systemC() ? "--sc" : "--cc") << "\n";
|
||||
*of << v3Global.opt.allArgsStringForHierBlock(true) << "\n";
|
||||
*of << v3Global.opt.allArgsStringForHierBlock(true, forCMake) << "\n";
|
||||
}
|
||||
|
||||
string V3HierBlockPlan::topCommandArgsFileName(bool forCMake) {
|
||||
|
||||
+13
-3
@@ -320,8 +320,16 @@ class InlineRelinkVisitor final : public VNVisitor {
|
||||
// module, so a AstVarRef not AstVarXRef below
|
||||
exprvarrefp = exprvarrefp->cloneTree(false);
|
||||
exprvarrefp->access(VAccess::READ);
|
||||
m_modp->addStmtsp(new AstAssignAlias{
|
||||
flp, new AstVarRef{flp, nodep, VAccess::WRITE}, exprvarrefp});
|
||||
AstVarRef* const nodeVarRefp = new AstVarRef{flp, nodep, VAccess::WRITE};
|
||||
if (nodep->isForced() && nodep->direction() == VDirection::INPUT) {
|
||||
m_modp->addStmtsp(new AstAssignW{flp, nodeVarRefp, exprvarrefp});
|
||||
} else if (nodep->isForced() && nodep->direction() == VDirection::OUTPUT) {
|
||||
exprvarrefp->access(VAccess::WRITE);
|
||||
nodeVarRefp->access(VAccess::READ);
|
||||
m_modp->addStmtsp(new AstAssignW{flp, exprvarrefp, nodeVarRefp});
|
||||
} else {
|
||||
m_modp->addStmtsp(new AstAssignAlias{flp, nodeVarRefp, exprvarrefp});
|
||||
}
|
||||
FileLine* const flbp = exprvarrefp->varp()->fileline();
|
||||
flp->modifyStateInherit(flbp);
|
||||
flbp->modifyStateInherit(flp);
|
||||
@@ -370,7 +378,9 @@ class InlineRelinkVisitor final : public VNVisitor {
|
||||
if (nodep->varp()->user2p() // It's being converted to an alias.
|
||||
&& !nodep->varp()->user3()
|
||||
// Don't constant propagate aliases (we just made)
|
||||
&& !VN_IS(nodep->backp(), AssignAlias)) {
|
||||
&& !VN_IS(nodep->backp(), AssignAlias)
|
||||
// Forced signals do not use aliases
|
||||
&& !nodep->varp()->isForced()) {
|
||||
AstVar* const varp = nodep->varp();
|
||||
if (AstConst* const constp = VN_CAST(varp->user2p(), Const)) {
|
||||
nodep->replaceWith(constp->cloneTree(false));
|
||||
|
||||
+1
-1
@@ -463,7 +463,7 @@ private:
|
||||
pushDeletep(pinVarp);
|
||||
} // else pinVarp already unlinked when another instance did this step
|
||||
nodep->replaceWith(prevPinp);
|
||||
pushDeletep(nodep);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-4
@@ -53,10 +53,7 @@ public:
|
||||
~LifeState() {
|
||||
V3Stats::addStatSum("Optimizations, Lifetime assign deletions", m_statAssnDel);
|
||||
V3Stats::addStatSum("Optimizations, Lifetime constant prop", m_statAssnCon);
|
||||
for (AstNode* ip : m_unlinkps) {
|
||||
ip->unlinkFrBack();
|
||||
ip->deleteTree();
|
||||
}
|
||||
for (AstNode* ip : m_unlinkps) VL_DO_DANGLING(ip->unlinkFrBack()->deleteTree(), ip);
|
||||
}
|
||||
// METHODS
|
||||
void pushUnlinkDeletep(AstNode* nodep) { m_unlinkps.push_back(nodep); }
|
||||
|
||||
+1
-1
@@ -273,7 +273,7 @@ class LinkCellsVisitor final : public VNVisitor {
|
||||
iterateAndNextNull(cellsp);
|
||||
}
|
||||
}
|
||||
pushDeletep(nodep->unlinkFrBack());
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
|
||||
void visit(AstCell* nodep) override {
|
||||
|
||||
+78
-10
@@ -1211,6 +1211,23 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||
foundp = m_modSymp; // Conflicts with modname?
|
||||
}
|
||||
AstVar* const findvarp = foundp ? VN_CAST(foundp->nodep(), Var) : nullptr;
|
||||
// clocking items can have duplicate names (inout)
|
||||
if (findvarp && VN_IS(findvarp->backp(), ClockingItem)
|
||||
&& VN_IS(nodep->backp(), ClockingItem)) {
|
||||
AstClockingItem* const itemp = VN_AS(nodep->backp(), ClockingItem);
|
||||
AstClockingItem* const finditemp = VN_AS(findvarp->backp(), ClockingItem);
|
||||
UINFO(4, "ClockCompl: " << itemp << " ;; " << finditemp << endl);
|
||||
UINFO(4, "ClockCompV: " << nodep << " ;; " << findvarp << endl);
|
||||
if (*itemp->exprp()->fileline() == *finditemp->exprp()->fileline()) {
|
||||
UASSERT_OBJ(finditemp->direction() == VDirection::INPUT
|
||||
&& itemp->direction() == VDirection::OUTPUT,
|
||||
itemp, "Input after output?");
|
||||
// pretend nothing found and rename
|
||||
foundp = nullptr;
|
||||
nodep->name("__Voutput_" + nodep->name());
|
||||
finditemp->outputp(itemp);
|
||||
}
|
||||
}
|
||||
bool ins = false;
|
||||
if (!foundp) {
|
||||
ins = true;
|
||||
@@ -1504,11 +1521,11 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||
VL_DO_DANGLING(argp->unlinkFrBackWithNext()->deleteTree(), argp);
|
||||
}
|
||||
// Type depends on the method used, let V3Width figure it out later
|
||||
if (nodep->exprp()) { // Else empty expression and pretend no "with"
|
||||
if (nodep->exprsp()) { // Else empty expression and pretend no "with"
|
||||
const auto indexArgRefp = new AstLambdaArgRef{argFl, name + "__DOT__index", true};
|
||||
const auto valueArgRefp = new AstLambdaArgRef{argFl, name, false};
|
||||
const auto newp = new AstWith{nodep->fileline(), indexArgRefp, valueArgRefp,
|
||||
nodep->exprp()->unlinkFrBackWithNext()};
|
||||
nodep->exprsp()->unlinkFrBackWithNext()};
|
||||
funcrefp->addPinsp(newp);
|
||||
}
|
||||
nodep->replaceWith(funcrefp->unlinkFrBack());
|
||||
@@ -1528,6 +1545,7 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||
// Insert argref's name into symbol table
|
||||
m_statep->insertSym(m_curSymp, nodep->valueArgRefp()->name(), nodep->valueArgRefp(),
|
||||
nullptr);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2181,6 +2199,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
AstVar* const eventp = new AstVar{
|
||||
clockingp->fileline(), VVarType::MODULETEMP, clockingp->name(), VFlagChildDType{},
|
||||
new AstBasicDType{clockingp->fileline(), VBasicDTypeKwd::EVENT}};
|
||||
eventp->lifetime(VLifetime::STATIC);
|
||||
clockingp->eventp(eventp);
|
||||
// Trigger the clocking event in Observed (IEEE 1800-2023 14.13)
|
||||
clockingp->addNextHere(new AstAlwaysObserved{
|
||||
@@ -2195,16 +2214,21 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
return reinterpret_cast<VSymEnt*>(clockingp->eventp()->user1p());
|
||||
}
|
||||
|
||||
bool isParamedClassRefDType(const AstNode* classp) {
|
||||
while (const AstRefDType* const refp = VN_CAST(classp, RefDType))
|
||||
classp = refp->subDTypep();
|
||||
return (VN_IS(classp, ClassRefDType) && VN_AS(classp, ClassRefDType)->paramsp())
|
||||
|| VN_IS(classp, ParamTypeDType);
|
||||
}
|
||||
bool isParamedClassRef(const AstNode* nodep) {
|
||||
// Is this a parameterized reference to a class, or a reference to class parameter
|
||||
if (const auto* classRefp = VN_CAST(nodep, ClassOrPackageRef)) {
|
||||
if (classRefp->paramsp()) return true;
|
||||
const auto* classp = classRefp->classOrPackageNodep();
|
||||
while (const auto* typedefp = VN_CAST(classp, Typedef)) classp = typedefp->subDTypep();
|
||||
return (VN_IS(classp, ClassRefDType) && VN_AS(classp, ClassRefDType)->paramsp())
|
||||
|| VN_IS(classp, ParamTypeDType);
|
||||
return isParamedClassRefDType(classp);
|
||||
}
|
||||
return false;
|
||||
return isParamedClassRefDType(nodep);
|
||||
}
|
||||
VSymEnt* getThisClassSymp() {
|
||||
VSymEnt* classSymp = m_ds.m_dotSymp;
|
||||
@@ -2240,9 +2264,9 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
<< "... Location of interface class's function\n"
|
||||
<< interfaceSubp->warnContextSecondary());
|
||||
}
|
||||
const auto it = m_ifClassImpNames.find(interfaceSubp->name());
|
||||
if (!existsInChild && it != m_ifClassImpNames.end()
|
||||
&& it->second != interfaceSubp) { // Not exact same function from diamond
|
||||
const auto itn = m_ifClassImpNames.find(interfaceSubp->name());
|
||||
if (!existsInChild && itn != m_ifClassImpNames.end()
|
||||
&& itn->second != interfaceSubp) { // Not exact same function from diamond
|
||||
implementsClassp->v3error(
|
||||
"Class " << implementsClassp->prettyNameQ() << impOrExtends
|
||||
<< baseClassp->prettyNameQ()
|
||||
@@ -2619,7 +2643,16 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
= VN_AS(m_ds.m_dotp->lhsp(), ClassOrPackageRef);
|
||||
classOrPackagep = cpackagerefp->classOrPackagep();
|
||||
UASSERT_OBJ(classOrPackagep, m_ds.m_dotp->lhsp(), "Bad package link");
|
||||
m_ds.m_dotSymp = m_statep->getNodeSym(classOrPackagep);
|
||||
if (cpackagerefp->name() == "local::") {
|
||||
if (m_pinSymp) {
|
||||
m_ds.m_dotSymp = m_curSymp->fallbackp();
|
||||
} else {
|
||||
nodep->v3error("Illegal 'local::' outside 'randomize() with'");
|
||||
m_ds.m_dotErr = true;
|
||||
}
|
||||
} else {
|
||||
m_ds.m_dotSymp = m_statep->getNodeSym(classOrPackagep);
|
||||
}
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
} else if (m_ds.m_dotPos == DP_SCOPE) {
|
||||
// {a}.{b}, where {a} maybe a module name
|
||||
@@ -3078,8 +3111,42 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
void visit(AstMethodCall* nodep) override {
|
||||
// Created here so should already be resolved.
|
||||
VL_RESTORER(m_ds);
|
||||
VL_RESTORER(m_pinSymp);
|
||||
{
|
||||
m_ds.init(m_curSymp);
|
||||
if (nodep->name() == "randomize" && VN_IS(nodep->pinsp(), With)) {
|
||||
const AstNodeDType* fromDtp = nodep->fromp()->dtypep();
|
||||
if (!fromDtp) {
|
||||
if (const AstNodeVarRef* const varRefp = VN_CAST(nodep->fromp(), NodeVarRef)) {
|
||||
fromDtp = varRefp->varp()->subDTypep();
|
||||
} else if (const AstNodeSel* const selp = VN_CAST(nodep->fromp(), NodeSel)) {
|
||||
if (const AstNodeVarRef* const varRefp
|
||||
= VN_CAST(selp->fromp(), NodeVarRef)) {
|
||||
fromDtp = varRefp->varp()->dtypeSkipRefp()->subDTypep();
|
||||
}
|
||||
} else if (const AstNodePreSel* const selp
|
||||
= VN_CAST(nodep->fromp(), NodePreSel)) {
|
||||
if (const AstNodeVarRef* const varRefp
|
||||
= VN_CAST(selp->fromp(), NodeVarRef)) {
|
||||
fromDtp = varRefp->varp()->dtypeSkipRefp()->subDTypep();
|
||||
}
|
||||
}
|
||||
if (!fromDtp)
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: 'randomize() with' on complex expressions");
|
||||
}
|
||||
if (m_statep->forPrimary() && isParamedClassRefDType(fromDtp)) {
|
||||
m_ds.m_unresolvedClass = true;
|
||||
} else if (fromDtp) {
|
||||
const AstClassRefDType* const classDtp
|
||||
= VN_CAST(fromDtp->skipRefp(), ClassRefDType);
|
||||
if (!classDtp)
|
||||
nodep->v3error("'randomize() with' on a non-class-instance "
|
||||
<< fromDtp->prettyNameQ());
|
||||
else
|
||||
m_pinSymp = m_statep->getNodeSym(classDtp->classp());
|
||||
}
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
@@ -3095,7 +3162,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
if (nodep->user3SetOnce()) return;
|
||||
UINFO(8, " " << nodep << endl);
|
||||
UINFO(8, " " << m_ds.ascii() << endl);
|
||||
{
|
||||
if (m_ds.m_dotPos != DP_MEMBER || nodep->name() != "randomize") {
|
||||
// Visit arguments at the beginning.
|
||||
// They may be visitted even if the current node can't be linked now.
|
||||
VL_RESTORER(m_ds);
|
||||
@@ -3439,6 +3506,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
VL_RESTORER(m_curSymp);
|
||||
{
|
||||
m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep);
|
||||
if (m_pinSymp) m_curSymp->importFromClass(m_statep->symsp(), m_pinSymp);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_ds.m_dotSymp = VL_RESTORER_PREV(m_curSymp);
|
||||
|
||||
+2
-1
@@ -341,7 +341,8 @@ class LinkJumpVisitor final : public VNVisitor {
|
||||
}
|
||||
// if (debug() >= 9) { UINFO(0, "\n"); blockp->dumpTree("- labeli: "); }
|
||||
if (!blockp) {
|
||||
nodep->v3error("disable isn't underneath a begin with name: " << nodep->prettyNameQ());
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"disable isn't underneath a begin with name: " << nodep->prettyNameQ());
|
||||
} else if (AstBegin* const beginp = VN_CAST(blockp, Begin)) {
|
||||
// Jump to the end of the named block
|
||||
AstJumpLabel* const labelp = findAddLabel(beginp, false);
|
||||
|
||||
+33
-19
@@ -22,6 +22,8 @@
|
||||
|
||||
#include "V3LinkLValue.h"
|
||||
|
||||
#include "V3Task.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
@@ -33,6 +35,7 @@ class LinkLValueVisitor final : public VNVisitor {
|
||||
// STATE
|
||||
bool m_setContinuously = false; // Set that var has some continuous assignment
|
||||
bool m_setStrengthSpecified = false; // Set that var has assignment with strength specified.
|
||||
bool m_setForcedByCode = false; // Set that var is the target of an AstAssignForce/AstRelease
|
||||
VAccess m_setRefLvalue; // Set VarRefs to lvalues for pin assignments
|
||||
|
||||
// VISITs
|
||||
@@ -40,15 +43,20 @@ class LinkLValueVisitor final : public VNVisitor {
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
// VarRef: LValue its reference
|
||||
if (m_setRefLvalue != VAccess::NOCHANGE) nodep->access(m_setRefLvalue);
|
||||
if (nodep->varp()) {
|
||||
if (nodep->access().isWriteOrRW() && m_setContinuously) {
|
||||
if (nodep->varp() && nodep->access().isWriteOrRW()) {
|
||||
if (m_setContinuously) {
|
||||
nodep->varp()->isContinuously(true);
|
||||
// Strength may only be specified in continuous assignment,
|
||||
// so it is needed to check only if m_setContinuously is true
|
||||
if (m_setStrengthSpecified) nodep->varp()->hasStrengthAssignment(true);
|
||||
}
|
||||
if (nodep->access().isWriteOrRW() && !nodep->varp()->isFuncLocal()
|
||||
&& nodep->varp()->isReadOnly()) {
|
||||
if (const AstClockingItem* itemp = VN_CAST(nodep->varp()->backp(), ClockingItem)) {
|
||||
UINFO(5, "ClkOut " << nodep << endl);
|
||||
if (itemp->outputp()) nodep->varp(itemp->outputp()->varp());
|
||||
}
|
||||
if (m_setForcedByCode) {
|
||||
nodep->varp()->setForcedByCode();
|
||||
} else if (!nodep->varp()->isFuncLocal() && nodep->varp()->isReadOnly()) {
|
||||
nodep->v3warn(ASSIGNIN,
|
||||
"Assigning to input/const variable: " << nodep->prettyNameQ());
|
||||
}
|
||||
@@ -77,7 +85,11 @@ class LinkLValueVisitor final : public VNVisitor {
|
||||
if (AstAssignW* assignwp = VN_CAST(nodep, AssignW)) {
|
||||
if (assignwp->strengthSpecp()) m_setStrengthSpecified = true;
|
||||
}
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
{
|
||||
VL_RESTORER(m_setForcedByCode);
|
||||
m_setForcedByCode = VN_IS(nodep, AssignForce);
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
}
|
||||
m_setRefLvalue = VAccess::NOCHANGE;
|
||||
m_setContinuously = false;
|
||||
m_setStrengthSpecified = false;
|
||||
@@ -87,9 +99,11 @@ class LinkLValueVisitor final : public VNVisitor {
|
||||
void visit(AstRelease* nodep) override {
|
||||
VL_RESTORER(m_setRefLvalue);
|
||||
VL_RESTORER(m_setContinuously);
|
||||
VL_RESTORER(m_setForcedByCode);
|
||||
{
|
||||
m_setRefLvalue = VAccess::WRITE;
|
||||
m_setContinuously = false;
|
||||
m_setForcedByCode = true;
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
}
|
||||
}
|
||||
@@ -279,23 +293,23 @@ class LinkLValueVisitor final : public VNVisitor {
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNodeFTaskRef* nodep) override {
|
||||
AstNode* pinp = nodep->pinsp();
|
||||
const AstNodeFTask* const taskp = nodep->taskp();
|
||||
// We'll deal with mismatching pins later
|
||||
if (!taskp) return;
|
||||
for (AstNode* stmtp = taskp->stmtsp(); stmtp && pinp; stmtp = stmtp->nextp()) {
|
||||
if (const AstVar* const portp = VN_CAST(stmtp, Var)) {
|
||||
if (portp->isIO()) {
|
||||
if (portp->isWritable()) {
|
||||
m_setRefLvalue = VAccess::WRITE;
|
||||
iterate(pinp);
|
||||
m_setRefLvalue = VAccess::NOCHANGE;
|
||||
} else {
|
||||
iterate(pinp);
|
||||
}
|
||||
// Advance pin
|
||||
pinp = pinp->nextp();
|
||||
}
|
||||
const V3TaskConnects tconnects
|
||||
= V3Task::taskConnects(nodep, taskp->stmtsp(), nullptr, false);
|
||||
for (const auto& tconnect : tconnects) {
|
||||
const AstVar* const portp = tconnect.first;
|
||||
const AstArg* const argp = tconnect.second;
|
||||
if (!argp) continue;
|
||||
AstNodeExpr* const pinp = argp->exprp();
|
||||
if (!pinp) continue;
|
||||
if (portp->isWritable()) {
|
||||
m_setRefLvalue = VAccess::WRITE;
|
||||
iterate(pinp);
|
||||
m_setRefLvalue = VAccess::NOCHANGE;
|
||||
} else {
|
||||
iterate(pinp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -874,7 +874,7 @@ class LinkParseVisitor final : public VNVisitor {
|
||||
itemp->v3fatalSrc("Incorrect direction");
|
||||
}
|
||||
}
|
||||
pushDeletep(itemp->unlinkFrBack());
|
||||
VL_DO_DANGLING(pushDeletep(itemp->unlinkFrBack()), itemp);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
+5
-5
@@ -81,7 +81,7 @@ class V3List final {
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
static void prefetch(T_Base* elementp, T_Base* fallbackp) {
|
||||
static void prefetch(const T_Base* elementp, const T_Base* fallbackp) {
|
||||
UDEBUGONLY(UASSERT(fallbackp, "Prefetch fallback pointer must be non nullptr"););
|
||||
// This compiles to a branchless prefetch with cmove, with the address always valid
|
||||
VL_PREFETCH_RW(elementp ? elementp : fallbackp);
|
||||
@@ -104,7 +104,7 @@ class V3List final {
|
||||
T_Base* m_currp; // Currently iterated element, or 'nullptr' for 'end()' iterator
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
SimpleItertatorImpl(T_Base* elementp)
|
||||
explicit SimpleItertatorImpl(T_Base* elementp)
|
||||
: m_currp{elementp} {}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
@@ -159,7 +159,7 @@ class V3List final {
|
||||
|
||||
ListType& m_list; // The proxied list
|
||||
|
||||
UnlinkableProxy(ListType& list)
|
||||
explicit UnlinkableProxy(ListType& list)
|
||||
: m_list{list} {}
|
||||
|
||||
// Unlinkable iterator class template. This only supports enough for range based for loops.
|
||||
@@ -179,11 +179,11 @@ class V3List final {
|
||||
T_Base* m_nextp; // Next element after current, or 'nullptr' for 'end()' iterator
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
UnlinkableItertatorImpl(T_Base* elementp)
|
||||
explicit UnlinkableItertatorImpl(T_Base* elementp)
|
||||
: m_currp{elementp}
|
||||
, m_nextp{toLinks(m_currp).m_nextp} {}
|
||||
VL_ATTR_ALWINLINE
|
||||
UnlinkableItertatorImpl(std::nullptr_t)
|
||||
explicit UnlinkableItertatorImpl(std::nullptr_t)
|
||||
: m_currp{nullptr}
|
||||
, m_nextp{nullptr} {}
|
||||
|
||||
|
||||
+8
-3
@@ -63,9 +63,14 @@ class LocalizeVisitor final : public VNVisitor {
|
||||
|
||||
// METHODS
|
||||
bool isOptimizable(AstVarScope* nodep) {
|
||||
return !nodep->user1() || // Not marked as not optimizable, or ...
|
||||
(nodep->varp()->varType() == VVarType::BLOCKTEMP
|
||||
&& m_accessors(nodep).size() == 1); // .. a block temp used in a single CFunc
|
||||
// Don't want to malloc/free the backing store all the time
|
||||
if (VN_IS(nodep->dtypep(), NBACommitQueueDType)) return false;
|
||||
return ((!nodep->user1() // Not marked as not optimizable, or ...
|
||||
// .. a block temp used in a single CFunc
|
||||
|| (nodep->varp()->varType() == VVarType::BLOCKTEMP
|
||||
&& m_accessors(nodep).size() == 1))
|
||||
// and under size limit
|
||||
&& nodep->varp()->dtypep()->widthTotalBytes() <= v3Global.opt.localizeMaxSize());
|
||||
}
|
||||
|
||||
static bool existsNonLeaf(const std::unordered_set<AstCFunc*>& funcps) {
|
||||
|
||||
+1
-1
@@ -1891,7 +1891,7 @@ V3Number& V3Number::opSub(const V3Number& lhs, const V3Number& rhs) {
|
||||
NUM_ASSERT_OP_ARGS2(lhs, rhs);
|
||||
NUM_ASSERT_LOGIC_ARGS2(lhs, rhs);
|
||||
if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX();
|
||||
V3Number negrhs(&rhs, rhs.width());
|
||||
V3Number negrhs(&rhs, width());
|
||||
negrhs.opNegate(rhs);
|
||||
return opAdd(lhs, negrhs);
|
||||
}
|
||||
|
||||
+31
-8
@@ -67,6 +67,7 @@ public:
|
||||
using DirMap = std::map<const string, std::set<std::string>>; // Directory listing
|
||||
|
||||
// STATE
|
||||
std::list<string> m_lineArgs; // List of command line argument encountered
|
||||
std::list<string> m_allArgs; // List of every argument encountered
|
||||
std::list<string> m_incDirUsers; // Include directories (ordered)
|
||||
std::set<string> m_incDirUserSet; // Include directories (for removing duplicates)
|
||||
@@ -393,6 +394,8 @@ void V3Options::addVFile(const string& filename) {
|
||||
}
|
||||
void V3Options::addForceInc(const string& filename) { m_forceIncs.push_back(filename); }
|
||||
|
||||
void V3Options::addLineArg(const string& arg) { m_impp->m_lineArgs.push_back(arg); }
|
||||
|
||||
void V3Options::addArg(const string& arg) { m_impp->m_allArgs.push_back(arg); }
|
||||
|
||||
string V3Options::allArgsString() const VL_MT_SAFE {
|
||||
@@ -405,12 +408,12 @@ string V3Options::allArgsString() const VL_MT_SAFE {
|
||||
}
|
||||
|
||||
// Delete some options for Verilation of the hierarchical blocks.
|
||||
string V3Options::allArgsStringForHierBlock(bool forTop) const {
|
||||
string V3Options::allArgsStringForHierBlock(bool forTop, bool forCMake) const {
|
||||
std::set<string> vFiles;
|
||||
for (const auto& vFile : m_vFiles) vFiles.insert(vFile);
|
||||
string out;
|
||||
for (std::list<string>::const_iterator it = m_impp->m_allArgs.begin();
|
||||
it != m_impp->m_allArgs.end(); ++it) {
|
||||
for (std::list<string>::const_iterator it = m_impp->m_lineArgs.begin();
|
||||
it != m_impp->m_lineArgs.end(); ++it) {
|
||||
int skip = 0;
|
||||
if (it->length() >= 2 && (*it)[0] == '-' && (*it)[1] == '-') {
|
||||
skip = 2;
|
||||
@@ -426,7 +429,7 @@ string V3Options::allArgsStringForHierBlock(bool forTop) const {
|
||||
continue;
|
||||
}
|
||||
} else { // Not an option
|
||||
if (vFiles.find(*it) != vFiles.end() // Remove HDL
|
||||
if ((forCMake && vFiles.find(*it) != vFiles.end()) // Remove HDL
|
||||
|| m_cppFiles.find(*it) != m_cppFiles.end()) { // Remove C++
|
||||
continue;
|
||||
}
|
||||
@@ -493,7 +496,10 @@ string V3Options::fileExists(const string& filename) {
|
||||
try {
|
||||
for (const auto& dirEntry : std::filesystem::directory_iterator(dir.c_str()))
|
||||
setp->insert(dirEntry.path().filename().string());
|
||||
} catch (std::filesystem::filesystem_error const& ex) { return ""; }
|
||||
} catch (std::filesystem::filesystem_error const& ex) {
|
||||
(void)ex;
|
||||
return "";
|
||||
}
|
||||
#else
|
||||
if (DIR* const dirp = opendir(dir.c_str())) {
|
||||
while (struct dirent* direntp = readdir(dirp)) setp->insert(direntp->d_name);
|
||||
@@ -750,6 +756,18 @@ string V3Options::getenvVERILATOR_ROOT() {
|
||||
return V3Os::filenameCleanup(var);
|
||||
}
|
||||
|
||||
string V3Options::getenvVERILATOR_SOLVER() {
|
||||
string var = V3Os::getenvStr("VERILATOR_SOLVER", "");
|
||||
// Treat compiled-in DEFENV string literals as C-strings to enable
|
||||
// binary patching for relocatable installs (e.g. conda)
|
||||
string defenv = string{DEFENV_VERILATOR_SOLVER}.c_str();
|
||||
if (var == "" && defenv != "") {
|
||||
var = defenv;
|
||||
V3Os::setenvStr("VERILATOR_SOLVER", var, "Hardcoded at build time");
|
||||
}
|
||||
return var;
|
||||
}
|
||||
|
||||
string V3Options::getStdPackagePath() {
|
||||
return V3Os::filenameJoin(getenvVERILATOR_ROOT(), "include", "verilated_std.sv");
|
||||
}
|
||||
@@ -988,6 +1006,9 @@ string V3Options::argString(int argc, char** argv) {
|
||||
// V3 Options Parsing
|
||||
|
||||
void V3Options::parseOpts(FileLine* fl, int argc, char** argv) VL_MT_DISABLED {
|
||||
// Save command line options
|
||||
for (int i = 0; i < argc; ++i) { addLineArg(argv[i]); }
|
||||
|
||||
// Parse all options
|
||||
// Initial entry point from Verilator.cpp
|
||||
parseOptsList(fl, ".", argc, argv);
|
||||
@@ -1307,6 +1328,9 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
|
||||
});
|
||||
|
||||
DECL_OPTION("-LDFLAGS", CbVal, callStrSetter(&V3Options::addLdLibs));
|
||||
DECL_OPTION("-l2-name", Set, &m_l2Name);
|
||||
DECL_OPTION("-no-l2name", CbCall, [this]() { m_l2Name = ""; }).undocumented(); // Historical
|
||||
DECL_OPTION("-l2name", CbCall, [this]() { m_l2Name = "v"; }).undocumented(); // Historical
|
||||
const auto setLang = [this, fl](const char* valp) {
|
||||
const V3LangCode optval{valp};
|
||||
if (optval.legal()) {
|
||||
@@ -1323,9 +1347,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
|
||||
DECL_OPTION("-language", CbVal, setLang);
|
||||
DECL_OPTION("-lib-create", Set, &m_libCreate);
|
||||
DECL_OPTION("-lint-only", OnOff, &m_lintOnly);
|
||||
DECL_OPTION("-l2-name", Set, &m_l2Name);
|
||||
DECL_OPTION("-no-l2name", CbCall, [this]() { m_l2Name = ""; }).undocumented(); // Historical
|
||||
DECL_OPTION("-l2name", CbCall, [this]() { m_l2Name = "v"; }).undocumented(); // Historical
|
||||
DECL_OPTION("-localize-max-size", Set, &m_localizeMaxSize);
|
||||
DECL_OPTION("-main-top-name", Set, &m_mainTopName);
|
||||
|
||||
DECL_OPTION("-MAKEFLAGS", CbVal, callStrSetter(&V3Options::addMakeFlags));
|
||||
@@ -1388,6 +1410,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
|
||||
m_pinsScBigUint = flag;
|
||||
m_pinsBv = 513;
|
||||
});
|
||||
DECL_OPTION("-pins-inout-enables", OnOff, &m_pinsInoutEnables);
|
||||
DECL_OPTION("-pins-uint8", OnOff, &m_pinsUint8);
|
||||
DECL_OPTION("-pipe-filter", Set, &m_pipeFilter);
|
||||
DECL_OPTION("-pp-comments", OnOff, &m_ppComments);
|
||||
|
||||
+7
-1
@@ -258,6 +258,7 @@ private:
|
||||
bool m_main = false; // main switch: --main
|
||||
bool m_outFormatOk = false; // main switch: --cc, --sc or --sp was specified
|
||||
bool m_pedantic = false; // main switch: --Wpedantic
|
||||
bool m_pinsInoutEnables = false;// main switch: --pins-inout-enables
|
||||
bool m_pinsScUint = false; // main switch: --pins-sc-uint
|
||||
bool m_pinsScBigUint = false; // main switch: --pins-sc-biguint
|
||||
bool m_pinsUint8 = false; // main switch: --pins-uint8
|
||||
@@ -308,6 +309,7 @@ private:
|
||||
int m_instrCountDpi = 200; // main switch: --instr-count-dpi
|
||||
bool m_jsonEditNums = true; // main switch: --no-json-edit-nums
|
||||
bool m_jsonIds = true; // main switch: --no-json-ids
|
||||
int m_localizeMaxSize = 1024; // main switch: --localize-max-size
|
||||
VOptionBool m_makeDepend; // main switch: -MMD
|
||||
int m_maxNumWidth = 65536; // main switch: --max-num-width
|
||||
int m_moduleRecursion = 100; // main switch: --module-recursion-depth
|
||||
@@ -398,6 +400,7 @@ private:
|
||||
|
||||
private:
|
||||
// METHODS
|
||||
void addLineArg(const string& arg);
|
||||
void addArg(const string& arg);
|
||||
void addDefine(const string& defline, bool allowPlus) VL_MT_DISABLED;
|
||||
void addFuture(const string& flag);
|
||||
@@ -509,6 +512,7 @@ public:
|
||||
bool outFormatOk() const { return m_outFormatOk; }
|
||||
bool keepTempFiles() const { return (V3Error::debugDefault() != 0); }
|
||||
bool pedantic() const { return m_pedantic; }
|
||||
bool pinsInoutEnables() const { return m_pinsInoutEnables; }
|
||||
bool pinsScUint() const { return m_pinsScUint; }
|
||||
bool pinsScBigUint() const { return m_pinsScBigUint; }
|
||||
bool pinsUint8() const { return m_pinsUint8; }
|
||||
@@ -544,6 +548,7 @@ public:
|
||||
int ifDepth() const { return m_ifDepth; }
|
||||
int inlineMult() const { return m_inlineMult; }
|
||||
int instrCountDpi() const { return m_instrCountDpi; }
|
||||
int localizeMaxSize() const { return m_localizeMaxSize; }
|
||||
bool jsonEditNums() const { return m_jsonEditNums; }
|
||||
bool jsonIds() const { return m_jsonIds; }
|
||||
VOptionBool makeDepend() const { return m_makeDepend; }
|
||||
@@ -695,7 +700,7 @@ public:
|
||||
string allArgsString() const VL_MT_SAFE; ///< Return all passed arguments as simple string
|
||||
// Return options for child hierarchical blocks when forTop==false, otherwise returns args for
|
||||
// the top module.
|
||||
string allArgsStringForHierBlock(bool forTop) const;
|
||||
string allArgsStringForHierBlock(bool forTop, bool forCMake) const;
|
||||
void parseOpts(FileLine* fl, int argc, char** argv) VL_MT_DISABLED;
|
||||
void parseOptsList(FileLine* fl, const string& optdir, int argc, char** argv) VL_MT_DISABLED;
|
||||
void parseOptsFile(FileLine* fl, const string& filename, bool rel) VL_MT_DISABLED;
|
||||
@@ -713,6 +718,7 @@ public:
|
||||
static string getenvSYSTEMC_INCLUDE();
|
||||
static string getenvSYSTEMC_LIBDIR();
|
||||
static string getenvVERILATOR_ROOT();
|
||||
static string getenvVERILATOR_SOLVER();
|
||||
static string getStdPackagePath();
|
||||
static string getSupported(const string& var);
|
||||
static bool systemCSystemWide();
|
||||
|
||||
@@ -110,6 +110,7 @@ class OrderMoveDomScope final {
|
||||
|
||||
struct Hash final {
|
||||
size_t operator()(const DomScopeMapKey& key) const {
|
||||
// cppcheck-suppress unreadVariable // cppcheck bug
|
||||
V3Hash hash{reinterpret_cast<uint64_t>(key.m_domainp)};
|
||||
hash += reinterpret_cast<uint64_t>(key.m_scopep);
|
||||
return hash.value();
|
||||
@@ -199,7 +200,7 @@ class OrderMoveGraphSerializer final {
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
OrderMoveGraphSerializer(OrderMoveGraph& moveGraph) {
|
||||
explicit OrderMoveGraphSerializer(OrderMoveGraph& moveGraph) {
|
||||
// Set V3GraphVertex::user() to the number of incoming edges (upstream dependencies)
|
||||
for (V3GraphVertex& vtx : moveGraph.vertices()) {
|
||||
const uint32_t nDeps = vtx.inEdges().size();
|
||||
|
||||
+2
-21
@@ -86,26 +86,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
// Environment
|
||||
|
||||
string V3Os::getenvStr(const string& envvar, const string& defaultValue) {
|
||||
string ret = "";
|
||||
#if defined(_MSC_VER)
|
||||
// Note: MinGW does not offer _dupenv_s
|
||||
const char* envvalue = nullptr;
|
||||
_dupenv_s((char**)&envvalue, nullptr, envvar.c_str());
|
||||
if (envvalue != nullptr) {
|
||||
const std::string result{envvalue};
|
||||
free((void*)envvalue);
|
||||
ret = result;
|
||||
} else {
|
||||
ret = defaultValue;
|
||||
}
|
||||
#else
|
||||
if (const char* const envvalue = getenv(envvar.c_str())) {
|
||||
ret = envvalue;
|
||||
} else {
|
||||
ret = defaultValue;
|
||||
}
|
||||
#endif
|
||||
return VString::escapeStringForPath(ret);
|
||||
return VString::escapeStringForPath(VlOs::getenvStr(envvar, defaultValue));
|
||||
}
|
||||
|
||||
void V3Os::setenvStr(const string& envvar, const string& value, const string& why) {
|
||||
@@ -122,7 +103,7 @@ void V3Os::setenvStr(const string& envvar, const string& value, const string& wh
|
||||
// setenv() replaced by putenv() in Solaris environment. Prototype is different
|
||||
// putenv() requires NAME=VALUE format
|
||||
const string vareq = envvar + "=" + value;
|
||||
putenv(const_cast<char*>(vareq.c_str()));
|
||||
putenv(strdup(vareq.c_str())); // will leak if setting the same variable again
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ AstNodeDType* V3ParseGrammar::createArray(AstNodeDType* basep, AstNodeRange* nra
|
||||
arrayp = new AstUnpackArrayDType{rangep->fileline(), VFlagChildDType{}, arrayp,
|
||||
rangep};
|
||||
} else if (VN_IS(nrangep, UnsizedRange)) {
|
||||
arrayp = new AstUnsizedArrayDType{nrangep->fileline(), VFlagChildDType{}, arrayp};
|
||||
arrayp = new AstDynArrayDType{nrangep->fileline(), VFlagChildDType{}, arrayp};
|
||||
VL_DO_DANGLING(nrangep->deleteTree(), nrangep);
|
||||
} else if (VN_IS(nrangep, BracketRange)) {
|
||||
const AstBracketRange* const arangep = VN_AS(nrangep, BracketRange);
|
||||
|
||||
+31
-25
@@ -688,43 +688,43 @@ string V3PreProcImp::defineSubst(VDefineRef* refp) {
|
||||
{ // Parse substitution define using arguments
|
||||
string argName;
|
||||
bool quote = false;
|
||||
bool triquote = false;
|
||||
bool backslashesc = false; // In \.....{space} block
|
||||
// Note we go through the loop once more at the nullptr end-of-string
|
||||
for (const char* cp = value.c_str(); (*cp) || argName != ""; cp = (*cp ? cp + 1 : cp)) {
|
||||
// UINFO(4, "CH "<<*cp<<" an "<<argName<<endl);
|
||||
if (!quote && *cp == '\\') {
|
||||
if (!quote && !triquote && *cp == '\\') {
|
||||
backslashesc = true;
|
||||
} else if (std::isspace(*cp)) {
|
||||
backslashesc = false;
|
||||
}
|
||||
// We don't check for quotes; some simulators expand even inside quotes
|
||||
if (std::isalpha(*cp) || *cp == '_'
|
||||
|| *cp == '$' // Won't replace system functions, since no $ in argValueByName
|
||||
|| (argName != "" && (std::isdigit(*cp) || *cp == '$'))) {
|
||||
argName += *cp;
|
||||
continue;
|
||||
}
|
||||
if (argName != "") {
|
||||
// Found a possible variable substitution
|
||||
const auto iter = argValueByName.find(argName);
|
||||
if (iter != argValueByName.end()) {
|
||||
// Substitute
|
||||
const string subst = iter->second;
|
||||
if (subst == "") {
|
||||
// Normally `` is removed later, but with no token after, we're otherwise
|
||||
// stuck, so remove proceeding ``
|
||||
if (out.size() >= 2 && out.substr(out.size() - 2) == "``") {
|
||||
out = out.substr(0, out.size() - 2);
|
||||
if (!quote && !triquote) {
|
||||
if (std::isalpha(*cp) || *cp == '_'
|
||||
|| *cp == '$' // Won't replace system functions, since no $ in argValueByName
|
||||
|| (argName != "" && (std::isdigit(*cp) || *cp == '$'))) {
|
||||
argName += *cp;
|
||||
continue;
|
||||
}
|
||||
if (argName != "") {
|
||||
// Found a possible variable substitution
|
||||
const auto iter = argValueByName.find(argName);
|
||||
if (iter != argValueByName.end()) {
|
||||
// Substitute
|
||||
const string subst = iter->second;
|
||||
if (subst == "") {
|
||||
// Normally `` is removed later, but with no token after, we're
|
||||
// otherwise stuck, so remove proceeding ``
|
||||
if (out.size() >= 2 && out.substr(out.size() - 2) == "``") {
|
||||
out = out.substr(0, out.size() - 2);
|
||||
}
|
||||
} else {
|
||||
out += subst;
|
||||
}
|
||||
} else {
|
||||
out += subst;
|
||||
out += argName;
|
||||
}
|
||||
} else {
|
||||
out += argName;
|
||||
argName = "";
|
||||
}
|
||||
argName = "";
|
||||
}
|
||||
if (!quote) {
|
||||
// Check for `` only after we've detected end-of-argname
|
||||
if (cp[0] == '`' && cp[1] == '`') {
|
||||
if (backslashesc) {
|
||||
@@ -772,6 +772,12 @@ string V3PreProcImp::defineSubst(VDefineRef* refp) {
|
||||
out += cp[0];
|
||||
continue;
|
||||
}
|
||||
if (cp[0] == '"' && cp[1] == '"' && cp[2] == '"') {
|
||||
triquote = !triquote;
|
||||
out += "\"\"\"";
|
||||
cp += 2;
|
||||
continue;
|
||||
}
|
||||
if (*cp == '"') quote = !quote;
|
||||
if (*cp) out += *cp;
|
||||
}
|
||||
|
||||
+3
-1
@@ -86,7 +86,7 @@ class PremitVisitor final : public VNVisitor {
|
||||
// Extract into constant pool.
|
||||
const bool merge = v3Global.opt.fMergeConstPool();
|
||||
varp = v3Global.rootp()->constPoolp()->findConst(constp, merge)->varp();
|
||||
nodep->deleteTree();
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
++m_extractedToConstPool;
|
||||
} else {
|
||||
// Keep as local temporary.
|
||||
@@ -183,9 +183,11 @@ class PremitVisitor final : public VNVisitor {
|
||||
|
||||
void visit(AstWhile* nodep) override {
|
||||
UINFO(4, " WHILE " << nodep << endl);
|
||||
// cppcheck-suppress shadowVariable // Also restored below
|
||||
START_STATEMENT_OR_RETURN(nodep);
|
||||
iterateAndNextNull(nodep->precondsp());
|
||||
{
|
||||
// cppcheck-suppress shadowVariable // Also restored above
|
||||
VL_RESTORER(m_inWhileCondp);
|
||||
m_inWhileCondp = nodep;
|
||||
iterateAndNextNull(nodep->condp());
|
||||
|
||||
+225
-17
@@ -39,24 +39,28 @@ class RandomizeMarkVisitor final : public VNVisitorConst {
|
||||
// NODE STATE
|
||||
// Cleared on Netlist
|
||||
// AstClass::user1() -> bool. Set true to indicate needs randomize processing
|
||||
// AstConstraintExpr::user1() -> bool. Set true to indicate state-dependent
|
||||
// AstNodeExpr::user1() -> bool. Set true to indicate constraint expression depending on a
|
||||
// randomized variable
|
||||
const VNUser1InUse m_inuser1;
|
||||
|
||||
using DerivedSet = std::unordered_set<AstClass*>;
|
||||
using BaseToDerivedMap = std::unordered_map<AstClass*, DerivedSet>;
|
||||
using BaseToDerivedMap = std::unordered_map<const AstClass*, DerivedSet>;
|
||||
|
||||
BaseToDerivedMap m_baseToDerivedMap; // Mapping from base classes to classes that extend them
|
||||
AstClass* m_classp = nullptr; // Current class
|
||||
AstConstraintExpr* m_constraintExprp = nullptr; // Current constraint expression
|
||||
|
||||
// METHODS
|
||||
void markMembers(AstClass* nodep) {
|
||||
for (auto* classp = nodep; classp;
|
||||
void markMembers(const AstClass* nodep) {
|
||||
for (const AstClass* classp = nodep; classp;
|
||||
classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr) {
|
||||
for (auto* memberp = classp->stmtsp(); memberp; memberp = memberp->nextp()) {
|
||||
for (const AstNode* memberp = classp->stmtsp(); memberp; memberp = memberp->nextp()) {
|
||||
// If member is rand and of class type, mark its class
|
||||
if (VN_IS(memberp, Var) && VN_AS(memberp, Var)->isRand()) {
|
||||
if (const auto* const classRefp
|
||||
if (const AstClassRefDType* const classRefp
|
||||
= VN_CAST(memberp->dtypep()->skipRefp(), ClassRefDType)) {
|
||||
auto* const rclassp = classRefp->classp();
|
||||
AstClass* const rclassp = classRefp->classp();
|
||||
if (!rclassp->user1()) {
|
||||
rclassp->user1(true);
|
||||
markMembers(rclassp);
|
||||
@@ -67,11 +71,11 @@ class RandomizeMarkVisitor final : public VNVisitorConst {
|
||||
}
|
||||
}
|
||||
}
|
||||
void markDerived(AstClass* nodep) {
|
||||
void markDerived(const AstClass* nodep) {
|
||||
const auto it = m_baseToDerivedMap.find(nodep);
|
||||
if (it != m_baseToDerivedMap.end()) {
|
||||
for (auto* classp : it->second) {
|
||||
if (!classp->user1p()) {
|
||||
if (!classp->user1()) {
|
||||
classp->user1(true);
|
||||
markMembers(classp);
|
||||
markDerived(classp);
|
||||
@@ -92,7 +96,7 @@ class RandomizeMarkVisitor final : public VNVisitorConst {
|
||||
iterateChildrenConst(nodep);
|
||||
if (nodep->extendsp()) {
|
||||
// Save pointer to derived class
|
||||
AstClass* const basep = nodep->extendsp()->classp();
|
||||
const AstClass* const basep = nodep->extendsp()->classp();
|
||||
m_baseToDerivedMap[basep].insert(nodep);
|
||||
}
|
||||
}
|
||||
@@ -111,6 +115,22 @@ class RandomizeMarkVisitor final : public VNVisitorConst {
|
||||
if (nodep->name() != "randomize") return;
|
||||
if (m_classp) m_classp->user1(true);
|
||||
}
|
||||
void visit(AstConstraintExpr* nodep) override {
|
||||
VL_RESTORER(m_constraintExprp);
|
||||
m_constraintExprp = nodep;
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
if (!m_constraintExprp) return;
|
||||
if (!nodep->varp()->isRand()) {
|
||||
m_constraintExprp->user1(true);
|
||||
nodep->v3warn(CONSTRAINTIGN, "State-dependent constraint ignored (unsupported)");
|
||||
return;
|
||||
}
|
||||
for (AstNode* backp = nodep; backp != m_constraintExprp && !backp->user1();
|
||||
backp = backp->backp())
|
||||
backp->user1(true);
|
||||
}
|
||||
|
||||
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
||||
|
||||
@@ -123,6 +143,143 @@ public:
|
||||
~RandomizeMarkVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Visitor that turns constraints into template strings for solvers
|
||||
|
||||
class ConstraintExprVisitor final : public VNVisitor {
|
||||
// NODE STATE
|
||||
// AstVar::user4() -> bool. Handled in constraints
|
||||
// AstNodeExpr::user1() -> bool. Depending on a randomized variable
|
||||
// VNUser4InUse m_inuser4; (Allocated for use in RandomizeVisitor)
|
||||
|
||||
AstTask* const m_taskp; // X_setup_constraint() method of the constraint
|
||||
AstVar* const m_genp; // VlRandomizer variable of the class
|
||||
|
||||
bool editFormat(AstNodeExpr* nodep) {
|
||||
if (nodep->user1()) return false;
|
||||
// Replace computable expression with SMT constant
|
||||
VNRelinker handle;
|
||||
nodep->unlinkFrBack(&handle);
|
||||
AstSFormatF* const newp = new AstSFormatF{
|
||||
nodep->fileline(), (nodep->width() & 3) ? "#b%b" : "#x%x", false, nodep};
|
||||
handle.relink(newp);
|
||||
return true;
|
||||
}
|
||||
void editSMT(AstNodeExpr* nodep, AstNodeExpr* lhsp = nullptr, AstNodeExpr* rhsp = nullptr) {
|
||||
// Replace incomputable (result-dependent) expression with SMT expression
|
||||
std::string smtExpr = nodep->emitSMT(); // Might need child width (AstExtend)
|
||||
UASSERT_OBJ(smtExpr != "", nodep,
|
||||
"Node needs randomization constraint, but no emitSMT: " << nodep);
|
||||
|
||||
if (lhsp) lhsp = VN_AS(iterateSubtreeReturnEdits(lhsp->unlinkFrBack()), NodeExpr);
|
||||
if (rhsp) rhsp = VN_AS(iterateSubtreeReturnEdits(rhsp->unlinkFrBack()), NodeExpr);
|
||||
|
||||
AstNodeExpr* argsp = nullptr;
|
||||
for (string::iterator pos = smtExpr.begin(); pos != smtExpr.end(); ++pos) {
|
||||
if (pos[0] == '%') {
|
||||
++pos;
|
||||
switch (pos[0]) {
|
||||
case '%': break;
|
||||
case 'l':
|
||||
pos[0] = '@';
|
||||
UASSERT_OBJ(lhsp, nodep, "emitSMT() references undef node");
|
||||
argsp = AstNode::addNext(argsp, lhsp);
|
||||
lhsp = nullptr;
|
||||
break;
|
||||
case 'r':
|
||||
pos[0] = '@';
|
||||
UASSERT_OBJ(rhsp, nodep, "emitSMT() references undef node");
|
||||
argsp = AstNode::addNext(argsp, rhsp);
|
||||
rhsp = nullptr;
|
||||
break;
|
||||
default: nodep->v3fatalSrc("Unknown emitSMT format code: %" << pos[0]); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
UASSERT_OBJ(!lhsp, nodep, "Missing emitSMT %l for " << lhsp);
|
||||
UASSERT_OBJ(!rhsp, nodep, "Missing emitSMT %r for " << rhsp);
|
||||
AstSFormatF* const newp = new AstSFormatF{nodep->fileline(), smtExpr, false, argsp};
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
if (editFormat(nodep)) return;
|
||||
// In SMT just variable name, but we also ensure write_var for the variable
|
||||
const std::string smtName = nodep->name(); // Can be anything unique
|
||||
nodep->replaceWith(new AstSFormatF{nodep->fileline(), smtName, false, nullptr});
|
||||
|
||||
AstVar* const varp = nodep->varp();
|
||||
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
|
||||
if (!varp->user4()) {
|
||||
varp->user4(true);
|
||||
AstCMethodHard* const methodp = new AstCMethodHard{
|
||||
varp->fileline(), new AstVarRef{varp->fileline(), m_genp, VAccess::READWRITE},
|
||||
"write_var"};
|
||||
methodp->dtypeSetVoid();
|
||||
methodp->addPinsp(new AstVarRef{varp->fileline(), varp, VAccess::WRITE});
|
||||
methodp->addPinsp(new AstConst{varp->dtypep()->fileline(), AstConst::Unsized64{},
|
||||
(size_t)varp->width()});
|
||||
AstNodeExpr* const varnamep
|
||||
= new AstCExpr{varp->fileline(), "\"" + smtName + "\"", varp->width()};
|
||||
varnamep->dtypep(varp->dtypep());
|
||||
methodp->addPinsp(varnamep);
|
||||
m_taskp->addStmtsp(new AstStmtExpr{varp->fileline(), methodp});
|
||||
}
|
||||
}
|
||||
void visit(AstNodeBiop* nodep) override {
|
||||
if (editFormat(nodep)) return;
|
||||
editSMT(nodep, nodep->lhsp(), nodep->rhsp());
|
||||
}
|
||||
void visit(AstNodeUniop* nodep) override {
|
||||
if (editFormat(nodep)) return;
|
||||
editSMT(nodep, nodep->lhsp());
|
||||
}
|
||||
void visit(AstReplicate* nodep) override {
|
||||
// Biop, but RHS is harmful
|
||||
if (editFormat(nodep)) return;
|
||||
editSMT(nodep, nodep->srcp());
|
||||
}
|
||||
void visit(AstSFormatF* nodep) override {}
|
||||
void visit(AstConstraintExpr* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstCMethodHard* nodep) override {
|
||||
if (editFormat(nodep)) return;
|
||||
|
||||
UASSERT_OBJ(nodep->name() == "size", nodep, "Non-size method call in constraints");
|
||||
|
||||
AstNode* fromp = nodep->fromp();
|
||||
// Warn early while the dtype is still there
|
||||
fromp->v3warn(E_UNSUPPORTED, "Unsupported: random member variable with type "
|
||||
<< fromp->dtypep()->prettyDTypeNameQ());
|
||||
|
||||
iterateChildren(nodep); // Might change fromp
|
||||
fromp = nodep->fromp()->unlinkFrBack();
|
||||
fromp->dtypep(nodep->dtypep());
|
||||
nodep->replaceWith(fromp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
void visit(AstNodeExpr* nodep) override {
|
||||
if (editFormat(nodep)) return;
|
||||
nodep->v3fatalSrc(
|
||||
"Visit function missing? Constraint function missing for math node: " << nodep);
|
||||
}
|
||||
void visit(AstNode* nodep) override {
|
||||
nodep->v3fatalSrc(
|
||||
"Visit function missing? Constraint function missing for node: " << nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit ConstraintExprVisitor(AstConstraintExpr* nodep, AstTask* taskp, AstVar* genp)
|
||||
: m_taskp(taskp)
|
||||
, m_genp(genp) {
|
||||
iterate(nodep);
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Visitor that defines a randomize method where needed
|
||||
|
||||
@@ -130,10 +287,15 @@ class RandomizeVisitor final : public VNVisitor {
|
||||
// NODE STATE
|
||||
// Cleared on Netlist
|
||||
// AstClass::user1() -> bool. Set true to indicate needs randomize processing
|
||||
// AstConstraintExpr::user1() -> bool. Set true to indicate state-dependent
|
||||
// AstEnumDType::user2() -> AstVar*. Pointer to table with enum values
|
||||
// AstClass::user3() -> AstFunc*. Pointer to randomize() method of a class
|
||||
// AstVar::user4() -> bool. Handled in constraints
|
||||
// AstClass::user4() -> AstVar*. Constrained randomizer variable
|
||||
// VNUser1InUse m_inuser1; (Allocated for use in RandomizeMarkVisitor)
|
||||
const VNUser2InUse m_inuser2;
|
||||
const VNUser3InUse m_inuser3;
|
||||
const VNUser4InUse m_inuser4;
|
||||
|
||||
// STATE
|
||||
VMemberMap m_memberMap; // Member names cached for fast lookup
|
||||
@@ -147,9 +309,9 @@ class RandomizeVisitor final : public VNVisitor {
|
||||
AstVar* enumValueTabp(AstEnumDType* nodep) {
|
||||
if (nodep->user2p()) return VN_AS(nodep->user2p(), Var);
|
||||
UINFO(9, "Construct Venumvaltab " << nodep << endl);
|
||||
AstNodeArrayDType* const vardtypep
|
||||
= new AstUnpackArrayDType{nodep->fileline(), nodep->dtypep(),
|
||||
new AstRange{nodep->fileline(), nodep->itemCount(), 0}};
|
||||
AstNodeArrayDType* const vardtypep = new AstUnpackArrayDType{
|
||||
nodep->fileline(), nodep->dtypep(),
|
||||
new AstRange{nodep->fileline(), static_cast<int>(nodep->itemCount()), 0}};
|
||||
AstInitArray* const initp = new AstInitArray{nodep->fileline(), vardtypep, nullptr};
|
||||
v3Global.rootp()->typeTablep()->addTypesp(vardtypep);
|
||||
AstVar* const varp
|
||||
@@ -274,6 +436,12 @@ class RandomizeVisitor final : public VNVisitor {
|
||||
funcp->addStmtsp(callp->makeStmt());
|
||||
}
|
||||
}
|
||||
AstTask* newSetupConstraintTask(AstClass* nodep, const std::string& name) {
|
||||
AstTask* const taskp = new AstTask{nodep->fileline(), name + "_setup_constraint", nullptr};
|
||||
taskp->classMethod(true);
|
||||
nodep->addMembersp(taskp);
|
||||
return taskp;
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
@@ -315,20 +483,28 @@ class RandomizeVisitor final : public VNVisitor {
|
||||
beginValp = baseRandCallp;
|
||||
}
|
||||
}
|
||||
if (m_modp->user4p()) {
|
||||
AstNode* const argsp = new AstVarRef{nodep->fileline(), VN_AS(m_modp->user4p(), Var),
|
||||
VAccess::READWRITE};
|
||||
argsp->addNext(new AstText{fl, ".next(__Vm_rng)"});
|
||||
AstNodeExpr* const solverCallp = new AstCExpr{fl, argsp};
|
||||
solverCallp->dtypeSetBit();
|
||||
beginValp = beginValp ? new AstAnd{fl, beginValp, solverCallp} : solverCallp;
|
||||
}
|
||||
if (!beginValp) beginValp = new AstConst{fl, AstConst::WidthedValue{}, 32, 1};
|
||||
|
||||
funcp->addStmtsp(new AstAssign{fl, new AstVarRef{fl, fvarp, VAccess::WRITE}, beginValp});
|
||||
|
||||
for (auto* memberp = nodep->stmtsp(); memberp; memberp = memberp->nextp()) {
|
||||
for (AstNode* memberp = nodep->stmtsp(); memberp; memberp = memberp->nextp()) {
|
||||
AstVar* const memberVarp = VN_CAST(memberp, Var);
|
||||
if (!memberVarp || !memberVarp->isRand()) continue;
|
||||
if (!memberVarp || !memberVarp->isRand() || memberVarp->user4()) continue;
|
||||
const AstNodeDType* const dtypep = memberp->dtypep()->skipRefp();
|
||||
if (VN_IS(dtypep, BasicDType) || VN_IS(dtypep, StructDType)) {
|
||||
AstVar* const randcVarp = newRandcVarsp(memberVarp);
|
||||
AstVarRef* const refp = new AstVarRef{fl, memberVarp, VAccess::WRITE};
|
||||
AstNodeStmt* const stmtp = newRandStmtsp(fl, refp, randcVarp);
|
||||
funcp->addStmtsp(stmtp);
|
||||
} else if (const auto* const classRefp = VN_CAST(dtypep, ClassRefDType)) {
|
||||
} else if (const AstClassRefDType* const classRefp = VN_CAST(dtypep, ClassRefDType)) {
|
||||
if (classRefp->classp() == nodep) {
|
||||
memberp->v3warn(
|
||||
E_UNSUPPORTED,
|
||||
@@ -358,8 +534,40 @@ class RandomizeVisitor final : public VNVisitor {
|
||||
nodep->user1(false);
|
||||
}
|
||||
void visit(AstConstraint* nodep) override {
|
||||
nodep->v3warn(CONSTRAINTIGN, "Constraint ignored (unsupported)");
|
||||
if (!v3Global.opt.xmlOnly()) VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
AstNodeFTask* const newp = VN_AS(m_memberMap.findMember(m_modp, "new"), NodeFTask);
|
||||
UASSERT_OBJ(newp, m_modp, "No new() in class");
|
||||
AstTask* const taskp = newSetupConstraintTask(VN_AS(m_modp, Class), nodep->name());
|
||||
AstTaskRef* const setupTaskRefp
|
||||
= new AstTaskRef{nodep->fileline(), taskp->name(), nullptr};
|
||||
setupTaskRefp->taskp(taskp);
|
||||
newp->addStmtsp(new AstStmtExpr{nodep->fileline(), setupTaskRefp});
|
||||
|
||||
AstVar* genp = VN_AS(m_modp->user4p(), Var);
|
||||
if (!genp) {
|
||||
genp = new AstVar(nodep->fileline(), VVarType::MEMBER, "constraint",
|
||||
m_modp->findBasicDType(VBasicDTypeKwd::RANDOM_GENERATOR));
|
||||
VN_AS(m_modp, Class)->addMembersp(genp);
|
||||
m_modp->user4p(genp);
|
||||
}
|
||||
|
||||
while (nodep->itemsp()) {
|
||||
AstConstraintExpr* const condsp = VN_CAST(nodep->itemsp(), ConstraintExpr);
|
||||
if (!condsp || condsp->user1()) {
|
||||
nodep->itemsp()->v3warn(CONSTRAINTIGN,
|
||||
"Constraint expression ignored (unsupported)");
|
||||
pushDeletep(nodep->itemsp()->unlinkFrBack());
|
||||
continue;
|
||||
}
|
||||
{ ConstraintExprVisitor{condsp->unlinkFrBack(), taskp, genp}; }
|
||||
// Only hard constraints are now supported
|
||||
AstCMethodHard* const methodp = new AstCMethodHard{
|
||||
condsp->fileline(), new AstVarRef{condsp->fileline(), genp, VAccess::READWRITE},
|
||||
"hard", condsp->exprp()->unlinkFrBack()};
|
||||
methodp->dtypeSetVoid();
|
||||
taskp->addStmtsp(new AstStmtExpr{condsp->fileline(), methodp});
|
||||
VL_DO_DANGLING(condsp->deleteTree(), condsp);
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
void visit(AstRandCase* nodep) override {
|
||||
// RANDCASE
|
||||
|
||||
@@ -283,6 +283,7 @@ void transformForks(AstNetlist* const netlistp) {
|
||||
// STATE
|
||||
bool m_inClass = false; // Are we in a class?
|
||||
bool m_beginHasAwaits = false; // Does the current begin have awaits?
|
||||
bool m_awaitMoved = false; // Has the current function lost awaits?
|
||||
AstFork* m_forkp = nullptr; // Current fork
|
||||
AstCFunc* m_funcp = nullptr; // Current function
|
||||
|
||||
@@ -344,7 +345,13 @@ void transformForks(AstNetlist* const netlistp) {
|
||||
}
|
||||
void visit(AstCFunc* nodep) override {
|
||||
m_funcp = nodep;
|
||||
m_awaitMoved = false;
|
||||
iterateChildren(nodep);
|
||||
if (nodep->isCoroutine() && m_awaitMoved
|
||||
&& !nodep->stmtsp()->exists([](AstCAwait*) { return true; })) {
|
||||
// co_return at the end (either that or a co_await is required in a coroutine
|
||||
nodep->addStmtsp(new AstCStmt{nodep->fileline(), "co_return;\n"});
|
||||
}
|
||||
m_funcp = nullptr;
|
||||
}
|
||||
void visit(AstVar* nodep) override {
|
||||
@@ -399,6 +406,8 @@ void transformForks(AstNetlist* const netlistp) {
|
||||
if (!m_beginHasAwaits) {
|
||||
// co_return at the end (either that or a co_await is required in a coroutine
|
||||
newfuncp->addStmtsp(new AstCStmt{nodep->fileline(), "co_return;\n"});
|
||||
} else {
|
||||
m_awaitMoved = true;
|
||||
}
|
||||
remapLocals(newfuncp, callp);
|
||||
} else {
|
||||
|
||||
@@ -128,11 +128,11 @@ private:
|
||||
if (writesToVirtIface(nodep)) {
|
||||
// Convert to always, as we have to assign the trigger var
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstAlwaysPost* const postp = new AstAlwaysPost{flp, nullptr, nullptr};
|
||||
AstAlwaysPost* const postp = new AstAlwaysPost{flp};
|
||||
nodep->replaceWith(postp);
|
||||
postp->addStmtsp(
|
||||
new AstAssign{flp, nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack()});
|
||||
nodep->deleteTree();
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstNodeIf* nodep) override {
|
||||
|
||||
@@ -167,7 +167,9 @@ class SenExprBuilder final {
|
||||
case VEdgeType::ET_CHANGED:
|
||||
case VEdgeType::ET_HYBRID: //
|
||||
if (VN_IS(senp->dtypep()->skipRefp(), UnpackArrayDType)) {
|
||||
AstCMethodHard* const resultp = new AstCMethodHard{flp, currp(), "neq", prevp()};
|
||||
// operand order reversed to avoid calling neq() method on non-VlUnpacked type, see
|
||||
// issue #5125
|
||||
AstCMethodHard* const resultp = new AstCMethodHard{flp, prevp(), "neq", currp()};
|
||||
resultp->dtypeSetBit();
|
||||
return {resultp, true};
|
||||
}
|
||||
|
||||
+1
-1
@@ -200,7 +200,7 @@ class SliceVisitor final : public VNVisitor {
|
||||
leOffset};
|
||||
} else if (VN_IS(nodep, ArraySel) || VN_IS(nodep, NodeVarRef) || VN_IS(nodep, NodeSel)
|
||||
|| VN_IS(nodep, CMethodHard) || VN_IS(nodep, MemberSel)
|
||||
|| VN_IS(nodep, ExprStmt)) {
|
||||
|| VN_IS(nodep, ExprStmt) || VN_IS(nodep, StructSel)) {
|
||||
UINFO(9, " cloneSel(" << elements << "," << elemIdx << ") " << nodep << endl);
|
||||
const int leOffset = !arrayp->rangep()->ascending()
|
||||
? arrayp->rangep()->elementsConst() - 1 - elemIdx
|
||||
|
||||
+1
-1
@@ -819,7 +819,7 @@ class RemovePlaceholdersVisitor final : public VNVisitor {
|
||||
}
|
||||
}
|
||||
if (emptyOrCommentOnly) {
|
||||
pushDeletep(nodep->unlinkFrBack());
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
++m_emptyAlways;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -93,8 +93,7 @@ class SplitAsCleanVisitor final : public VNVisitor {
|
||||
m_keepStmt = true;
|
||||
} else {
|
||||
UINFO(6, " Delete STMT " << nodep << endl);
|
||||
nodep->unlinkFrBack();
|
||||
pushDeletep(nodep);
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
}
|
||||
// If something below matches, the upper statement remains too.
|
||||
|
||||
+6
-5
@@ -79,7 +79,7 @@ public:
|
||||
: m_fl{fl} {}
|
||||
|
||||
~TableBuilder() {
|
||||
if (m_initp) m_initp->deleteTree();
|
||||
if (m_initp) VL_DO_DANGLING(m_initp->deleteTree(), m_initp);
|
||||
}
|
||||
|
||||
void setTableSize(AstNodeDType* elemDType, unsigned size) {
|
||||
@@ -89,16 +89,17 @@ public:
|
||||
// Create data type
|
||||
const int width = elemDType->width();
|
||||
AstNodeDType* const subDTypep
|
||||
= elemDType->isString()
|
||||
= elemDType->isString() || elemDType->isDouble()
|
||||
? elemDType
|
||||
: v3Global.rootp()->findBitDType(width, width, VSigning::UNSIGNED);
|
||||
AstUnpackArrayDType* const tableDTypep = new AstUnpackArrayDType{
|
||||
m_fl, subDTypep, new AstRange{m_fl, static_cast<int>(size), 0}};
|
||||
v3Global.rootp()->typeTablep()->addTypesp(tableDTypep);
|
||||
// Create table initializer (with default value 0)
|
||||
AstConst* const defaultp = elemDType->isString()
|
||||
? new AstConst{m_fl, AstConst::String{}, ""}
|
||||
: new AstConst{m_fl, AstConst::WidthedValue{}, width, 0};
|
||||
AstConst* const defaultp
|
||||
= elemDType->isString() ? new AstConst{m_fl, AstConst::String{}, ""}
|
||||
: elemDType->isDouble() ? new AstConst{m_fl, AstConst::RealDouble{}, 0.0}
|
||||
: new AstConst{m_fl, AstConst::WidthedValue{}, width, 0};
|
||||
m_initp = new AstInitArray{m_fl, tableDTypep, defaultp};
|
||||
}
|
||||
|
||||
|
||||
+32
-17
@@ -305,6 +305,9 @@ struct TaskDpiUtils final {
|
||||
if (portp->basicp() && portp->basicp()->keyword() == VBasicDTypeKwd::CHANDLE) {
|
||||
frstmt = "VL_CVT_VP_Q(" + frName;
|
||||
ket = ")";
|
||||
} else if (portp->basicp() && portp->basicp()->keyword() == VBasicDTypeKwd::STRING) {
|
||||
frstmt = "VL_CVT_N_CSTR(" + frName;
|
||||
ket = ")";
|
||||
} else if ((portp->basicp() && portp->basicp()->isDpiPrimitive())) {
|
||||
frstmt = frName;
|
||||
} else {
|
||||
@@ -694,7 +697,7 @@ class TaskVisitor final : public VNVisitor {
|
||||
// differ we may get C compilation problems later
|
||||
const std::string dpiType = portp->dpiArgType(false, false);
|
||||
dpiproto += dpiType;
|
||||
const std::string vType = portp->dtypep()->prettyDTypeName();
|
||||
const std::string vType = portp->dtypep()->prettyDTypeName(false);
|
||||
if (!portp->isDpiOpenArray() && dpiType != vType) {
|
||||
dpiproto += " /* " + vType + " */ ";
|
||||
}
|
||||
@@ -1589,14 +1592,13 @@ public:
|
||||
const char* const V3Task::s_dpiTemporaryVarSuffix = "__Vcvt";
|
||||
|
||||
V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp,
|
||||
V3TaskConnectState* statep) {
|
||||
V3TaskConnectState* statep, bool makeChanges) {
|
||||
// Output list will be in order of the port declaration variables (so
|
||||
// func calls are made right in C)
|
||||
// Missing pin/expr? We return (pinvar, nullptr)
|
||||
// Extra pin/expr? We clean it up
|
||||
UINFO(9, "taskConnects " << nodep << endl);
|
||||
std::map<const std::string, int> nameToIndex;
|
||||
std::set<const AstVar*> argWrap; // Which ports are defaulted, forcing arg wrapper creation
|
||||
V3TaskConnects tconnects;
|
||||
UASSERT_OBJ(nodep->taskp(), nodep, "unlinked");
|
||||
|
||||
@@ -1608,7 +1610,7 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp,
|
||||
if (portp->isIO()) {
|
||||
tconnects.emplace_back(portp, static_cast<AstArg*>(nullptr));
|
||||
nameToIndex.emplace(portp->name(), tpinnum); // For name based connections
|
||||
tpinnum++;
|
||||
++tpinnum;
|
||||
if (portp->attrSFormat()) {
|
||||
sformatp = portp;
|
||||
} else if (sformatp) {
|
||||
@@ -1630,17 +1632,19 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp,
|
||||
// By name
|
||||
const auto it = nameToIndex.find(argp->name());
|
||||
if (it == nameToIndex.end()) {
|
||||
pinp->v3error("No such argument " << argp->prettyNameQ() << " in function call to "
|
||||
<< nodep->taskp()->prettyTypeName());
|
||||
// We'll just delete it; seems less error prone than making a false argument
|
||||
VL_DO_DANGLING(pinp->unlinkFrBack()->deleteTree(), pinp);
|
||||
if (makeChanges) {
|
||||
pinp->v3error("No such argument " << argp->prettyNameQ()
|
||||
<< " in function call to "
|
||||
<< nodep->taskp()->prettyTypeName());
|
||||
// We'll just delete it; seems less error prone than making a false argument
|
||||
VL_DO_DANGLING(pinp->unlinkFrBack()->deleteTree(), pinp);
|
||||
}
|
||||
} else {
|
||||
if (tconnects[it->second].second) {
|
||||
if (tconnects[it->second].second && makeChanges) {
|
||||
pinp->v3error("Duplicate argument " << argp->prettyNameQ()
|
||||
<< " in function call to "
|
||||
<< nodep->taskp()->prettyTypeName());
|
||||
}
|
||||
argp->name(""); // Can forget name as will add back in pin order
|
||||
tconnects[it->second].second = argp;
|
||||
reorganize = true;
|
||||
}
|
||||
@@ -1649,8 +1653,8 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp,
|
||||
if (sformatp) {
|
||||
tconnects.emplace_back(sformatp, static_cast<AstArg*>(nullptr));
|
||||
tconnects[ppinnum].second = argp;
|
||||
tpinnum++;
|
||||
} else {
|
||||
++tpinnum;
|
||||
} else if (makeChanges) {
|
||||
pinp->v3error("Too many arguments in function call to "
|
||||
<< nodep->taskp()->prettyTypeName());
|
||||
// We'll just delete it; seems less error prone than making a false argument
|
||||
@@ -1660,10 +1664,13 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp,
|
||||
tconnects[ppinnum].second = argp;
|
||||
}
|
||||
}
|
||||
ppinnum++;
|
||||
++ppinnum;
|
||||
}
|
||||
|
||||
if (!makeChanges) return tconnects;
|
||||
|
||||
// Connect missing ones
|
||||
std::set<const AstVar*> argWrap; // Which ports are defaulted, forcing arg wrapper creation
|
||||
for (int i = 0; i < tpinnum; ++i) {
|
||||
AstVar* const portp = tconnects[i].first;
|
||||
if (!tconnects[i].second || !tconnects[i].second->exprp()) {
|
||||
@@ -1723,6 +1730,11 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp,
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& tconnect : tconnects) {
|
||||
AstArg* const argp = tconnect.second;
|
||||
argp->name(""); // Can forget name as will add back in pin order
|
||||
}
|
||||
|
||||
if (reorganize) {
|
||||
// To simplify downstream, put argument list back into pure pinnumber ordering
|
||||
while (nodep->pinsp()) {
|
||||
@@ -1839,10 +1851,13 @@ AstNodeFTask* V3Task::taskConnectWrapNew(AstNodeFTask* taskp, const string& newn
|
||||
newPortp->funcLocal(true);
|
||||
newTaskp->addStmtsp(newPortp);
|
||||
// Runtime-assign it to the default
|
||||
AstAssign* const newAssignp = new AstAssign{
|
||||
valuep->fileline(), new AstVarRef{valuep->fileline(), newPortp, VAccess::WRITE},
|
||||
valuep->cloneTree(true)};
|
||||
newTaskp->addStmtsp(newAssignp);
|
||||
if (!VN_IS(valuep, EmptyQueue)) {
|
||||
AstAssign* const newAssignp
|
||||
= new AstAssign{valuep->fileline(),
|
||||
new AstVarRef{valuep->fileline(), newPortp, VAccess::WRITE},
|
||||
valuep->cloneTree(true)};
|
||||
newTaskp->addStmtsp(newAssignp);
|
||||
}
|
||||
}
|
||||
oldNewVars.emplace(portp, newPortp);
|
||||
const VAccess pinAccess = portp->isWritable() ? VAccess::WRITE : VAccess::READ;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user