Merge from master for release.

This commit is contained in:
Wilson Snyder 2023-06-13 19:37:22 -04:00
commit 4f13c4d1bb
395 changed files with 7047 additions and 2824 deletions

View File

@ -39,9 +39,11 @@ jobs:
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-20.04' || 'do-not-exclude' }}
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
# Build -m32 only on ubuntu-22.04
# Build -m32 only on ubuntu-22.04 clang++
- {os: ubuntu-18.04, m32: 1}
- {os: ubuntu-20.04, m32: 1}
- compiler: { cc: gcc, cxx: g++ }
m32: 1
include:
# Build GCC 10 on ubuntu-20.04
- os: ubuntu-20.04
@ -111,6 +113,8 @@ jobs:
# Build -m32 only on ubuntu-22.04
- {os: ubuntu-18.04, m32: 1}
- {os: ubuntu-20.04, m32: 1}
- compiler: { cc: gcc, cxx: g++ }
m32: 1
include:
# Test with GCC 10 on ubuntu-20.04 without m32
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, m32: 0, suite: dist-vlt-0}

84
.github/workflows/docker.yml vendored Normal file
View File

@ -0,0 +1,84 @@
# Build and push verilator docker image when tags are pushed to the repository.
# The following secrets must be configured in the github repository:
# DOCKER_HUB_NAMESPACE: docker hub namespace.
# DOCKER_HUB_USER: user name for logging into docker hub
# DOCKER_HUB_ACCESS_TOKEN: docker hub access token.
name: Build Verilator Container
on:
push:
tags: [ 'v*' ]
workflow_dispatch:
inputs:
manual_tag:
description: 'Git tag to use for image build'
required: true
type: string
add_latest_tag:
description: 'Tag workflow_dispatch docker image as "latest"'
required: true
type: boolean
default: false
jobs:
build:
runs-on: ubuntu-22.04
strategy:
matrix:
contexts:
- "ci/docker/run:verilator"
# - "ci/docker/buildenv:verilator-buildenv"
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Extract context variables
run: |
echo "${{ matrix.contexts }}" | sed -r 's/(.*):.*/build_context=\1/' >> "$GITHUB_ENV"
echo "${{ matrix.contexts }}" | sed -r 's/.*:(.*)/image_name=\1/' >> "$GITHUB_ENV"
echo "git_tag=${GITHUB_REF#refs/*/}" >> "$GITHUB_ENV"
- name: Use manual tag
if: ${{ inputs.manual_tag }}
run: |
echo "git_tag=${{ inputs.manual_tag }}" >> "$GITHUB_ENV"
- name: Docker meta
id: docker_meta
uses: docker/metadata-action@v4
with:
images: |
${{ secrets.DOCKER_HUB_NAMESPACE }}/${{ env.image_name }}
tags: |
type=match,pattern=(v.*),group=1,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
type=raw,value=${{ inputs.manual_tag }},enable=${{ inputs.manual_tag != '' }}
type=raw,value=latest,enable=${{ inputs.add_latest_tag == true }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
buildkitd-flags: --debug
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Build and Push to Docker
uses: docker/build-push-action@v4
if: startsWith(github.ref, 'refs/tags/v')
with:
context: ${{ env.build_context }}
build-args: SOURCE_COMMIT=${{ env.git_tag }}
platforms: linux/arm64,linux/amd64
push: ${{ !env.ACT && startsWith(github.ref, 'refs/tags/v') }}
tags: ${{ steps.docker_meta.outputs.tags }}
labels: ${{ steps.docker_meta.outputs.labels }}

View File

@ -15,7 +15,7 @@
cmake_minimum_required(VERSION 3.15)
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
project(Verilator
VERSION 5.010
VERSION 5.012
HOMEPAGE_URL https://verilator.org
LANGUAGES CXX
)

64
Changes
View File

@ -8,6 +8,70 @@ The changes in each Verilator version are described below. The
contributors that suggested a given feature are shown in []. Thanks!
Verilator 5.012 2023-06-13
==========================
**Major:**
* With -j or --build-jobs, multithread Verilator's emit phase of Verilation. [Kamil Rakoczy, Antmicro Ltd]
Additional Verilator-internal stages will become multithreaded over time.
**Minor:**
* Add --main-top-name option for C main TOP name (#4235) (#4249). [Don Williamson]
* Add creating __inputs.vpp file with --debug (#4177). [Tudor Timi]
* Add NEWERSTD warning when using feature in newer language standard (#4168) (#4172). [Ethan Sifferman]
* Add warning that timing controls in DPI exports are unsupported (#4238). [Krzysztof Bieganski, Antmicro Ltd]
* Support std::process class (#4212). [Aleksander Kiryk, Antmicro Ltd]
* Support inside expressions with strings and doubles (#4138) (#4139). [Krzysztof Boroński]
* Support get_randstate/set_randstate class method functions.
* Support for condition operator on class objects (#4214). [Ryszard Rozak, Antmicro Ltd]
* Support array max (#4275). [Aleksander Kiryk, Antmicro Ltd]
* Optimize VPI callValueCbs (#4155). [Hennadii Chernyshchyk]
* Configure for faster C++ linking using 'mold', if it is installed.
* Fix crash on duplicate imported modules (#3231). [Robert Balas]
* Fix false WIDTHEXPAND on array declarations (#3959). [JOTEGO]
* Fix marking overridden methods as coroutines (#4120) (#4169). [Krzysztof Bieganski, Antmicro Ltd]
* Fix SystemC signal copy macro use (#4135). [Josep Sans]
* Fix duplicate static names in blocks in functions (#4144) (#4160). [Stefan Wallentowitz]
* Fix initialization order of initial static after function/task (#4159). [Kamil Rakoczy, Antmicro Ltd]
* Fix linking AstRefDType if it has parameterized class ref (#4164) (#4170). [Ryszard Rozak, Antmicro Ltd]
* Fix crash caused by $display() optimization (#4165) (#4166). [Tudor Timi]
* Fix arrays of unpacked structs (#4173). [Risto Pejašinović]
* Fix $fscanf of decimals overflowing variables (#4174). [Ahmed El-Mahmoudy]
* Fix super.new missing data type (#4147). [Tudor Timi]
* Fix missing class forward declarations (#4151). [Krzysztof Boroński]
* Fix hashes of instances of parameterized classes (#4182). [Ryszard Rozak, Antmicro Ltd]
* Fix forced assignments that override non-continuous assignments (#4183) (#4192). [Krzysztof Bieganski, Antmicro Ltd]
* Fix wide structure VL_TOSTRING_W generation (#4188) (#4189). [Aylon Chaim Porat]
* Fix references to members of parameterized base classes (#4196). [Ryszard Rozak, Antmicro Ltd]
* Fix tracing undefined alignment (#4201) (#4288) [John Wehle]
* Fix class specific same methods for AstVarScope, AstVar, and AstScope (#4203) (#4250). [John Wehle]
* Fix dotted references in parameterized classes (#4206). [Ryszard Rozak, Antmicro Ltd]
* Fix bit selections under parameterized classes (#4210). [Ryszard Rozak, Antmicro Ltd]
* Fix duplicate std:: declaration with -I (#4215). [Harald Pretl]
* Fix deep traversal of class inheritance timing (#4216). [Krzysztof Boroński]
* Fix class parameters of enum types (#4219). [Ryszard Rozak, Antmicro Ltd]
* Fix static methods with prototypes (#4220). [Ryszard Rozak, Antmicro Ltd]
* Fix LATCH warning on function local variables (#4221) (#4284) [Julien Margetts]
* Fix VCD scope types (#4227) (#4282). [Àlex Torregrosa]
* Fix incorrect multi-driven lint warning (#4231) (#4248). [Adrien Le Masle]
* Fix missing assignment for wide unpacked structs (#4233). [Jiamin Zhu]
* Fix unpacked struct == and != operators (#4234) (#4240). [Risto Pejašinović]
* Fix AstStructSel clean when data type is structure (#4241) (#4244). [Risto Pejašinović]
* Fix function calls in with statements (#4245). [Ryszard Rozak, Antmicro Ltd]
* Fix operator == for unpacked struct, if elements are VlUnpacked arrays (#4247). [Risto Pejašinović]
* Fix STATIC lifetime for variables created from clocking items (#4262). [Krzysztof Boroński]
* Fix names of foreach blocks (#4264). [Ryszard Rozak, Antmicro Ltd]
* Fix iterated variables in foreach loops to have VAUTOM lifetimes (#4265). [Krzysztof Boroński]
* Fix missing assignment for wide class members (#4267). [Jiamin Zhu]
* Fix the global uses timing flag when forks exist (#4274). [Krzysztof Bieganski, Antmicro Ltd]
* Fix struct redefinition (#4276). [Aleksander Kiryk, Antmicro Ltd]
* Fix detection of wire/reg duplicates.
* Fix false IMPLICITSTATIC on package functions.
* Fix method calls on function return values.
Verilator 5.010 2023-04-30
==========================

View File

@ -424,17 +424,15 @@ YAPF_FLAGS = -i
yapf:
$(YAPF) $(YAPF_FLAGS) $(PY_FILES)
FLAKE8 = flake8
FLAKE8_FLAGS = \
--extend-exclude=fastcov.py \
--ignore=E123,E129,E251,E402,E501,W503,W504,E701
PYLINT = pylint
PYLINT_FLAGS = --score=n --disable=R0801
RUFF = ruff
RUFF_FLAGS = check --ignore=E402,E501,E701
lint-py:
-$(FLAKE8) $(FLAKE8_FLAGS) $(PY_PROGRAMS)
-$(PYLINT) $(PYLINT_FLAGS) $(PY_PROGRAMS)
-$(RUFF) $(RUFF_FLAGS) $(PY_PROGRAMS)
format-pl-exec:
-chmod a+x test_regress/t/*.pl

View File

@ -13,6 +13,8 @@
:target: https://codecov.io/gh/verilator/verilator
.. image:: https://github.com/verilator/verilator/workflows/build/badge.svg
:target: https://github.com/verilator/verilator/actions?query=workflow%3Abuild
.. image:: https://img.shields.io/docker/pulls/verilator/verilator
:target: https://hub.docker.com/r/verilator/verilator
Welcome to Verilator
@ -57,17 +59,16 @@ files, the "Verilated" code.
These Verilated C++/SystemC files are then compiled by a C++ compiler
(gcc/clang/MSVC++), optionally along with a user's own C++/SystemC wrapper
file to instantiate the Verilated model. Executing the resulting executable
performs the design simulation. Verilator also supports linking Verilated
generated libraries, optionally encrypted, into other simulators.
file, to instantiate the Verilated model. Executing the resulting
executable performs the design simulation. Verilator also supports linking
Verilated generated libraries, optionally encrypted, into other simulators.
Verilator may not be the best choice if you are expecting a full-featured
replacement for a closed-source Verilog simulator, needs SDF annotation,
mixed-signal simulation, or are doing a quick class project (we recommend
`Icarus Verilog`_ for classwork.) However, if you are looking for a path
to migrate SystemVerilog to C++/SystemC, or want high-speed simulation of
synthesizable designs containing limited verification constructs, Verilator
is the tool for you.
designs, Verilator is the tool for you.
Performance
@ -83,11 +84,11 @@ 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 the
closed-source Verilog simulators (Carbon Design Systems Carbonator,
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
cycles/dollar.
simulation cycles/dollar.
Installation & Documentation

View File

@ -372,6 +372,7 @@ detailed descriptions of these arguments.
--make <build-tool> Generate scripts for specified build tool
-MAKEFLAGS <flags> Arguments to pass to make during --build
--main Generate C++ main() file
--main-top-name Specify top name passed to Verilated model in generated C++ main
--max-num-width <value> Maximum number width (default: 64K)
--Mdir <directory> Name of output object directory
--MMD Create .d dependency files

View File

@ -61,7 +61,7 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
sudo apt-get update
sudo apt-get install ccache help2man libfl-dev ||
sudo apt-get install ccache help2man libfl-dev
if [ "$CI_RUNS_ON" != "ubuntu-22.04" ]; then
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ]; then
# Some conflict of libunwind verison on 22.04, can live without it for now
sudo apt-get install libgoogle-perftools-dev ||
sudo apt-get install libgoogle-perftools-dev
@ -70,6 +70,10 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
sudo apt-get install libsystemc libsystemc-dev ||
sudo apt-get install libsystemc libsystemc-dev
fi
if [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
sudo apt-get install mold ||
sudo apt-get install mold
fi
if [ "$COVERAGE" = 1 ]; then
yes yes | sudo cpan -fi Parallel::Forker
fi
@ -102,8 +106,8 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
sudo apt-get install gdb gtkwave lcov libfl-dev ccache
# Required for test_regress/t/t_dist_attributes.pl
if [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
sudo apt-get install libclang-dev ||
sudo apt-get install libclang-dev
sudo apt-get install libclang-dev mold ||
sudo apt-get install libclang-dev mold
pip3 install clang==14.0
fi
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ] || [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then

View File

@ -19,10 +19,16 @@ RUN apt-get update \
ccache \
flex \
git \
help2man \
libfl2 \
libfl-dev \
libgoogle-perftools-dev \
numactl \
perl \
perl-doc \
python3 \
zlib1g \
zlib1g-dev \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
@ -39,7 +45,7 @@ RUN git clone "${REPO}" verilator && \
git checkout "${SOURCE_COMMIT}" && \
autoconf && \
./configure && \
make -j "$(nproc)" && \
make && \
make install && \
cd .. && \
rm -r verilator

View File

@ -5,12 +5,12 @@
# General Public License Version 3 or the Perl Artistic License Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
# When releasing, also update header of Changes file,
# 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.010 2023-04-30],
AC_INIT([Verilator],[5.012 2023-06-13],
[https://verilator.org],
[verilator],[https://verilator.org])
@ -470,6 +470,10 @@ m4_foreach([ldflag], [
AC_SUBST(CFG_LDLIBS_THREADS)
AC_SUBST(CFG_LDFLAGS_THREADS_CMAKE)
# If 'mold' is installed, use it to link for faster buildtimes
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_SRC, -fuse-ld=mold)
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_VERILATED, -fuse-ld=mold)
# When linking partially statically
if test "$CFG_ENABLE_PARTIAL_STATIC" = "yes"; then
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_SRC, -static-libgcc)
@ -480,6 +484,7 @@ else
LTCMALLOC=-ltcmalloc_minimal
fi
AC_SUBST(CFG_LDFLAGS_SRC)
AC_SUBST(CFG_LDFLAGS_VERILATED)
# The pthread library is required by tcmalloc, so add it if it exists. If it
# does not, the tcmalloc check below will fail anyway, and linking against

View File

@ -12,8 +12,10 @@ Alex Chadwick
Aliaksei Chapyzhenka
Ameya Vikram Singh
Andreas Kuster
Andrei Kostovski
Andrew Nolte
Arkadiusz Kozdra
Aylon Chaim Porat
Cameron Kirk
Chris Randall
Chuxuan Wang
@ -24,13 +26,16 @@ David Horton
David Metz
David Stanford
David Turner
Don Williamson
Drew Ranck
Drew Taussig
Driss Hafdi
Edgar E. Iglesias
Eric Rippey
Ethan Sifferman
Eyck Jentzsch
Fan Shupei
february cozzocrea
Felix Neumärker
Felix Yan
Garrett Smith
@ -43,6 +48,7 @@ Guokai Chen
Gustav Svensk
G-A. Kamendje
Harald Heckmann
Hennadii Chernyshchyk
Howard Su
Huang Rui
Huanghuang Zhou
@ -69,7 +75,10 @@ Jiuyang Liu
Joey Liu
John Coiner
John Demme
John Wehle
Jiamin Zhu
Jonathan Drolet
Jose Loyola
Joseph Nwabueze
Josep Sans
Josh Redford
@ -108,6 +117,7 @@ Mostafa Gamal
Nandu Raj
Nathan Kohagen
Nathan Myers
Oleh Maksymenko
Patrick Stewart
Paul Wright
Pawel Sagan
@ -136,6 +146,7 @@ Stephen Henry
Steven Hugg
Sören Tempel
Teng Huang
Tim Hutt
Tim Snyder
Tobias Rosenkranz
Tobias Wölfel
@ -143,6 +154,7 @@ Todd Strader
Tomasz Gorochowik
Topa Topino
Toru Niina
Tudor Timi
Tymoteusz Blazejczyk
Udi Finkelstein
Unai Martinez-Corral

View File

@ -31,7 +31,7 @@ def get_vlt_version():
if match:
try:
data = os.popen('git log -n 1 --pretty=%cs').read()
except Exception:
except Exception: # pylint: disable=broad-except
data = "" # fallback, and Sphinx will fill in today's date
return "Devel " + match.group(1), data
return "unknown", "unknown"

View File

@ -91,9 +91,29 @@ associated programs.
.. option:: VERILATOR_ROOT
Specifies the directory containing the distribution kit. This is used
to find the executable, Perl library, and include files. If not
specified, it will come from a default optionally specified at configure
time (before Verilator was compiled). It should not be specified if
using a pre-compiled Verilator package as the hard-coded value should be
correct. See :ref:`Installation`.
The ``VERILATOR_ROOT`` environment variable is used in several places:
* At ``./configure`` time: If set, it is embedded into the binary, and
at runtime if ``VERILATOR_ROOT`` is not set, the embedded value is
used for the runtime default.
* When ``verilator`` is run: If ``VERILATOR_ROOT`` is set it will be
used to find the ``verilator_bin`` executable (this is the actual
Verilator binary; ``verilator`` is a Perl wrapper). If not set, the
``verilator`` script uses other methods to find ``verilator_bin``
(looking in the same directory and falling back to ``$PATH``).
* When ``make`` is run on the Makefile generated by ``verilator``: The
value of ``VERILATOR_ROOT`` (falling back to the value embedded in the
binary if not set) is used to find the include files
(``include/verilated.mk``).
If you are using a pre-compiled Verilator package, you should not need
to set ``VERILATOR_ROOT`` - the value embedded in the binary should be
correct. In fact this option *does not work* with Verilator packages
that have been installed with ``make install``. If a Verilator package
has been installed using ``./configure --prefix=/some/path && make
install`` and then moved to another location, you cannot use
``VERILATOR_ROOT`` to point to the new version.
See :ref:`Installation` for more details.

View File

@ -808,6 +808,13 @@ Summary:
See also :vlopt:`--binary`.
.. option:: --main-top-name <string>
Specify the name passed to the Verilated model being constructed, in the
generated C++ main() function.
If the string ``"-"`` is used, no top level scope is added.
.. option:: --max-num-width <value>
Set the maximum number literal width (e.g., in 1024'd22 this
@ -1467,8 +1474,11 @@ Summary:
them systematically.
The generated file is in the Verilator Configuration format, see
:ref:`Configuration Files`, and can directly be consumed by
Verilator. The standard file extension is ".vlt".
:ref:`Configuration Files`. The standard file extension is ".vlt".
These files can directly be consumed by Verilator, typically by placing
the filename as part of the Verilator command line options. Waiver files
need to be listed on the command line before listing the files they are
waiving.
.. option:: -Wall

View File

@ -356,8 +356,8 @@ Why do I get "undefined reference to \`VL_RAND_RESET_I' or \`Verilated::...'"?
You need to link your compiled Verilated code against the
:code:`verilated.cpp` file found in the include directory of the Verilator
kit. This is one target in the ``$(VK_GLOBAL_OBJS)`` make variable, which
should be part of your Makefile's link rule. If you use :vlopt:`--exe`,
this is done for you.
should be part of your Makefile's link rule. If you use :vlopt:`--exe` or
:vlopt:`--binary`, this is done for you.
Is the PLI supported?

View File

@ -126,8 +126,10 @@ In specific debug and other modes, it also creates:
- Debugging graph files (from --debug)
* - *{prefix}{misc}*\ .tree
- Debugging files (from --debug)
* - {mod_prefix}_{each_verilog_base_filename}*\ .vpp
- Pre-processed verilog (from --debug)
* - *{prefix}*\ __inputs\ .vpp
- Pre-processed verilog for all files (from --debug)
* - *{prefix}*\ _ *{each_verilog_base_filename}*\ .vpp
- Pre-processed verilog for each file (from --debug)
After running Make, the C++ compiler may produce the following:

View File

@ -39,7 +39,7 @@ In brief, to install from git:
::
# Prerequisites:
#sudo apt-get install git perl python3 make autoconf g++ flex bison ccache
#sudo apt-get install git help2man perl python3 make autoconf g++ flex bison ccache
#sudo apt-get install libgoogle-perftools-dev numactl perl-doc
#sudo apt-get install libfl2 # Ubuntu only (ignore if gives error)
#sudo apt-get install libfl-dev # Ubuntu only (ignore if gives error)
@ -104,6 +104,7 @@ for good performance:
::
sudo apt-get install ccache # If present at build, needed for run
sudo apt-get install mold # If present at build, needed for run
sudo apt-get install libgoogle-perftools-dev numactl
The following is optional but is recommended for nicely rendered command line
@ -124,9 +125,9 @@ Those developing Verilator itself may also want these (see internals.rst):
::
sudo apt-get install gdb graphviz cmake clang clang-format-14 gprof lcov
sudo apt-get install clang clang-format-14 cmake gdb gprof graphviz lcov
sudo apt-get install libclang-dev yapf3
sudo pip3 install clang sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe
sudo pip3 install clang sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe ruff
cpan install Pod::Perldoc
cpan install Parallel::Forker
@ -201,8 +202,9 @@ These are the installation options:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Our personal favorite is to always run Verilator in-place from its Git
directory. This allows the easiest experimentation and upgrading, and
allows many versions of Verilator to co-exist on a system.
directory (don't run ``make install``). This allows the easiest
experimentation and upgrading, and allows many versions of Verilator to
co-exist on a system.
::
@ -211,17 +213,18 @@ allows many versions of Verilator to co-exist on a system.
./configure
# Running will use files from $VERILATOR_ROOT, so no install needed
Note after installing (below steps), a calling program or shell must set
the environment variable :option:`VERILATOR_ROOT` to point to this Git
directory, then execute ``$VERILATOR_ROOT/bin/verilator``, which will find
the path to all needed files.
Note after installing (see `Installation`_), a calling program or shell
must set the environment variable :option:`VERILATOR_ROOT` to point to this
Git directory, then execute ``$VERILATOR_ROOT/bin/verilator``, which will
find the path to all needed files.
2. Install into a specific location
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2. Install into a Specific Prefix
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You may eventually be installing onto a project/company-wide "CAD" tools
disk that may support multiple versions of every tool. Tell configure the
You may be an OS package maintainer building a Verilator package, or you
may eventually be installing onto a project/company-wide "CAD" tools disk
that may support multiple versions of every tool. Tell configure the
eventual destination directory name. We recommend that the destination
location include the Verilator version name:
@ -232,7 +235,8 @@ location include the Verilator version name:
# For the tarball, use the version number instead of git describe
./configure --prefix /CAD_DISK/verilator/`git describe | sed "s/verilator_//"`
Note after installing (below steps), if you use `modulecmd
Note after installing (see `Installation`_), you need to add the path to
the ``bin`` directory to your ``PATH``. Or, if you use `modulecmd
<http://modules.sourceforge.net/>`__, you'll want a module file like the
following:
@ -245,23 +249,7 @@ following:
prepend-path PKG_CONFIG_PATH $install_root/share/pkgconfig
3. Install into a Specific Path
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You may eventually install Verilator into a specific installation prefix,
as most GNU tools support:
::
unset VERILATOR_ROOT # if your shell is bash
unsetenv VERILATOR_ROOT # if your shell is csh
./configure --prefix /opt/verilator-VERSION
Then after installing (below steps), you will need to add
``/opt/verilator-VERSION/bin`` to your ``$PATH`` environment variable.
4. Install System Globally
3. Install System Globally
^^^^^^^^^^^^^^^^^^^^^^^^^^
The final option is to eventually install Verilator globally, using
@ -273,8 +261,8 @@ configure's default system paths:
unsetenv VERILATOR_ROOT # if your shell is csh
./configure
Then after installing (below), the binaries should be in a location
already in your ``$PATH`` environment variable.
Then after installing (see `Installation`_), the binaries should be in a
location already in your ``$PATH`` environment variable.
Configure

View File

@ -143,7 +143,7 @@ Functional Coverage
-------------------
With :vlopt:`--coverage` or :vlopt:`--coverage-user`, Verilator will
translate functional coverage points the user has inserted manually win
translate functional coverage points the user has inserted manually in
SystemVerilog code through into the Verilated model.
Currently, all functional coverage points are specified using SystemVerilog

View File

@ -769,11 +769,14 @@ List Of Warnings
then automatic as static prevents the function from being reentrant,
which may be a source of bugs, and/or performance issues.
If the function does not require static behavior, change it to "function
automatic".
If the function is in a module, and does not require static behavior,
change it to "function automatic".
If the function requires static behavior, change it to "function
static".
If the function is in a module, and requires static behavior, change it
to "function static".
If the function is in a package, it defaults to static, and label the
function's variables as static.
Ignoring this warning will only suppress the lint check; it will
simulate correctly.
@ -972,6 +975,21 @@ List Of Warnings
(neither :vlopt:`--timing` nor :vlopt:`--no-timing` option was provided).
.. option:: NEWERSTD
Warns that a feature requires a newer standard of Verilog or SystemVerilog
than the one specified by the :vlopt:`--language` option. For example, unsized
unbased literals (`'0`, `'1`, `'z`, `'x`) require 1800-2005 or later.
To avoid this warning, use a Verilog or SystemVerilog standard that
supports the feature. Alternatively, modify your code to use a different
syntax that is supported by the Verilog/SystemVerilog standard specified
by the :vlopt:`--language` option.
Ignoring this warning will only suppress the lint check; it will
simulate correctly.
.. option:: NOLATCH
.. TODO better example

View File

@ -638,7 +638,22 @@ condition. See the `Timing Pass` section for more details.
Timing Pass
~~~~~~~~~~~
The visitor in ``V3Timing.cpp`` transforms each timing control into a ``co_await``.
There are two visitors in ``V3Timing.cpp``.
The first one, ``TimingSuspendableVisitor``, does not perform any AST
transformations. It is responsible for marking processes and C++ functions that
contain timing controls as suspendable. Processes that call suspendable
functions are also marked as suspendable. Functions that call, are overridden
by, or override suspendable functions are marked as suspendable as well.
The visitor keeps a dependency graph of functions and processes to handle such
cases. A function or process is dependent on a function if it calls it. A
virtual class method is dependent on another class method if it calls it,
overrides it, or is overriden by it.
The second visitor in ``V3Timing.cpp``, ``TimingControlVisitor``, uses the
information provided by ``TimingSuspendableVisitor`` and transforms each timing
control into a ``co_await``.
* event controls are turned into ``co_await`` on a trigger scheduler's
``trigger`` method. The awaited trigger scheduler is the one corresponding to
@ -670,14 +685,9 @@ Each sub-statement of a ``fork`` is put in an ``AstBegin`` node for easier
grouping. In a later step, each of these gets transformed into a new, separate
function. See the `Forks` section for more detail.
Processes that use awaits are marked as suspendable. Later, during ``V3Sched``,
they are transformed into coroutines. Functions that use awaits get the return
type of ``VlCoroutine``. This immediately makes them coroutines. Note that if a
process calls a function that is a coroutine, the call gets wrapped in an
await, which means the process itself will be marked as suspendable. A virtual
function is a coroutine if any of its overriding or overridden functions are
coroutines. The visitor keeps a dependency graph of functions and processes to
handle such cases.
Suspendable functions get the return type of ``VlCoroutine``, which makes them
coroutines. Later, during ``V3Sched``, suspendable processes are also
transformed into coroutines.
Scheduling with timing
~~~~~~~~~~~~~~~~~~~~~~
@ -1503,8 +1513,19 @@ debug level 5, with the V3Width.cpp file at level 9.
--debug
-------
When you run with ``--debug``, there are two primary output file types
placed into the obj_dir, .tree and .dot files.
When you run with ``--debug``, there are three primary output file types
placed into the obj_dir, .vpp, .tree and .dot files.
.vpp Output
-----------
Verilator creates a *{mod_prefix}*\ __inputs\ .vpp file containing all the
files that were read, filtered by preprocessing. This file can be fed back
into Verilator, replacing on the command line all of the previous input
files, to enable simplification of test cases.
Verilator also creates .vpp files for each individual file passed on the
command line.
.dot Output

View File

@ -260,6 +260,7 @@ Prateek
Pre
Preprocess
Pretet
Pretl
Priyadharshini
Pullup
Pulver
@ -472,6 +473,7 @@ constexpr
constpool
coredump
coroutine
coroutines
countbits
countones
cout
@ -602,6 +604,7 @@ genvar
genvars
getenv
getline
ggdb
gmake
gmon
gotFinish
@ -791,6 +794,7 @@ qrq
radix
randc
randcase
randstate
rarr
rdtsc
reStructuredText
@ -896,6 +900,7 @@ typedefs
typename
uint
un
unbased
undef
undefineall
undriven
@ -966,3 +971,4 @@ ypq
yurivict
zdave
Øyvind

View File

@ -2052,7 +2052,8 @@ if(xc && !xc->already_in_close && !xc->already_in_flush)
int zfd;
int fourpack_duo = 0;
#ifndef __MINGW32__
char *fnam = (char *)malloc(strlen(xc->filename) + 5 + 1);
int fnam_len = strlen(xc->filename) + 5 + 1;
char *fnam = (char *)malloc(fnam_len);
#endif
fixup_offs = ftello(xc->handle);
@ -2142,7 +2143,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush)
fflush(xc->handle);
#ifndef __MINGW32__
sprintf(fnam, "%s.hier", xc->filename);
snprintf(fnam, fnam_len, "%s.hier", xc->filename);
unlink(fnam);
free(fnam);
#endif
@ -2821,7 +2822,7 @@ if(ctx && name && literal_arr && val_arr && (elem_count != 0))
uint32_t i;
name_len = strlen(name);
elem_count_len = sprintf(elem_count_buf, "%" PRIu32, elem_count);
elem_count_len = snprintf(elem_count_buf, 16, "%" PRIu32, elem_count);
literal_lens = (unsigned int *)calloc(elem_count, sizeof(unsigned int));
val_lens = (unsigned int *)calloc(elem_count, sizeof(unsigned int));
@ -3909,7 +3910,8 @@ int pass_status = 1;
if(!xc->fh)
{
fst_off_t offs_cache = ftello(xc->f);
char *fnam = (char *)malloc(strlen(xc->filename) + 6 + 16 + 32 + 1);
int fnam_len = strlen(xc->filename) + 6 + 16 + 32 + 1;
char *fnam = (char *)malloc(fnam_len);
unsigned char *mem = (unsigned char *)malloc(FST_GZIO_LEN);
fst_off_t hl, uclen;
fst_off_t clen = 0;
@ -3928,7 +3930,7 @@ if(!xc->fh)
htyp = xc->contains_hier_section_lz4duo ? FST_BL_HIER_LZ4DUO : FST_BL_HIER_LZ4;
}
sprintf(fnam, "%s.hier_%d_%p", xc->filename, getpid(), (void *)xc);
snprintf(fnam, fnam_len, "%s.hier_%d_%p", xc->filename, getpid(), (void *)xc);
fstReaderFseeko(xc, xc->f, xc->hier_pos, SEEK_SET);
uclen = fstReaderUint64(xc->f);
#ifndef __MINGW32__
@ -4585,15 +4587,17 @@ if(sectype == FST_BL_ZWRAPPER)
int zfd;
int flen = strlen(xc->filename);
char *hf;
int hf_len;
seclen = fstReaderUint64(xc->f);
uclen = fstReaderUint64(xc->f);
if(!seclen) return(0); /* not finished compressing, this is a failed read */
hf = (char *)calloc(1, flen + 16 + 32 + 1);
hf_len = flen + 16 + 32 + 1;
hf = (char *)calloc(1, hf_len);
sprintf(hf, "%s.upk_%d_%p", xc->filename, getpid(), (void *)xc);
snprintf(hf, hf_len, "%s.upk_%d_%p", xc->filename, getpid(), (void *)xc);
fcomp = fopen(hf, "w+b");
if(!fcomp)
{
@ -5189,16 +5193,16 @@ for(;;)
if(beg_tim)
{
if(dumpvars_state == 1) { wx_len = sprintf(wx_buf, "$end\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 2; }
wx_len = sprintf(wx_buf, "#%" PRIu64 "\n", beg_tim);
if(dumpvars_state == 1) { wx_len = snprintf(wx_buf, 32, "$end\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 2; }
wx_len = snprintf(wx_buf, 32, "#%" PRIu64 "\n", beg_tim);
fstWritex(xc, wx_buf, wx_len);
if(!dumpvars_state) { wx_len = sprintf(wx_buf, "$dumpvars\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 1; }
if(!dumpvars_state) { wx_len = snprintf(wx_buf, 32, "$dumpvars\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 1; }
}
if((xc->num_blackouts)&&(cur_blackout != xc->num_blackouts))
{
if(beg_tim == xc->blackout_times[cur_blackout])
{
wx_len = sprintf(wx_buf, "$dump%s $end\n", (xc->blackout_activity[cur_blackout++]) ? "on" : "off");
wx_len = snprintf(wx_buf, 32, "$dump%s $end\n", (xc->blackout_activity[cur_blackout++]) ? "on" : "off");
fstWritex(xc, wx_buf, wx_len);
}
}
@ -5332,7 +5336,7 @@ for(;;)
clone_d[j] = srcdata[7-j];
}
}
sprintf((char *)xc->temp_signal_value_buf, "%.16g", d);
snprintf((char *)xc->temp_signal_value_buf, xc->longest_signal_value_len + 1, "%.16g", d);
value_change_callback(user_callback_data_pointer, beg_tim, idx+1, xc->temp_signal_value_buf);
}
}
@ -5360,7 +5364,7 @@ for(;;)
}
fstVcdID(vcdid_buf, idx+1);
wx_len = sprintf(wx_buf, "r%.16g %s\n", d, vcdid_buf);
wx_len = snprintf(wx_buf, 64, "r%.16g %s\n", d, vcdid_buf);
fstWritex(xc, wx_buf, wx_len);
}
}
@ -5625,16 +5629,16 @@ for(;;)
}
}
if(dumpvars_state == 1) { wx_len = sprintf(wx_buf, "$end\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 2; }
wx_len = sprintf(wx_buf, "#%" PRIu64 "\n", time_table[i]);
if(dumpvars_state == 1) { wx_len = snprintf(wx_buf, 32, "$end\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 2; }
wx_len = snprintf(wx_buf, 32, "#%" PRIu64 "\n", time_table[i]);
fstWritex(xc, wx_buf, wx_len);
if(!dumpvars_state) { wx_len = sprintf(wx_buf, "$dumpvars\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 1; }
if(!dumpvars_state) { wx_len = snprintf(wx_buf, 32, "$dumpvars\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 1; }
if((xc->num_blackouts)&&(cur_blackout != xc->num_blackouts))
{
if(time_table[i] == xc->blackout_times[cur_blackout])
{
wx_len = sprintf(wx_buf, "$dump%s $end\n", (xc->blackout_activity[cur_blackout++]) ? "on" : "off");
wx_len = snprintf(wx_buf, 32, "$dump%s $end\n", (xc->blackout_activity[cur_blackout++]) ? "on" : "off");
fstWritex(xc, wx_buf, wx_len);
}
}
@ -5884,7 +5888,7 @@ for(;;)
clone_d[j] = srcdata[7-j];
}
}
sprintf((char *)xc->temp_signal_value_buf, "%.16g", d);
snprintf((char *)xc->temp_signal_value_buf, xc->longest_signal_value_len + 1, "%.16g", d);
value_change_callback(user_callback_data_pointer, time_table[i], idx+1, xc->temp_signal_value_buf);
}
}
@ -5910,7 +5914,7 @@ for(;;)
}
}
wx_len = sprintf(wx_buf, "r%.16g", d);
wx_len = snprintf(wx_buf, 32, "r%.16g", d);
fstWritex(xc, wx_buf, wx_len);
}
}
@ -6016,7 +6020,7 @@ if(xc->signal_lens[facidx] == 1)
}
}
sprintf((char *)buf, "%.16g", d);
snprintf((char *)buf, 32, "%.16g", d); /* this will write 18 bytes */
}
}
@ -6608,7 +6612,7 @@ if(xc->signal_lens[facidx] == 1)
}
}
sprintf(buf, "r%.16g", d);
snprintf(buf, 32, "r%.16g", d); /* this will write 19 bytes */
return(buf);
}
}

View File

@ -315,15 +315,28 @@ void VlRNG::srandom(uint64_t n) VL_MT_UNSAFE {
if (VL_COUNTONES_I(m_state[0]) < 10) m_state[0] = ~m_state[0];
if (VL_COUNTONES_I(m_state[1]) < 10) m_state[1] = ~m_state[1];
}
// Unused: void VlRNG::set_randstate(const std::string& state) VL_MT_UNSAFE {
// Unused: if (VL_LIKELY(state.length() == sizeof(m_state))) {
// Unused: memcpy(m_state, state.data(), sizeof(m_state));
// Unused: }
// Unused: }
// Unused: std::string VlRNG::get_randstate() const VL_MT_UNSAFE {
// Unused: std::string result{reinterpret_cast<const char *>(&m_state), sizeof(m_state)};
// Unused: return result;
// Unused: }
std::string VlRNG::get_randstate() const VL_MT_UNSAFE {
// Though not stated in IEEE, assumption is the string must be printable
const char* const stateCharsp = reinterpret_cast<const char*>(&m_state);
static_assert(sizeof(m_state) == 16, "");
std::string result{"R00112233445566770011223344556677"};
for (int i = 0; i < sizeof(m_state); ++i) {
result[1 + i * 2] = 'a' + ((stateCharsp[i] >> 4) & 15);
result[1 + i * 2 + 1] = 'a' + (stateCharsp[i] & 15);
}
return result;
}
void VlRNG::set_randstate(const std::string& state) VL_MT_UNSAFE {
if (VL_UNLIKELY((state.length() != 1 + 2 * sizeof(m_state)) || (state[0] != 'R'))) {
VL_PRINTF_MT("%%Warning: set_randstate ignored as state string not from get_randstate\n");
return;
}
char* const stateCharsp = reinterpret_cast<char*>(&m_state);
for (int i = 0; i < sizeof(m_state); ++i) {
stateCharsp[i]
= (((state[1 + i * 2] - 'a') & 15) << 4) | ((state[1 + i * 2 + 1] - 'a') & 15);
}
}
static uint32_t vl_sys_rand32() VL_MT_SAFE {
// Return random 32-bits using system library.
@ -1360,16 +1373,18 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
*p = t_tmp;
} else if (obits <= VL_BYTESIZE) {
CData* const p = va_arg(ap, CData*);
*p = owp[0];
*p = VL_CLEAN_II(obits, obits, owp[0]);
} else if (obits <= VL_SHORTSIZE) {
SData* const p = va_arg(ap, SData*);
*p = owp[0];
*p = VL_CLEAN_II(obits, obits, owp[0]);
} else if (obits <= VL_IDATASIZE) {
IData* const p = va_arg(ap, IData*);
*p = owp[0];
*p = VL_CLEAN_II(obits, obits, owp[0]);
} else if (obits <= VL_QUADSIZE) {
QData* const p = va_arg(ap, QData*);
*p = VL_SET_QW(owp);
*p = VL_CLEAN_QQ(obits, obits, VL_SET_QW(owp));
} else {
_vl_clean_inplace_w(obits, owp);
}
}
} // switch
@ -3213,15 +3228,18 @@ void VerilatedAssertOneThread::fatal_different() VL_MT_SAFE {
//===========================================================================
// VlDeleter:: Methods
void VlDeleter::deleteAll() {
void VlDeleter::deleteAll() VL_EXCLUDES(m_mutex) VL_EXCLUDES(m_deleteMutex) VL_MT_SAFE {
while (true) {
VerilatedLockGuard lock{m_mutex};
if (m_newGarbage.empty()) break;
VerilatedLockGuard deleteLock{m_deleteMutex};
std::swap(m_newGarbage, m_toDelete);
lock.unlock(); // So destructors can enqueue new objects
{
VerilatedLockGuard lock{m_mutex};
if (m_newGarbage.empty()) break;
m_deleteMutex.lock();
std::swap(m_newGarbage, m_toDelete);
// m_mutex is unlocked here, so destructors can enqueue new objects
}
for (VlDeletable* const objp : m_toDelete) delete objp;
m_toDelete.clear();
m_deleteMutex.unlock();
}
}

View File

@ -175,6 +175,7 @@ public:
/// Construct mutex (without locking it)
VerilatedMutex() = default;
~VerilatedMutex() = default;
VL_UNCOPYABLE(VerilatedMutex);
const VerilatedMutex& operator!() const { return *this; } // For -fthread_safety
/// Acquire/lock mutex
void lock() VL_ACQUIRE() VL_MT_SAFE {
@ -218,23 +219,10 @@ public:
/// Construct and hold given mutex lock until destruction or unlock()
explicit VerilatedLockGuard(VerilatedMutex& mutexr) VL_ACQUIRE(mutexr) VL_MT_SAFE
: m_mutexr(mutexr) { // Need () or GCC 4.8 false warning
m_mutexr.lock();
mutexr.lock();
}
/// Destruct and unlock the mutex
~VerilatedLockGuard() VL_RELEASE() { m_mutexr.unlock(); }
/// Lock the mutex
void lock() VL_ACQUIRE() VL_MT_SAFE { m_mutexr.lock(); }
/// Unlock the mutex
void unlock() VL_RELEASE() VL_MT_SAFE { m_mutexr.unlock(); }
/// Acquire/lock mutex and check for stop request.
/// It tries to lock the mutex and if it fails, it check if stop request was send.
/// It returns after locking mutex.
/// This function should be extracted to V3ThreadPool, but due to clang thread-safety
/// limitations it needs to be placed here.
void lockCheckStopRequest(std::function<void()> checkStopRequestFunction)
VL_ACQUIRE() VL_MT_SAFE {
m_mutexr.lockCheckStopRequest(checkStopRequestFunction);
}
};
// Internals: Remember the calling thread at construction time, and make

View File

@ -31,6 +31,8 @@ CFG_CXXFLAGS_NO_UNUSED = @CFG_CXXFLAGS_NO_UNUSED@
CFG_CXXFLAGS_WEXTRA = @CFG_CXXFLAGS_WEXTRA@
# Compiler flags that enable coroutine support
CFG_CXXFLAGS_COROUTINES = @CFG_CXXFLAGS_COROUTINES@
# Linker flags
CFG_LDFLAGS_VERILATED = @CFG_LDFLAGS_VERILATED@
# Linker libraries for multithreading
CFG_LDLIBS_THREADS = @CFG_LDLIBS_THREADS@
@ -77,6 +79,8 @@ VPATH += ..
VPATH += $(VERILATOR_ROOT)/include
VPATH += $(VERILATOR_ROOT)/include/vltstd
LDFLAGS += $(CFG_LDFLAGS_VERILATED)
#OPT = -ggdb -DPRINTINITSTR -DDETECTCHANGE
#OPT = -ggdb -DPRINTINITSTR
CPPFLAGS += $(OPT)

View File

@ -152,9 +152,11 @@ extern const char* vl_mc_scan_plusargs(const char* prefixp) VL_MT_SAFE; // PLIi
// Base macros
// Return true if data[bit] set; not 0/1 return, but 0/non-zero return.
// Arguments must not have side effects
#define VL_BITISSETLIMIT_W(data, width, bit) (((bit) < (width)) && VL_BITISSET_W(data, bit))
// Shift appropriate word by bit. Does not account for wrapping between two words
// Argument 'bit' must not have side effects
#define VL_BITRSHIFT_W(data, bit) ((data)[VL_BITWORD_E(bit)] >> VL_BITBIT_E(bit))
// Create two 32-bit words from quadword
@ -758,6 +760,7 @@ static inline IData VL_ONEHOT0_W(int words, WDataInP const lwp) VL_PURE {
static inline IData VL_CLOG2_I(IData lhs) VL_PURE {
// There are faster algorithms, or fls GCC4 builtins, but rarely used
// In C++20 there will be std::bit_width(lhs) - 1
if (VL_UNLIKELY(!lhs)) return 0;
--lhs;
int shifts = 0;
@ -953,11 +956,19 @@ static inline void VL_NEGATE_INPLACE_W(int words, WDataOutP owp_lwp) VL_MT_SAFE
// EMIT_RULE: VL_MUL: oclean=dirty; lclean==clean; rclean==clean;
// EMIT_RULE: VL_DIV: oclean=dirty; lclean==clean; rclean==clean;
// EMIT_RULE: VL_MODDIV: oclean=dirty; lclean==clean; rclean==clean;
#define VL_DIV_III(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) / (rhs))
#define VL_DIV_QQQ(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) / (rhs))
static inline IData VL_DIV_III(int lbits, IData lhs, IData rhs) {
return (rhs == 0) ? 0 : lhs / rhs;
}
static inline QData VL_DIV_QQQ(int lbits, QData lhs, QData rhs) {
return (rhs == 0) ? 0 : lhs / rhs;
}
#define VL_DIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 0))
#define VL_MODDIV_III(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) % (rhs))
#define VL_MODDIV_QQQ(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) % (rhs))
static inline IData VL_MODDIV_III(int lbits, IData lhs, IData rhs) {
return (rhs == 0) ? 0 : lhs % rhs;
}
static inline QData VL_MODDIV_QQQ(int lbits, QData lhs, QData rhs) {
return (rhs == 0) ? 0 : lhs % rhs;
}
#define VL_MODDIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 1))
static inline WDataOutP VL_ADD_W(int words, WDataOutP owp, WDataInP const lwp,

View File

@ -26,10 +26,6 @@
// verilator lint_off TIMESCALEMOD
// verilator lint_off UNUSEDSIGNAL
package std;
// The process class is not implemented, but it's predeclared here,
// so the linter accepts references to it.
typedef class process;
class mailbox #(type T);
protected int m_bound;
protected T m_queue[$];
@ -45,7 +41,7 @@ package std;
task put(T message);
`ifdef VERILATOR_TIMING
if (m_bound != 0)
wait (m_queue.size() < m_bound);
wait (m_queue.size() < m_bound);
m_queue.push_back(message);
`endif
endtask
@ -117,43 +113,80 @@ package std;
endclass
class process;
typedef enum { FINISHED, RUNNING, WAITING, SUSPENDED, KILLED } state;
static process _s_global_process;
typedef enum {
FINISHED = 0,
RUNNING = 1,
WAITING = 2,
SUSPENDED = 3,
KILLED = 4
} state;
`ifdef VERILATOR_TIMING
// Width visitor changes it to VlProcessRef
protected chandle m_process;
`endif
static function process self();
// Unsupported, emulating with single process' state
if (!_s_global_process) _s_global_process = new;
return _s_global_process;
process p = new;
`ifdef VERILATOR_TIMING
$c(p.m_process, " = vlProcess;");
`endif
return p;
endfunction
protected function void set_status(state s);
`ifdef VERILATOR_TIMING
$c(m_process, "->state(", s, ");");
`endif
endfunction
function state status();
// Unsupported, emulating with single process' state
`ifdef VERILATOR_TIMING
return state'($c(m_process, "->state()"));
`else
return RUNNING;
`endif
endfunction
function void kill();
$error("std::process::kill() not supported");
set_status(KILLED);
endfunction
task await();
$error("std::process::await() not supported");
endtask
function void suspend();
$error("std::process::suspend() not supported");
endfunction
function void resume();
$error("std::process::resume() not supported");
set_status(RUNNING);
endfunction
// When really implemented, srandom must operates on the process, but for
task await();
`ifdef VERILATOR_TIMING
wait (status() == FINISHED || status() == KILLED);
`endif
endtask
// When really implemented, srandom must operate on the process, but for
// now rely on the srandom() that is automatically generated for all
// classes.
//
// function void srandom(int seed);
// endfunction
// The methods below work only if set_randstate is never applied to
// a state string created before another such string. Full support
// could use VlRNG class to store the state per process in VlProcess
// objects.
function string get_randstate();
// Could operate on all proceses for now
// No error, as harmless until set_randstate is called
return "NOT_SUPPORTED";
string s;
s.itoa($random); // Get a random number
set_randstate(s); // Pretend it's the state of RNG
return s;
endfunction
function void set_randstate(string randstate);
$error("std::process::set_randstate() not supported");
// Could operate on all proceses for now
function void set_randstate(string s);
$urandom(s.atoi()); // Set the seed using a string
endfunction
endclass
endpackage

View File

@ -173,7 +173,7 @@ public:
VerilatedLockGuard lock{m_mutex};
while (m_ready.empty()) {
m_waiting = true;
m_cv.wait(lock);
m_cv.wait(m_mutex);
}
m_waiting = false;
// As noted above this is inefficient if our ready list is ever

View File

@ -30,7 +30,16 @@ void VlCoroutineHandle::resume() {
// main process
if (VL_LIKELY(m_coro)) {
VL_DEBUG_IF(VL_DBG_MSGF(" Resuming: "); dump(););
m_coro();
if (m_process) { // If process state is managed with std::process
if (m_process->state() == VlProcess::KILLED) {
m_coro.destroy();
} else {
m_process->state(VlProcess::RUNNING);
m_coro();
}
} else {
m_coro();
}
m_coro = nullptr;
}
}
@ -95,8 +104,9 @@ void VlTriggerScheduler::resume(const char* eventDescription) {
VL_DEBUG_IF(dump(eventDescription);
VL_DBG_MSGF(" Resuming processes waiting for %s\n", eventDescription););
#endif
for (auto& susp : m_ready) susp.resume();
m_ready.clear();
std::swap(m_ready, m_resumeQueue);
for (VlCoroutineHandle& coro : m_resumeQueue) coro.resume();
m_resumeQueue.clear();
commit(eventDescription);
}

View File

@ -86,6 +86,36 @@ public:
#endif
};
//===================================================================
// VlProcess stores metadata of running processes
class VlProcess final {
// MEMBERS
int m_state; // Current state of the process
public:
// TYPES
enum : int { // Type int for compatibility with $c
FINISHED = 0,
RUNNING = 1,
WAITING = 2,
SUSPENDED = 3,
KILLED = 4,
};
// CONSTRUCTORS
VlProcess()
: m_state{RUNNING} {}
// METHODS
int state() { return m_state; }
void state(int s) { m_state = s; }
};
using VlProcessRef = std::shared_ptr<VlProcess>;
inline std::string VL_TO_STRING(const VlProcessRef& p) { return std::string("process"); }
//=============================================================================
// VlCoroutineHandle is a non-copyable (but movable) coroutine handle. On resume, the handle is
// cleared, as we assume that either the coroutine has finished and deleted itself, or, if it got
@ -96,25 +126,38 @@ class VlCoroutineHandle final {
// MEMBERS
std::coroutine_handle<> m_coro; // The wrapped coroutine handle
VlProcessRef m_process; // Data of the suspended process, null if not needed
VlFileLineDebug m_fileline;
public:
// CONSTRUCTORS
// Construct
VlCoroutineHandle()
: m_coro{nullptr} {}
VlCoroutineHandle(std::coroutine_handle<> coro, VlFileLineDebug fileline)
VlCoroutineHandle(VlProcessRef process)
: m_coro{nullptr}
, m_process{process} {
if (m_process) m_process->state(VlProcess::WAITING);
}
VlCoroutineHandle(std::coroutine_handle<> coro, VlProcessRef process, VlFileLineDebug fileline)
: m_coro{coro}
, m_fileline{fileline} {}
, m_process{process}
, m_fileline{fileline} {
if (m_process) m_process->state(VlProcess::WAITING);
}
// Move the handle, leaving a nullptr
VlCoroutineHandle(VlCoroutineHandle&& moved)
: m_coro{std::exchange(moved.m_coro, nullptr)}
, m_process{moved.m_process}
, m_fileline{moved.m_fileline} {}
// Destroy if the handle isn't null
~VlCoroutineHandle() {
// Usually these coroutines should get resumed; we only need to clean up if we destroy a
// model with some coroutines suspended
if (VL_UNLIKELY(m_coro)) m_coro.destroy();
if (VL_UNLIKELY(m_coro)) {
m_coro.destroy();
if (m_process && m_process->state() != VlProcess::KILLED) {
m_process->state(VlProcess::FINISHED);
}
}
}
// METHODS
// Move the handle, leaving a null handle
@ -122,7 +165,7 @@ public:
m_coro = std::exchange(moved.m_coro, nullptr);
return *this;
}
// Resume the coroutine if the handle isn't null
// Resume the coroutine if the handle isn't null and the process isn't killed
void resume();
#ifdef VL_DEBUG
void dump() const;
@ -173,21 +216,24 @@ public:
void dump() const;
#endif
// Used by coroutines for co_awaiting a certain simulation time
auto delay(uint64_t delay, const char* filename = VL_UNKNOWN, int lineno = 0) {
auto delay(uint64_t delay, VlProcessRef process, const char* filename = VL_UNKNOWN,
int lineno = 0) {
struct Awaitable {
VlProcessRef process; // Data of the suspended process, null if not needed
VlDelayedCoroutineQueue& queue;
uint64_t delay;
VlFileLineDebug fileline;
bool await_ready() const { return false; } // Always suspend
void await_suspend(std::coroutine_handle<> coro) {
queue.push_back({delay, VlCoroutineHandle{coro, fileline}});
queue.push_back({delay, VlCoroutineHandle{coro, process, fileline}});
// Move last element to the proper place in the max-heap
std::push_heap(queue.begin(), queue.end());
}
void await_resume() const {}
};
return Awaitable{m_queue, m_context.time() + delay, VlFileLineDebug{filename, lineno}};
return Awaitable{process, m_queue, m_context.time() + delay,
VlFileLineDebug{filename, lineno}};
}
};
@ -207,6 +253,10 @@ class VlTriggerScheduler final {
// (not resumable)
VlCoroutineVec m_ready; // Coroutines that can be resumed (all coros from m_uncommitted are
// moved here in commit())
VlCoroutineVec m_resumeQueue; // Coroutines being resumed by resume(); kept as a field to
// avoid reallocation. Resumed coroutines are moved to
// m_resumeQueue to allow adding coroutines to m_ready
// during resume(). Outside of resume() should always be empty.
public:
// METHODS
@ -220,21 +270,23 @@ public:
void dump(const char* eventDescription) const;
#endif
// Used by coroutines for co_awaiting a certain trigger
auto trigger(const char* eventDescription = VL_UNKNOWN, const char* filename = VL_UNKNOWN,
int lineno = 0) {
auto trigger(bool commit, VlProcessRef process, const char* eventDescription = VL_UNKNOWN,
const char* filename = VL_UNKNOWN, int lineno = 0) {
VL_DEBUG_IF(VL_DBG_MSGF(" Suspending process waiting for %s at %s:%d\n",
eventDescription, filename, lineno););
struct Awaitable {
VlCoroutineVec& suspended; // Coros waiting on trigger
VlProcessRef process; // Data of the suspended process, null if not needed
VlFileLineDebug fileline;
bool await_ready() const { return false; } // Always suspend
void await_suspend(std::coroutine_handle<> coro) {
suspended.emplace_back(coro, fileline);
suspended.emplace_back(coro, process, fileline);
}
void await_resume() const {}
};
return Awaitable{m_uncommitted, VlFileLineDebug{filename, lineno}};
return Awaitable{commit ? m_ready : m_uncommitted, process,
VlFileLineDebug{filename, lineno}};
}
};
@ -267,18 +319,19 @@ class VlDynamicTriggerScheduler final {
// with destructive post updates, e.g. named events)
// METHODS
auto awaitable(VlCoroutineVec& queue, const char* filename, int lineno) {
auto awaitable(VlProcessRef process, VlCoroutineVec& queue, const char* filename, int lineno) {
struct Awaitable {
VlProcessRef process; // Data of the suspended process, null if not needed
VlCoroutineVec& suspended; // Coros waiting on trigger
VlFileLineDebug fileline;
bool await_ready() const { return false; } // Always suspend
void await_suspend(std::coroutine_handle<> coro) {
suspended.emplace_back(coro, fileline);
suspended.emplace_back(coro, process, fileline);
}
void await_resume() const {}
};
return Awaitable{queue, VlFileLineDebug{filename, lineno}};
return Awaitable{process, queue, VlFileLineDebug{filename, lineno}};
}
public:
@ -292,23 +345,26 @@ public:
void dump() const;
#endif
// Used by coroutines for co_awaiting trigger evaluation
auto evaluation(const char* eventDescription, const char* filename, int lineno) {
auto evaluation(VlProcessRef process, const char* eventDescription, const char* filename,
int lineno) {
VL_DEBUG_IF(VL_DBG_MSGF(" Suspending process waiting for %s at %s:%d\n",
eventDescription, filename, lineno););
return awaitable(m_suspended, filename, lineno);
return awaitable(process, m_suspended, filename, lineno);
}
// Used by coroutines for co_awaiting the trigger post update step
auto postUpdate(const char* eventDescription, const char* filename, int lineno) {
auto postUpdate(VlProcessRef process, const char* eventDescription, const char* filename,
int lineno) {
VL_DEBUG_IF(
VL_DBG_MSGF(" Process waiting for %s at %s:%d awaiting the post update step\n",
eventDescription, filename, lineno););
return awaitable(m_post, filename, lineno);
return awaitable(process, m_post, filename, lineno);
}
// Used by coroutines for co_awaiting the resumption step (in 'act' eval)
auto resumption(const char* eventDescription, const char* filename, int lineno) {
auto resumption(VlProcessRef process, const char* eventDescription, const char* filename,
int lineno) {
VL_DEBUG_IF(VL_DBG_MSGF(" Process waiting for %s at %s:%d awaiting resumption\n",
eventDescription, filename, lineno););
return awaitable(m_triggered, filename, lineno);
return awaitable(process, m_triggered, filename, lineno);
}
};
@ -338,24 +394,27 @@ class VlForkSync final {
public:
// Create the join object and set the counter to the specified number
void init(size_t count) { m_join.reset(new VlJoin{count, {}}); }
void init(size_t count, VlProcessRef process) { m_join.reset(new VlJoin{count, {process}}); }
// Called whenever any of the forked processes finishes. If the join counter reaches 0, the
// main process gets resumed
void done(const char* filename = VL_UNKNOWN, int lineno = 0);
// Used by coroutines for co_awaiting a join
auto join(const char* filename = VL_UNKNOWN, int lineno = 0) {
auto join(VlProcessRef process, const char* filename = VL_UNKNOWN, int lineno = 0) {
assert(m_join);
VL_DEBUG_IF(
VL_DBG_MSGF(" Awaiting join of fork at: %s:%d\n", filename, lineno););
struct Awaitable {
VlProcessRef process; // Data of the suspended process, null if not needed
const std::shared_ptr<VlJoin> join; // Join to await on
VlFileLineDebug fileline;
bool await_ready() { return join->m_counter == 0; } // Suspend if join still exists
void await_suspend(std::coroutine_handle<> coro) { join->m_susp = {coro, fileline}; }
void await_suspend(std::coroutine_handle<> coro) {
join->m_susp = {coro, process, fileline};
}
void await_resume() const {}
};
return Awaitable{m_join, VlFileLineDebug{filename, lineno}};
return Awaitable{process, m_join, VlFileLineDebug{filename, lineno}};
}
};

View File

@ -75,7 +75,7 @@ public:
// Get an element from the front of the queue. Blocks if none available
T get() VL_MT_SAFE_EXCLUDES(m_mutex) {
VerilatedLockGuard lock{m_mutex};
m_cv.wait(lock, [this]() VL_REQUIRES(m_mutex) { return !m_queue.empty(); });
m_cv.wait(m_mutex, [this]() VL_REQUIRES(m_mutex) { return !m_queue.empty(); });
assert(!m_queue.empty());
T value = m_queue.front();
m_queue.pop_front();
@ -447,7 +447,9 @@ public:
if (VL_UNLIKELY(diff)) fullIData(oldp, newval, bits);
}
VL_ATTR_ALWINLINE void chgQData(uint32_t* oldp, QData newval, int bits) {
const uint64_t diff = *reinterpret_cast<QData*>(oldp) ^ newval;
QData old;
std::memcpy(&old, oldp, sizeof(old));
const uint64_t diff = old ^ newval;
if (VL_UNLIKELY(diff)) fullQData(oldp, newval, bits);
}
VL_ATTR_ALWINLINE void chgWData(uint32_t* oldp, const WData* newvalp, int bits) {
@ -460,8 +462,9 @@ public:
}
VL_ATTR_ALWINLINE void chgEvent(uint32_t* oldp, VlEvent newval) { fullEvent(oldp, newval); }
VL_ATTR_ALWINLINE void chgDouble(uint32_t* oldp, double newval) {
// cppcheck-suppress invalidPointerCast
if (VL_UNLIKELY(*reinterpret_cast<double*>(oldp) != newval)) fullDouble(oldp, newval);
double old;
std::memcpy(&old, oldp, sizeof(old));
if (VL_UNLIKELY(old != newval)) fullDouble(oldp, newval);
}
};

View File

@ -482,7 +482,7 @@ VL_ATTR_NOINLINE void VerilatedTrace<VL_SUB_T, VL_BUF_T>::ParallelWorkerData::wa
// We have been spinning for a while, so yield the thread
VerilatedLockGuard lock{m_mutex};
m_waiting = true;
m_cv.wait(lock, [this] { return m_ready.load(std::memory_order_relaxed); });
m_cv.wait(m_mutex, [this] { return m_ready.load(std::memory_order_relaxed); });
m_waiting = false;
}
@ -872,7 +872,7 @@ void VerilatedTraceBuffer<VL_BUF_T>::fullIData(uint32_t* oldp, IData newval, int
template <>
void VerilatedTraceBuffer<VL_BUF_T>::fullQData(uint32_t* oldp, QData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*reinterpret_cast<QData*>(oldp) = newval;
std::memcpy(oldp, &newval, sizeof(newval));
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
emitQData(code, newval, bits);
}
@ -888,7 +888,7 @@ void VerilatedTraceBuffer<VL_BUF_T>::fullWData(uint32_t* oldp, const WData* newv
template <>
void VerilatedTraceBuffer<VL_BUF_T>::fullDouble(uint32_t* oldp, double newval) {
const uint32_t code = oldp - m_sigs_oldvalp;
*reinterpret_cast<double*>(oldp) = newval;
std::memcpy(oldp, &newval, sizeof(newval));
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
// cppcheck-suppress invalidPointerCast
emitDouble(code, newval);

View File

@ -161,8 +161,8 @@ public:
VlRNG() VL_MT_SAFE;
explicit VlRNG(uint64_t seed0) VL_MT_SAFE : m_state{0x12341234UL, seed0} {}
void srandom(uint64_t n) VL_MT_UNSAFE;
// Unused: std::string get_randstate() const VL_MT_UNSAFE;
// Unused: void set_randstate(const std::string& state) VL_MT_UNSAFE;
std::string get_randstate() const VL_MT_UNSAFE;
void set_randstate(const std::string& state) VL_MT_UNSAFE;
uint64_t rand64() VL_MT_UNSAFE;
// Threadsafe, but requires use on vl_thread_rng
static uint64_t vl_thread_rng_rand64() VL_MT_SAFE;
@ -1040,9 +1040,15 @@ void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
template <class T_Value, std::size_t T_Depth>
struct VlUnpacked final {
private:
// TYPES
using T_Key = IData; // Index type, for uniformity with other containers
using Unpacked = T_Value[T_Depth];
public:
// MEMBERS
// This should be the only data member, otherwise generated static initializers need updating
T_Value m_storage[T_Depth]; // Contents of the unpacked array
Unpacked m_storage; // Contents of the unpacked array
// CONSTRUCTORS
// Default constructors and destructor are used. Note however that C++20 requires that
@ -1053,6 +1059,7 @@ struct VlUnpacked final {
// Default copy assignment operators are used.
// METHODS
public:
// Raw access
WData* data() { return &m_storage[0]; }
const WData* data() const { return &m_storage[0]; }
@ -1065,6 +1072,170 @@ struct VlUnpacked final {
bool neq(const VlUnpacked<T_Value, T_Depth>& that) const { return neq(*this, that); }
// 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); }
void sort() { std::sort(std::begin(m_storage), std::end(m_storage)); }
template <typename Func>
void sort(Func with_func) {
// with_func returns arbitrary type to use for the sort comparison
std::sort(std::begin(m_storage), std::end(m_storage),
[=](const T_Value& a, const T_Value& b) {
// index number is meaningless with sort, as it changes
return with_func(0, a) < with_func(0, b);
});
}
// std::rbegin/std::rend not available until C++14
void rsort() {
std::sort(std::begin(m_storage), std::end(m_storage), std::greater<T_Value>());
}
template <typename Func>
void rsort(Func with_func) {
// with_func returns arbitrary type to use for the sort comparison
// std::rbegin/std::rend not available until C++14, so using > below
std::sort(std::begin(m_storage), std::end(m_storage),
[=](const T_Value& a, const T_Value& b) {
// index number is meaningless with sort, as it changes
return with_func(0, a) > with_func(0, b);
});
}
void reverse() { std::reverse(std::begin(m_storage), std::end(m_storage)); }
void shuffle() { std::shuffle(std::begin(m_storage), std::end(m_storage), VlURNG{}); }
VlQueue<T_Value> unique() const {
VlQueue<T_Value> out;
std::set<T_Value> saw;
for (const auto& i : m_storage) {
const auto it = saw.find(i);
if (it == saw.end()) {
saw.insert(it, i);
out.push_back(i);
}
}
return out;
}
template <typename Func>
VlQueue<T_Value> unique(Func with_func) const {
VlQueue<T_Value> out;
std::set<T_Value> saw;
for (const auto& i : m_storage) {
const auto i_mapped = with_func(0, i);
const auto it = saw.find(i_mapped);
if (it == saw.end()) {
saw.insert(it, i_mapped);
out.push_back(i);
}
}
return out;
}
VlQueue<T_Key> unique_index() const {
VlQueue<T_Key> out;
IData index = 0;
std::set<T_Value> saw;
for (const auto& i : m_storage) {
const auto it = saw.find(i);
if (it == saw.end()) {
saw.insert(it, i);
out.push_back(index);
}
++index;
}
return out;
}
template <typename Func>
VlQueue<T_Key> unique_index(Func with_func) const {
VlQueue<T_Key> out;
IData index = 0;
std::unordered_set<T_Value> saw;
for (const auto& i : m_storage) {
const auto i_mapped = with_func(index, i);
auto it = saw.find(i_mapped);
if (it == saw.end()) {
saw.insert(it, i_mapped);
out.push_back(index);
}
++index;
}
return out;
}
template <typename Func>
VlQueue<T_Value> find(Func with_func) const {
VlQueue<T_Value> out;
IData index = 0;
for (const auto& i : m_storage) {
if (with_func(index, i)) out.push_back(i);
++index;
}
return out;
}
template <typename Func>
VlQueue<T_Key> find_index(Func with_func) const {
VlQueue<T_Key> out;
IData index = 0;
for (const auto& i : m_storage) {
if (with_func(index, i)) out.push_back(index);
++index;
}
return out;
}
template <typename Func>
VlQueue<T_Value> find_first(Func with_func) const {
// Can't use std::find_if as need index number
IData index = 0;
for (const auto& i : m_storage) {
if (with_func(index, i)) return VlQueue<T_Value>::cons(i);
++index;
}
return VlQueue<T_Value>{};
}
template <typename Func>
VlQueue<T_Key> find_first_index(Func with_func) const {
IData index = 0;
for (const auto& i : m_storage) {
if (with_func(index, i)) return VlQueue<IData>::cons(index);
++index;
}
return VlQueue<T_Key>{};
}
template <typename Func>
VlQueue<T_Value> find_last(Func with_func) const {
for (int i = T_Depth - 1; i >= 0; i--) {
if (with_func(i, m_storage[i])) return VlQueue<T_Value>::cons(m_storage[i]);
}
return VlQueue<T_Value>{};
}
template <typename Func>
VlQueue<T_Key> find_last_index(Func with_func) const {
for (int i = T_Depth - 1; i >= 0; i--) {
if (with_func(i, m_storage[i])) return VlQueue<IData>::cons(i);
}
return VlQueue<T_Key>{};
}
// Reduction operators
VlQueue<T_Value> min() const {
const auto it = std::min_element(std::begin(m_storage), std::end(m_storage));
return VlQueue<T_Value>::cons(*it);
}
template <typename Func>
VlQueue<T_Value> min(Func with_func) const {
const auto it = std::min_element(std::begin(m_storage), std::end(m_storage),
[&with_func](const IData& a, const IData& b) {
return with_func(0, a) < with_func(0, b);
});
return VlQueue<T_Value>::cons(*it);
}
VlQueue<T_Value> max() const {
const auto it = std::max_element(std::begin(m_storage), std::end(m_storage));
return VlQueue<T_Value>::cons(*it);
}
template <typename Func>
VlQueue<T_Value> max(Func with_func) const {
const auto it = std::max_element(std::begin(m_storage), std::end(m_storage),
[&with_func](const IData& a, const IData& b) {
return with_func(0, a) < with_func(0, b);
});
return VlQueue<T_Value>::cons(*it);
}
// Dumping. Verilog: str = $sformatf("%p", assoc)
std::string to_string() const {
@ -1138,7 +1309,7 @@ public:
}
// Deletes all queued garbage objects.
void deleteAll() VL_MT_SAFE;
void deleteAll() VL_EXCLUDES(m_mutex) VL_EXCLUDES(m_deleteMutex) VL_MT_SAFE;
};
//===================================================================

View File

@ -410,28 +410,14 @@ void VerilatedVcd::dumpHeader() {
if (*np == ' ') np++;
if (*np == '\t') break; // tab means signal name starts
printIndent(1);
// Find character after name end
const char* sp = np;
while (*sp && *sp != ' ' && *sp != '\t' && !(*sp & '\x80')) sp++;
printStr("$scope ");
if (*sp & '\x80') {
switch (*sp & 0x7f) {
case VLT_TRACE_SCOPE_STRUCT: printStr("struct "); break;
case VLT_TRACE_SCOPE_INTERFACE: printStr("interface "); break;
case VLT_TRACE_SCOPE_UNION: printStr("union "); break;
default: printStr("module ");
}
} else {
printStr("module ");
}
printStr("$scope module ");
for (; *np && *np != ' ' && *np != '\t'; np++) {
if (*np == '[') {
printStr("[");
} else if (*np == ']') {
printStr("]");
} else if (!(*np & '\x80')) {
} else {
*m_writep++ = *np;
}
}

View File

@ -165,6 +165,7 @@ class VerilatedVpioVarBase VL_NOT_FINAL : public VerilatedVpio {
protected:
const VerilatedVar* m_varp = nullptr;
const VerilatedScope* m_scopep = nullptr;
std::string m_fullname;
const VerilatedRange& get_range() const {
// Determine number of dimensions and return outermost
return (m_varp->dims() > 1) ? m_varp->unpacked() : m_varp->packed();
@ -173,11 +174,13 @@ protected:
public:
VerilatedVpioVarBase(const VerilatedVar* varp, const VerilatedScope* scopep)
: m_varp{varp}
, m_scopep{scopep} {}
, m_scopep{scopep}
, m_fullname{std::string{m_scopep->name()} + '.' + name()} {}
explicit VerilatedVpioVarBase(const VerilatedVpioVarBase* varp) {
if (varp) {
m_varp = varp->m_varp;
m_scopep = varp->m_scopep;
m_fullname = varp->m_fullname;
}
}
static VerilatedVpioVarBase* castp(vpiHandle h) {
@ -188,11 +191,7 @@ public:
uint32_t size() const override { return get_range().elements(); }
const VerilatedRange* rangep() const override { return &get_range(); }
const char* name() const override { return m_varp->name(); }
const char* fullname() const override {
static thread_local std::string t_out;
t_out = std::string{m_scopep->name()} + "." + name();
return t_out.c_str();
}
const char* fullname() const override { return m_fullname.c_str(); }
};
class VerilatedVpioParam final : public VerilatedVpioVarBase {
@ -646,23 +645,21 @@ public:
continue;
}
VerilatedVpiCbHolder& ho = *it++;
if (VerilatedVpioVar* const varop = VerilatedVpioVar::castp(ho.cb_datap()->obj)) {
void* const newDatap = varop->varDatap();
void* const prevDatap
= varop->prevDatap(); // Was malloced when we added the callback
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n",
varop->fullname(), *(static_cast<CData*>(newDatap)),
*(static_cast<CData*>(prevDatap)), newDatap,
prevDatap););
if (std::memcmp(prevDatap, newDatap, varop->entSize()) != 0) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" PRId64 " %s v[0]=%d\n",
ho.id(), varop->fullname(),
*(static_cast<CData*>(newDatap))););
update.insert(varop);
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
(ho.cb_rtnp())(ho.cb_datap());
called = true;
}
VerilatedVpioVar* const varop
= reinterpret_cast<VerilatedVpioVar*>(ho.cb_datap()->obj);
void* const newDatap = varop->varDatap();
void* const prevDatap = varop->prevDatap(); // Was malloced when we added the callback
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n",
varop->fullname(), *(static_cast<CData*>(newDatap)),
*(static_cast<CData*>(prevDatap)), newDatap, prevDatap););
if (std::memcmp(prevDatap, newDatap, varop->entSize()) != 0) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" PRId64 " %s v[0]=%d\n",
ho.id(), varop->fullname(),
*(static_cast<CData*>(newDatap))););
update.insert(varop);
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
(ho.cb_rtnp())(ho.cb_datap());
called = true;
}
if (was_last) break;
}

View File

@ -37,8 +37,13 @@
//=========================================================================
// Compiler pragma abstraction
#if defined(__clang__)
# define VL_CLANG_ATTR(attr) __attribute__(( attr ))
#else
# define VL_CLANG_ATTR(attr)
#endif
#ifdef __GNUC__
# define VL_ATTR_ALIGNED(alignment) __attribute__((aligned(alignment)))
# define VL_ATTR_ALWINLINE __attribute__((always_inline)) inline
# define VL_ATTR_NOINLINE __attribute__((noinline))
# define VL_ATTR_COLD __attribute__((cold))
@ -57,19 +62,6 @@
// All VL_ATTR_WEAK symbols must be marked with the macOS -U linker flag in verilated.mk.in
# define VL_ATTR_WEAK __attribute__((weak))
# endif
# if defined(__clang__)
# define VL_ACQUIRE(...) __attribute__((annotate("ACQUIRE"))) __attribute__((acquire_capability(__VA_ARGS__)))
# define VL_ACQUIRE_SHARED(...) __attribute__((annotate("ACQUIRE_SHARED"))) __attribute__((acquire_shared_capability(__VA_ARGS__)))
# define VL_RELEASE(...) __attribute__((annotate("RELEASE"))) __attribute__((release_capability(__VA_ARGS__)))
# define VL_RELEASE_SHARED(...) __attribute__((annotate("RELEASE_SHARED"))) __attribute__((release_shared_capability(__VA_ARGS__)))
# define VL_TRY_ACQUIRE(...) __attribute__((try_acquire_capability(__VA_ARGS__)))
# define VL_TRY_ACQUIRE_SHARED(...) __attribute__((try_acquire_shared_capability(__VA_ARGS__)))
# define VL_CAPABILITY(x) __attribute__((capability(x)))
# define VL_REQUIRES(x) __attribute__((annotate("REQUIRES"))) __attribute__((requires_capability(x)))
# define VL_GUARDED_BY(x) __attribute__((annotate("GUARDED_BY"))) __attribute__((guarded_by(x)))
# define VL_EXCLUDES(x) __attribute__((annotate("EXCLUDES"))) __attribute__((locks_excluded(x)))
# define VL_SCOPED_CAPABILITY __attribute__((scoped_lockable))
# endif
# define VL_LIKELY(x) __builtin_expect(!!(x), 1) // Prefer over C++20 [[likely]]
# define VL_UNLIKELY(x) __builtin_expect(!!(x), 0) // Prefer over C++20 [[unlikely]]
# define VL_UNREACHABLE __builtin_unreachable() // C++23 std::unreachable()
@ -77,10 +69,58 @@
# define VL_PREFETCH_RW(p) __builtin_prefetch((p), 1)
#endif
// Function acquires a capability/lock (-fthread-safety)
#define VL_ACQUIRE(...) \
VL_CLANG_ATTR(annotate("ACQUIRE")) \
VL_CLANG_ATTR(acquire_capability(__VA_ARGS__))
// Function acquires a shared capability/lock (-fthread-safety)
#define VL_ACQUIRE_SHARED(...) \
VL_CLANG_ATTR(annotate("ACQUIRE_SHARED")) \
VL_CLANG_ATTR(acquire_shared_capability(__VA_ARGS__))
// Function releases a capability/lock (-fthread-safety)
#define VL_RELEASE(...) \
VL_CLANG_ATTR(annotate("RELEASE")) \
VL_CLANG_ATTR(release_capability(__VA_ARGS__))
// Function releases a shared capability/lock (-fthread-safety)
#define VL_RELEASE_SHARED(...) \
VL_CLANG_ATTR(annotate("RELEASE_SHARED")) \
VL_CLANG_ATTR(release_shared_capability(__VA_ARGS__))
// Function returns bool if acquired a capability (-fthread-safety)
#define VL_TRY_ACQUIRE(...) \
VL_CLANG_ATTR(try_acquire_capability(__VA_ARGS__))
// Function returns bool if acquired shared (-fthread-safety)
#define VL_TRY_ACQUIRE_SHARED(...) \
VL_CLANG_ATTR(try_acquire_shared_capability(__VA_ARGS__))
// Function requires a capability inbound (-fthread-safety)
#define VL_CAPABILITY(x) \
VL_CLANG_ATTR(capability(x))
// Function requires not having a capability inbound (-fthread-safety)
#define VL_REQUIRES(x) \
VL_CLANG_ATTR(annotate("REQUIRES")) \
VL_CLANG_ATTR(requires_capability(x))
// Name of capability/lock (-fthread-safety)
#define VL_GUARDED_BY(x) \
VL_CLANG_ATTR(annotate("GUARDED_BY")) \
VL_CLANG_ATTR(guarded_by(x))
// The data that the annotated pointer points to is protected by the given capability.
// The pointer itself is not protected.
// Allowed on: pointer data member. (-fthread-safety)
#define VL_PT_GUARDED_BY(x) \
VL_CLANG_ATTR(annotate("PT_GUARDED_BY")) \
VL_CLANG_ATTR(pt_guarded_by(x))
// Name of mutex protecting this variable (-fthread-safety)
#define VL_EXCLUDES(x) \
VL_CLANG_ATTR(annotate("EXCLUDES")) \
VL_CLANG_ATTR(locks_excluded(x))
// Scoped threaded capability/lock (-fthread-safety)
#define VL_SCOPED_CAPABILITY \
VL_CLANG_ATTR(scoped_lockable)
// Annotated function returns reference to the given capability.
// Allowed on: function, method. (-fthread-safety)
#define VL_RETURN_CAPABILITY(x) \
VL_CLANG_ATTR(lock_returned(x))
// Defaults for unsupported compiler features
#ifndef VL_ATTR_ALIGNED
# define VL_ATTR_ALIGNED(alignment) ///< Attribute to align structure to byte alignment
#endif
#ifndef VL_ATTR_ALWINLINE
# define VL_ATTR_ALWINLINE ///< Attribute to inline, even when not optimizing
#endif
@ -111,19 +151,6 @@
#ifndef VL_ATTR_WEAK
# define VL_ATTR_WEAK ///< Attribute that function external that is optionally defined
#endif
#ifndef VL_CAPABILITY
# define VL_ACQUIRE(...) ///< Function acquires a capability/lock (-fthread-safety)
# define VL_ACQUIRE_SHARED(...) ///< Function acquires a shared capability/lock (-fthread-safety)
# define VL_RELEASE(...) ///< Function releases a capability/lock (-fthread-safety)
# define VL_RELEASE_SHARED(...) ///< Function releases a shared capability/lock (-fthread-safety)
# define VL_TRY_ACQUIRE(...) ///< Function returns bool if acquired a capability (-fthread-safety)
# define VL_TRY_ACQUIRE_SHARED(...) ///< Function returns bool if acquired shared (-fthread-safety)
# define VL_REQUIRES(x) ///< Function requires a capability inbound (-fthread-safety)
# define VL_EXCLUDES(x) ///< Function requires not having a capability inbound (-fthread-safety)
# define VL_CAPABILITY(x) ///< Name of capability/lock (-fthread-safety)
# define VL_GUARDED_BY(x) ///< Name of mutex protecting this variable (-fthread-safety)
# define VL_SCOPED_CAPABILITY ///< Scoped threaded capability/lock (-fthread-safety)
#endif
#ifndef VL_LIKELY
# define VL_LIKELY(x) (!!(x)) ///< Return boolean expression that is more often true
# define VL_UNLIKELY(x) (!!(x)) ///< Return boolean expression that is more often false
@ -142,6 +169,7 @@
#ifndef VL_NO_LEGACY
# define VL_ATTR_ALIGNED(alignment) // Deprecated
# define VL_FUNC __func__ // Deprecated
# define VL_THREAD // Deprecated
# define VL_THREAD_LOCAL thread_local // Deprecated
@ -149,56 +177,24 @@
#endif
// Comment tag that Function is pure (and thus also VL_MT_SAFE)
#if defined(__clang__)
# define VL_PURE __attribute__((annotate("PURE")))
#else
# define VL_PURE
#endif
#define VL_PURE VL_CLANG_ATTR(annotate("PURE"))
// Comment tag that function is threadsafe
#if defined(__clang__)
# define VL_MT_SAFE __attribute__((annotate("MT_SAFE")))
#else
# define VL_MT_SAFE
#endif
#define VL_MT_SAFE VL_CLANG_ATTR(annotate("MT_SAFE"))
// Comment tag that function is threadsafe, only if
// other threads doesn't change tree topology
#if defined(__clang__)
# define VL_MT_STABLE __attribute__((annotate("MT_STABLE")))
#else
# define VL_MT_STABLE
#endif
#define VL_MT_STABLE VL_CLANG_ATTR(annotate("MT_STABLE"))
// Comment tag that function is threadsafe, only
// during normal operation (post-init)
#if defined(__clang__)
# define VL_MT_SAFE_POSTINIT __attribute__((annotate("MT_SAFE_POSTINIT")))
#else
# define VL_MT_SAFE_POSTINIT
#endif
#define VL_MT_SAFE_POSTINIT VL_CLANG_ATTR(annotate("MT_SAFE_POSTINIT"))
// Attribute that function is clang threadsafe and uses given mutex
#if defined(__clang__)
# define VL_MT_SAFE_EXCLUDES(mutex) __attribute__((annotate("MT_SAFE_EXCLUDES"))) VL_EXCLUDES(mutex)
#else
# define VL_MT_SAFE_EXCLUDES(mutex) VL_EXCLUDES(mutex)
#endif
#define VL_MT_SAFE_EXCLUDES(mutex) VL_CLANG_ATTR(annotate("MT_SAFE_EXCLUDES")) VL_EXCLUDES(mutex)
// Comment tag that function is not threadsafe
#if defined(__clang__)
# define VL_MT_UNSAFE __attribute__((annotate("MT_UNSAFE")))
#else
# define VL_MT_UNSAFE
#endif
#define VL_MT_UNSAFE VL_CLANG_ATTR(annotate("MT_UNSAFE"))
// Comment tag that function is not threadsafe
// protected to make sure single-caller
#if defined(__clang__)
# define VL_MT_UNSAFE_ONE __attribute__((annotate("MT_UNSAFE_ONE")))
#else
# define VL_MT_UNSAFE_ONE
#endif
#define VL_MT_UNSAFE_ONE VL_CLANG_ATTR(annotate("MT_UNSAFE_ONE"))
// Comment tag that function is entry point of parallelization
#if defined(__clang__)
# define VL_MT_START __attribute__((annotate("MT_START")))
#else
# define VL_MT_START
#endif
#define VL_MT_START VL_CLANG_ATTR(annotate("MT_START"))
#ifndef VL_NO_LEGACY
# define VL_ULL(c) (c##ULL) // Add appropriate suffix to 64-bit constant (deprecated)
@ -324,6 +320,7 @@ extern "C" void __gcov_dump();
// Now that C++ requires these standard types the vl types are deprecated
#include <cstdint>
#include <cinttypes>
#include <cmath>
#ifndef VL_NO_LEGACY
using vluint8_t = uint8_t; ///< 8-bit unsigned type (backward compatibility)
@ -448,11 +445,14 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
#define VL_SIZEBITS_E (VL_EDATASIZE - 1) ///< Bit mask for bits in a quad
/// Return mask for words with 1's where relevant bits are (0=all bits)
/// Arguments must not have side effects
#define VL_MASK_I(nbits) (((nbits) & VL_SIZEBITS_I) ? ((1U << ((nbits) & VL_SIZEBITS_I)) - 1) : ~0)
/// Return mask for quads with 1's where relevant bits are (0=all bits)
/// Arguments must not have side effects
#define VL_MASK_Q(nbits) \
(((nbits) & VL_SIZEBITS_Q) ? ((1ULL << ((nbits) & VL_SIZEBITS_Q)) - 1ULL) : ~0ULL)
/// Return mask for EData with 1's where relevant bits are (0=all bits)
/// Arguments must not have side effects
#define VL_MASK_E(nbits) VL_MASK_I(nbits)
#define VL_EUL(n) VL_UL(n) // Make constant number EData sized
@ -474,8 +474,12 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
// #defines, to avoid requiring math.h on all compile runs
#ifdef _MSC_VER
# define VL_TRUNC(n) (((n) < 0) ? std::ceil((n)) : std::floor((n)))
# define VL_ROUND(n) (((n) < 0) ? std::ceil((n)-0.5) : std::floor((n) + 0.5))
static inline double VL_TRUNC(double n) {
return (n < 0) ? std::ceil(n) : std::floor(n);
}
static inline double VL_ROUND(double n) {
return (n < 0) ? std::ceil(n-0.5) : std::floor(n + 0.5);
}
#else
# define VL_TRUNC(n) std::trunc(n)
# define VL_ROUND(n) std::round(n)

View File

@ -33,6 +33,26 @@ def fully_qualified_name(node):
return [node.displayname] if node.displayname else []
# Returns True, if `class_node` contains node
# that matches `member` spelling
def check_class_member_exists(class_node, member):
for child in class_node.get_children():
if member.spelling == child.spelling:
return True
return False
# Returns Base class (if found) of `class_node`
# that is of type `base_type`
def get_base_class(class_node, base_type):
for child in class_node.get_children():
if child.kind is CursorKind.CXX_BASE_SPECIFIER:
base_class = child.type
if base_type.spelling == base_class.spelling:
return base_class
return None
@dataclass
class VlAnnotations:
mt_start: bool = False
@ -251,7 +271,10 @@ class CallAnnotationsValidator:
self._call_location: Optional[FunctionInfo] = None
self._caller: Optional[FunctionInfo] = None
self._level: int = 0
self._constructor_context: int = 0
self._constructor_context: list[clang.cindex.Cursor] = []
def is_constructor_context(self):
return len(self._constructor_context) > 0
def compile_and_analyze_file(self, source_file: str,
compiler_args: list[str],
@ -341,7 +364,7 @@ class CallAnnotationsValidator:
return None
refn = find_object_ref(node)
if self._constructor_context and not refn:
if self.is_constructor_context() and not refn:
# we are in constructor and no object reference means
# we are calling local method. It is MT safe
# only if this method is also only calling local methods or
@ -353,20 +376,44 @@ class CallAnnotationsValidator:
elif refn and refn.kind == CursorKind.MEMBER_REF_EXPR and refn.referenced:
refn = refn.referenced
refna = VlAnnotations.from_nodes_list(refn.get_children())
if refna.guarded or self._constructor_context:
if refna.guarded:
is_mt_safe = True
if self.is_constructor_context() and refn.semantic_parent:
# we are in constructor, so calling local members is MT_SAFE,
# make sure object that we are calling is local to the constructor
constructor_class = self._constructor_context[
-1].semantic_parent
if refn.semantic_parent.spelling == constructor_class.spelling:
if check_class_member_exists(constructor_class, refn):
is_mt_safe = True
else:
# check if this class inherits from some base class
base_class = get_base_class(constructor_class,
refn.semantic_parent)
if base_class:
if check_class_member_exists(
base_class.get_declaration(), refn):
is_mt_safe = True
# variable
elif refn and refn.kind == CursorKind.DECL_REF_EXPR and refn.referenced:
# This is probably a local or an argument.
# Calling methods on local pointers or references is MT-safe,
# but on argument pointers or references is not.
if "*" not in refn.type.spelling and "&" not in refn.type.spelling:
is_mt_safe = True
# local variable
if refn.referenced.kind == CursorKind.VAR_DECL:
is_mt_safe = True
if refn.get_definition():
if refn.referenced.semantic_parent:
if refn.referenced.semantic_parent.kind in [
CursorKind.FUNCTION_DECL, CursorKind.CXX_METHOD
]:
# This is a local or an argument.
# Calling methods on local pointers or references is MT-safe,
# but on argument pointers or references is not.
if "*" not in refn.type.spelling and "&" not in refn.type.spelling:
is_mt_safe = True
# local variable
if refn.referenced.kind == CursorKind.VAR_DECL:
is_mt_safe = True
else:
# Global variable in different translation unit, unsafe
pass
elif refn and refn.kind == CursorKind.CALL_EXPR:
if self._constructor_context:
if self.is_constructor_context():
# call to local function from constructor context
# safe if this function also calling local methods or
# MT-safe methods
@ -442,18 +489,18 @@ class CallAnnotationsValidator:
# Constructors are OK in MT-safe context
# only if they call local methods or MT-safe functions.
if ctx.is_mt_safe_context() or self._constructor_context:
self._constructor_context += 1
if ctx.is_mt_safe_context() or self.is_constructor_context():
self._constructor_context.append(refd)
self.iterate_children(refd.get_children(),
self.dispatch_node_inside_definition)
self._constructor_context -= 1
self._constructor_context.pop()
# stable tree context
if ctx.is_stabe_tree_context():
self._constructor_context += 1
self._constructor_context.append(refd)
self.iterate_children(refd.get_children(),
self.dispatch_node_inside_definition)
self._constructor_context -= 1
self._constructor_context.pop()
# pure context
if ctx.is_pure_context():

View File

@ -183,7 +183,7 @@ public:
<< " (not all control paths of combinational always assign a value)\n"
<< nodep->warnMore()
<< "... Suggest use of always_latch for intentional latches");
if (dumpGraph() >= 9) dumpDotFilePrefixed("latch_" + vrp->name());
if (dumpGraphLevel() >= 9) dumpDotFilePrefixed("latch_" + vrp->name());
}
vertp->user(false); // Clear again (see above)
vrp->varp()->isLatched(latch_detected);
@ -320,7 +320,8 @@ private:
// VISITORS
void visit(AstVarRef* nodep) override {
const AstVar* const varp = nodep->varp();
if (nodep->access().isWriteOrRW() && varp->isSignal() && !varp->isUsedLoopIdx()) {
if (nodep->access().isWriteOrRW() && varp->isSignal() && !varp->isUsedLoopIdx()
&& !varp->isFuncLocalSticky()) {
m_graph.addAssignment(nodep);
}
}
@ -333,6 +334,8 @@ private:
m_graph.addPathVertex(branchp, "ELSE");
iterateAndNextConstNull(nodep->elsesp());
m_graph.currentp(parentp);
} else {
iterateChildrenConst(nodep);
}
}
//--------------------
@ -652,5 +655,5 @@ public:
void V3Active::activeAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ActiveVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("active", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("active", 0, dumpTreeLevel() >= 3);
}

View File

@ -145,5 +145,5 @@ public:
void V3ActiveTop::activeTopAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ActiveTopVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("activetop", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("activetop", 0, dumpTreeLevel() >= 3);
}

View File

@ -20,6 +20,7 @@
#include "V3Assert.h"
#include "V3Ast.h"
#include "V3Error.h"
#include "V3Global.h"
#include "V3Stats.h"
@ -544,5 +545,5 @@ public:
void V3Assert::assertAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ AssertVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("assert", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("assert", 0, dumpTreeLevel() >= 3);
}

View File

@ -21,7 +21,6 @@
#include "verilatedos.h"
#include "V3Ast.h"
#include "V3Error.h"
//============================================================================

View File

@ -496,5 +496,5 @@ public:
void V3AssertPre::assertPreAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ AssertPreVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("assertpre", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("assertpre", 0, dumpTreeLevel() >= 3);
}

View File

@ -1217,7 +1217,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo
if (logsp->fail()) v3fatal("Can't write " << filename);
*logsp << "Verilator Tree Dump (format 0x3900) from <e" << std::dec << editCountLast();
*logsp << "> to <e" << std::dec << editCountGbl() << ">\n";
if (editCountGbl() == editCountLast() && ::dumpTree() < 9) {
if (editCountGbl() == editCountLast() && ::dumpTreeLevel() < 9) {
*logsp << '\n';
*logsp << "No changes since last dump!\n";
} else {
@ -1227,7 +1227,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo
}
}
if (doDump && v3Global.opt.debugEmitV()) V3EmitV::debugEmitV(filename + ".v");
if (doCheck && (v3Global.opt.debugCheck() || ::dumpTree())) {
if (doCheck && (v3Global.opt.debugCheck() || ::dumpTreeLevel())) {
// Error check
checkTree();
// Broken isn't part of check tree because it can munge iterp's

View File

@ -461,6 +461,7 @@ public:
TRIGGER_SCHEDULER,
DYNAMIC_TRIGGER_SCHEDULER,
FORK_SYNC,
PROCESS_REFERENCE,
// Unsigned and two state; fundamental types
UINT32,
UINT64,
@ -493,6 +494,7 @@ public:
"VlTriggerScheduler",
"VlDynamicTriggerScheduler",
"VlFork",
"VlProcessRef",
"IData",
"QData",
"LOGIC_IMPLICIT",
@ -500,13 +502,20 @@ 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", "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", "IData",
"QData", "%E-logic-implct",
" MAX"};
return names[m_e];
}
static void selfTest() {
@ -545,6 +554,7 @@ public:
case TRIGGER_SCHEDULER: return 0; // opaque
case DYNAMIC_TRIGGER_SCHEDULER: return 0; // opaque
case FORK_SYNC: return 0; // opaque
case PROCESS_REFERENCE: return 0; // opaque
case UINT32: return 32;
case UINT64: return 64;
default: return 0;
@ -584,7 +594,7 @@ 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 == DOUBLE || m_e == UNTYPED);
|| m_e == PROCESS_REFERENCE || m_e == DOUBLE || m_e == UNTYPED);
}
bool isDouble() const VL_MT_SAFE { return m_e == DOUBLE; }
bool isEvent() const { return m_e == EVENT; }
@ -1817,6 +1827,7 @@ public:
// ACCESSORS for specific types
// Alas these can't be virtual or they break when passed a nullptr
inline bool isClassHandleValue() const;
inline bool isZero() const;
inline bool isOne() const;
inline bool isNeqZero() const;
@ -1915,8 +1926,6 @@ public:
// Iterate and insert - assumes tree format
virtual void addNextStmt(AstNode* newp,
AstNode* belowp); // When calling, "this" is second argument
virtual void addBeforeStmt(AstNode* newp,
AstNode* belowp); // When calling, "this" is second argument
// METHODS - Iterate on a tree
// Clone or return nullptr if nullptr

View File

@ -41,6 +41,10 @@ bool AstNode::isString() const VL_MT_STABLE {
}
bool AstNode::isSigned() const VL_MT_STABLE { return dtypep() && dtypep()->isSigned(); }
bool AstNode::isClassHandleValue() const {
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isNull())
|| VN_IS(dtypep(), ClassRefDType);
}
bool AstNode::isZero() const {
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqZero());
}

View File

@ -438,6 +438,7 @@ public:
bool isEvent() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::EVENT; }
bool isTriggerVec() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::TRIGGERVEC; }
bool isForkSync() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::FORK_SYNC; }
bool isProcessRef() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::PROCESS_REFERENCE; }
bool isDelayScheduler() const VL_MT_SAFE {
return keyword() == VBasicDTypeKwd::DELAY_SCHEDULER;
}
@ -887,6 +888,7 @@ public:
if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep();
}
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
AstNodeUOrStructDType* getChildStructp() const;
AstNodeDType* subDTypep() const override VL_MT_STABLE {
return m_refDTypep ? m_refDTypep : childDTypep();
}

View File

@ -975,6 +975,20 @@ public:
, m_num(this, 1, on) {
dtypeSetBit();
}
class All0 {};
AstConst(FileLine* fl, All0)
: ASTGEN_SUPER_Const(fl)
, m_num(this, "'0") {
initWithNumber();
fl->warnOff(V3ErrorCode::NEWERSTD, true);
}
class All1 {};
AstConst(FileLine* fl, All1)
: ASTGEN_SUPER_Const(fl)
, m_num(this, "'1") {
initWithNumber();
fl->warnOff(V3ErrorCode::NEWERSTD, true);
}
class Null {};
AstConst(FileLine* fl, Null)
: ASTGEN_SUPER_Const(fl)
@ -1092,7 +1106,7 @@ public:
// METHODS
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return false; }
bool cleanOut() const override { return true; }
bool same(const AstNode*) const override { return true; }
};
class AstFError final : public AstNodeExpr {
@ -1436,7 +1450,7 @@ public:
void name(const string& name) override { m_name = name; }
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return false; }
bool cleanOut() const override { return true; }
bool same(const AstNode* samep) const override { return true; } // dtype comparison does it
int instrCount() const override { return widthInstrs(); }
AstVar* varp() const { return m_varp; }
@ -2108,14 +2122,14 @@ class AstWith final : public AstNodeExpr {
// Children: expression (equation establishing the with)
// @astgen op1 := indexArgRefp : AstLambdaArgRef
// @astgen op2 := valueArgRefp : AstLambdaArgRef
// @astgen op3 := exprp : AstNodeExpr
// @astgen op3 := exprp : List[AstNode]
public:
AstWith(FileLine* fl, AstLambdaArgRef* indexArgRefp, AstLambdaArgRef* valueArgRefp,
AstNodeExpr* exprp)
: ASTGEN_SUPER_With(fl) {
this->indexArgRefp(indexArgRefp);
this->valueArgRefp(valueArgRefp);
this->exprp(exprp);
this->addExprp(exprp);
}
ASTGEN_MEMBERS_AstWith;
bool same(const AstNode* /*samep*/) const override { return true; }

View File

@ -86,6 +86,7 @@ private:
bool m_recursive : 1; // Recursive or part of recursion
bool m_underGenerate : 1; // Under generate (for warning)
bool m_virtual : 1; // Virtual method in class
bool m_fromStd : 1; // Part of std
VLifetime m_lifetime; // Lifetime
protected:
AstNodeFTask(VNType t, FileLine* fl, const string& name, AstNode* stmtsp)
@ -110,7 +111,8 @@ protected:
, m_pureVirtual{false}
, m_recursive{false}
, m_underGenerate{false}
, m_virtual{false} {
, m_virtual{false}
, m_fromStd{false} {
addStmtsp(stmtsp);
cname(name); // Might be overridden by dpi import/export
}
@ -170,6 +172,8 @@ public:
bool underGenerate() const { return m_underGenerate; }
void isVirtual(bool flag) { m_virtual = flag; }
bool isVirtual() const { return m_virtual; }
void isFromStd(bool flag) { m_fromStd = flag; }
bool isFromStd() const { return m_fromStd; }
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
VLifetime lifetime() const { return m_lifetime; }
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
@ -272,10 +276,13 @@ public:
class AstNodeProcedure VL_NOT_FINAL : public AstNode {
// IEEE procedure: initial, final, always
// @astgen op2 := stmtsp : List[AstNode] // Note: op1 is used in some sub-types only
bool m_suspendable = false; // Is suspendable by a Delay, EventControl, etc.
bool m_suspendable : 1; // Is suspendable by a Delay, EventControl, etc.
bool m_needProcess : 1; // Implements part of a process that allocates std::process
protected:
AstNodeProcedure(VNType t, FileLine* fl, AstNode* stmtsp)
: AstNode{t, fl} {
m_needProcess = false;
m_suspendable = false;
addStmtsp(stmtsp);
}
@ -286,6 +293,8 @@ public:
bool isJustOneBodyStmt() const { return stmtsp() && !stmtsp()->nextp(); }
bool isSuspendable() const { return m_suspendable; }
void setSuspendable() { m_suspendable = true; }
bool needProcess() const { return m_needProcess; }
void setNeedProcess() { m_needProcess = true; }
};
class AstNodeRange VL_NOT_FINAL : public AstNode {
// A range, sized or unsized
@ -308,8 +317,6 @@ public:
// METHODS
void addNextStmt(AstNode* newp,
AstNode* belowp) override; // Stop statement searchback here
void addBeforeStmt(AstNode* newp,
AstNode* belowp) override; // Stop statement searchback here
void dump(std::ostream& str = std::cout) const override;
};
class AstNodeAssign VL_NOT_FINAL : public AstNodeStmt {
@ -578,6 +585,7 @@ private:
bool m_dpiImportPrototype : 1; // This is the DPI import prototype (i.e.: provided by user)
bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code
bool m_dpiTraceInit : 1; // DPI trace_init
bool m_needProcess : 1; // Implements part of a process that allocates std::process
public:
AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "")
: ASTGEN_SUPER_CFunc(fl) {
@ -597,6 +605,7 @@ public:
m_isLoose = false;
m_isInline = false;
m_isVirtual = false;
m_needProcess = false;
m_entryPoint = false;
m_pure = false;
m_dpiContext = false;
@ -665,6 +674,8 @@ public:
void isInline(bool flag) { m_isInline = flag; }
bool isVirtual() const { return m_isVirtual; }
void isVirtual(bool flag) { m_isVirtual = flag; }
bool needProcess() const { return m_needProcess; }
void setNeedProcess() { m_needProcess = true; }
bool entryPoint() const { return m_entryPoint; }
void entryPoint(bool flag) { m_entryPoint = flag; }
bool pure() const { return m_pure; }
@ -809,6 +820,8 @@ class AstClassExtends final : public AstNode {
// @astgen op1 := childDTypep : Optional[AstNodeDType]
// @astgen op2 := classOrPkgsp : Optional[AstNode]
const bool m_isImplements = false; // class implements
bool m_parameterized = false; // has parameters in its statement
public:
AstClassExtends(FileLine* fl, AstNode* classOrPkgsp, bool isImplements)
: ASTGEN_SUPER_ClassExtends(fl)
@ -819,8 +832,12 @@ public:
void dump(std::ostream& str) const override;
bool hasDType() const override { return true; }
string verilogKwd() const override { return isImplements() ? "implements" : "extends"; }
AstClass* classp() const; // Class being extended (after link)
// Class being extended (after link and instantiation if needed)
AstClass* classOrNullp() const;
AstClass* classp() const; // Like above, but throws error if nulll
bool isImplements() const { return m_isImplements; }
void parameterized(bool flag) { m_parameterized = flag; }
bool parameterized() const { return m_parameterized; }
};
class AstClocking final : public AstNode {
// Parents: MODULE
@ -1359,6 +1376,7 @@ public:
string name() const override VL_MT_STABLE { return m_name; } // * = Scope name
void name(const string& name) override { m_name = name; }
void dump(std::ostream& str) const override;
bool same(const AstNode* samep) const override;
string nameDotless() const;
string nameVlSym() const { return string{"vlSymsp->"} + nameDotless(); }
AstNodeModule* modp() const { return m_modp; }
@ -1628,6 +1646,7 @@ class AstVar final : public AstNode {
bool m_usedLoopIdx : 1; // Variable subject of for unrolling
bool m_usedVirtIface : 1; // Signal used through a virtual interface
bool m_funcLocal : 1; // Local variable for a function
bool m_funcLocalSticky : 1; // As m_funcLocal but remains set if var is moved to a static
bool m_funcReturn : 1; // Return variable for a function
bool m_attrScBv : 1; // User force bit vector attribute
bool m_attrIsolateAssign : 1; // User isolate_assignments attribute
@ -1671,6 +1690,7 @@ class AstVar final : public AstNode {
m_sigUserRdPublic = false;
m_sigUserRWPublic = false;
m_funcLocal = false;
m_funcLocalSticky = false;
m_funcReturn = false;
m_attrScBv = false;
m_attrIsolateAssign = false;
@ -1758,6 +1778,7 @@ public:
}
ASTGEN_MEMBERS_AstVar;
void dump(std::ostream& str) const override;
bool same(const AstNode* samep) const override;
string name() const override VL_MT_STABLE VL_MT_SAFE { return m_name; } // * = Var name
bool hasDType() const override { return true; }
bool maybePointedTo() const override { return true; }
@ -1833,7 +1854,10 @@ public:
void isContinuously(bool flag) { m_isContinuously = flag; }
void isStatic(bool flag) { m_isStatic = flag; }
void isIfaceParent(bool flag) { m_isIfaceParent = flag; }
void funcLocal(bool flag) { m_funcLocal = flag; }
void funcLocal(bool flag) {
m_funcLocal = flag;
if (flag) m_funcLocalSticky = true;
}
void funcReturn(bool flag) { m_funcReturn = flag; }
void hasStrengthAssignment(bool flag) { m_hasStrengthAssignment = flag; }
bool hasStrengthAssignment() { return m_hasStrengthAssignment; }
@ -1915,6 +1939,7 @@ public:
bool isStatic() const VL_MT_SAFE { return m_isStatic; }
bool isLatched() const { return m_isLatched; }
bool isFuncLocal() const { return m_funcLocal; }
bool isFuncLocalSticky() const { return m_funcLocalSticky; }
bool isFuncReturn() const { return m_funcReturn; }
bool isPullup() const { return m_isPullup; }
bool isPulldown() const { return m_isPulldown; }
@ -2000,6 +2025,7 @@ public:
bool maybePointedTo() const override { return true; }
string name() const override VL_MT_STABLE { return scopep()->name() + "->" + varp()->name(); }
void dump(std::ostream& str) const override;
bool same(const AstNode* samep) const override;
bool hasDType() const override { return true; }
AstVar* varp() const VL_MT_STABLE { return m_varp; } // [After Link] Pointer to variable
AstScope* scopep() const VL_MT_STABLE { return m_scopep; } // Pointer to scope it's under
@ -3240,8 +3266,6 @@ public:
int instrCount() const override { return INSTR_COUNT_BRANCH; }
bool same(const AstNode* /*samep*/) const override { return true; }
// Stop statement searchback here
void addBeforeStmt(AstNode* newp, AstNode* belowp) override;
// Stop statement searchback here
void addNextStmt(AstNode* newp, AstNode* belowp) override;
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
};

View File

@ -391,8 +391,10 @@ string AstVar::verilogKwd() const {
return "wreal";
} else if (varType() == VVarType::IFACEREF) {
return "ifaceref";
} else {
} else if (dtypep()) {
return dtypep()->name();
} else {
return "UNKNOWN";
}
}
@ -782,6 +784,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const VL_M
info.m_type = "VlDynamicTriggerScheduler";
} else if (bdtypep->isForkSync()) {
info.m_type = "VlForkSync";
} else if (bdtypep->isProcessRef()) {
info.m_type = "VlProcessRef";
} else if (bdtypep->isEvent()) {
info.m_type = "VlEvent";
} else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width
@ -1269,44 +1273,17 @@ AstVarScope* AstConstPool::findConst(AstConst* initp, bool mergeDType) {
//======================================================================
// Special walking tree inserters
void AstNode::addBeforeStmt(AstNode* newp, AstNode*) {
UASSERT_OBJ(backp(), newp, "Can't find current statement to addBeforeStmt");
// Look up; virtual call will find where to put it
this->backp()->addBeforeStmt(newp, this);
}
void AstNode::addNextStmt(AstNode* newp, AstNode*) {
UASSERT_OBJ(backp(), newp, "Can't find current statement to addNextStmt");
// Look up; virtual call will find where to put it
this->backp()->addNextStmt(newp, this);
}
void AstNodeStmt::addBeforeStmt(AstNode* newp, AstNode*) {
// Insert newp before current node
this->addHereThisAsNext(newp);
}
void AstNodeStmt::addNextStmt(AstNode* newp, AstNode*) {
// Insert newp after current node
this->addNextHere(newp);
}
void AstWhile::addBeforeStmt(AstNode* newp, AstNode* belowp) {
// Special, as statements need to be put in different places
// Belowp is how we came to recurse up to this point
// Preconditions insert first just before themselves (the normal rule
// for other statement types)
if (belowp == precondsp()) {
// Must have been first statement in precondsp list, so newp is new first statement
belowp->addHereThisAsNext(newp);
} else if (belowp == condp()) {
// Goes before condition, IE in preconditions
addPrecondsp(newp);
} else if (belowp == stmtsp()) {
// Was first statement in body, so new front
belowp->addHereThisAsNext(newp);
} else {
belowp->v3fatalSrc("Doesn't look like this was really under the while");
}
}
void AstWhile::addNextStmt(AstNode* newp, AstNode* belowp) {
// Special, as statements need to be put in different places
// Belowp is how we came to recurse up to this point
@ -1377,6 +1354,7 @@ void AstNode::dump(std::ostream& str) const {
void AstNodeProcedure::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (isSuspendable()) str << " [SUSP]";
if (needProcess()) str << " [NPRC]";
}
void AstAlways::dump(std::ostream& str) const {
@ -1499,13 +1477,20 @@ void AstClassExtends::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (isImplements()) str << " [IMPLEMENTS]";
}
AstClass* AstClassExtends::classp() const {
const AstClassRefDType* refp = VN_CAST(dtypep(), ClassRefDType);
if (VL_UNLIKELY(!refp)) { // LinkDot uses this for 'super.'
refp = VN_AS(childDTypep(), ClassRefDType);
AstClass* AstClassExtends::classOrNullp() const {
const AstNodeDType* const dtp = dtypep() ? dtypep() : childDTypep();
const AstClassRefDType* const refp = VN_CAST(dtp, ClassRefDType);
if (refp && !refp->paramsp()) {
// Class already resolved
return refp->classp();
} else {
return nullptr;
}
UASSERT_OBJ(refp, this, "class extends non-ref");
return refp->classp();
}
AstClass* AstClassExtends::classp() const {
AstClass* const clsp = classOrNullp();
UASSERT_OBJ(clsp, this, "Extended class is unresolved");
return clsp;
}
void AstClassRefDType::dump(std::ostream& str) const {
this->AstNodeDType::dump(str);
@ -1669,10 +1654,19 @@ void AstLogOr::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (sideEffect()) str << " [SIDE]";
}
void AstMemberDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "member";
}
AstNodeUOrStructDType* AstMemberDType::getChildStructp() const {
AstNodeDType* subdtp = skipRefp();
while (AstNodeArrayDType* const asubdtp = VN_CAST(subdtp, NodeArrayDType)) {
subdtp = asubdtp->subDTypep();
}
return VN_CAST(subdtp, NodeUOrStructDType); // Maybe nullptr
}
void AstMemberSel::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
str << " -> ";
@ -1829,6 +1823,7 @@ void AstNodeUOrStructDType::dump(std::ostream& str) const {
this->AstNodeDType::dump(str);
if (packed()) str << " [PACKED]";
if (isFourstate()) str << " [4STATE]";
if (classOrPackagep()) str << " pkg=" << nodeAddr(classOrPackagep());
}
void AstNodeDType::dump(std::ostream& str) const {
this->AstNode::dump(str);
@ -2071,6 +2066,10 @@ void AstVarScope::dump(std::ostream& str) const {
str << " ->UNLINKED";
}
}
bool AstVarScope::same(const AstNode* samep) const {
const AstVarScope* const asamep = static_cast<const AstVarScope*>(samep);
return varp()->same(asamep->varp()) && scopep()->same(asamep->scopep());
}
void AstNodeVarRef::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (classOrPackagep()) str << " pkg=" << nodeAddr(classOrPackagep());
@ -2134,12 +2133,23 @@ void AstVar::dump(std::ostream& str) const {
if (!lifetime().isNone()) str << " [" << lifetime().ascii() << "] ";
str << " " << varType();
}
bool AstVar::same(const AstNode* samep) const {
const AstVar* const asamep = static_cast<const AstVar*>(samep);
return name() == asamep->name() && varType() == asamep->varType();
}
void AstScope::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " [abovep=" << reinterpret_cast<const void*>(aboveScopep()) << "]";
str << " [cellp=" << reinterpret_cast<const void*>(aboveCellp()) << "]";
str << " [modp=" << reinterpret_cast<const void*>(modp()) << "]";
}
bool AstScope::same(const AstNode* samep) const {
const AstScope* const asamep = static_cast<const AstScope*>(samep);
return name() == asamep->name()
&& ((!aboveScopep() && !asamep->aboveScopep())
|| (aboveScopep() && asamep->aboveScopep()
&& aboveScopep()->name() == asamep->aboveScopep()->name()));
}
void AstScopeName::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (dpiExport()) str << " [DPIEX]";
@ -2302,6 +2312,7 @@ void AstCFunc::dump(std::ostream& str) const {
if (isDestructor()) str << " [DTOR]";
if (isVirtual()) str << " [VIRT]";
if (isCoroutine()) str << " [CORO]";
if (needProcess()) str << " [NPRC]";
}
const char* AstCAwait::broken() const {
BROKEN_RTN(m_sensesp && !m_sensesp->brokeExists());

View File

@ -40,6 +40,39 @@ VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
class RenameStaticVisitor final : public VNVisitor {
private:
// STATE
const std::set<AstVar*>& m_staticFuncVarsr; // Static variables from m_ftaskp
AstNodeFTask* const m_ftaskp; // Current function/task
// VISITORS
void visit(AstVarRef* nodep) override {
const auto it = m_staticFuncVarsr.find(nodep->varp());
if (it != m_staticFuncVarsr.end()) nodep->name((*it)->name());
iterateChildren(nodep);
}
void visit(AstInitialStatic* nodep) override {
iterateChildren(nodep);
nodep->unlinkFrBack();
m_ftaskp->addHereThisAsNext(nodep);
}
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
RenameStaticVisitor(std::set<AstVar*>& staticFuncVars, AstNodeFTask* ftaskp, AstNode* nodep)
: m_staticFuncVarsr(staticFuncVars)
, m_ftaskp(ftaskp) {
iterateChildren(nodep);
}
~RenameStaticVisitor() override = default;
};
//######################################################################
class BeginState final {
private:
// NODE STATE
@ -78,6 +111,7 @@ private:
string dot(const string& a, const string& b) {
if (a == "") return b;
if (b == "") return a;
return a + "__DOT__" + b;
}
@ -123,25 +157,6 @@ private:
}
}
void renameAndStaticsRecurse(AstNode* const nodep) {
// Rename references and move InitialStatic items
if (AstVarRef* const varrefp = VN_CAST(nodep, VarRef)) {
const auto it = m_staticFuncVars.find(varrefp->varp());
if (it != m_staticFuncVars.end()) varrefp->name((*it)->name());
}
if (nodep->op1p()) renameAndStaticsRecurse(nodep->op1p());
if (nodep->op2p()) renameAndStaticsRecurse(nodep->op2p());
if (nodep->op3p()) renameAndStaticsRecurse(nodep->op3p());
if (nodep->op4p()) renameAndStaticsRecurse(nodep->op4p());
if (nodep->nextp()) renameAndStaticsRecurse(nodep->nextp());
if (VN_IS(nodep, InitialStatic)) {
nodep->unlinkFrBack();
m_ftaskp->addHereThisAsNext(nodep);
}
}
// VISITORS
void visit(AstFork* nodep) override {
// Keep begins in forks to group their statements together
@ -197,7 +212,7 @@ private:
m_ftaskp = nodep;
m_liftedp = nullptr;
iterateChildren(nodep);
renameAndStaticsRecurse(nodep);
RenameStaticVisitor{m_staticFuncVars, m_ftaskp, nodep};
if (m_liftedp) {
// Place lifted nodes at beginning of stmtsp, so Var nodes appear before referenced
if (AstNode* const stmtsp = nodep->stmtsp()) {
@ -246,7 +261,8 @@ private:
void visit(AstVar* nodep) override {
// If static variable, move it outside a function.
if (nodep->lifetime().isStatic() && m_ftaskp) {
const std::string newName = m_ftaskp->name() + "__Vstatic__" + nodep->name();
const std::string newName
= m_ftaskp->name() + "__Vstatic__" + dot(m_unnamedScope, nodep->name());
nodep->name(newName);
nodep->unlinkFrBack();
m_ftaskp->addHereThisAsNext(nodep);
@ -392,5 +408,5 @@ void V3Begin::debeginAll(AstNetlist* nodep) {
{ BeginVisitor{nodep, &state}; }
if (state.anyFuncInBegin()) { BeginRelinkVisitor{nodep, &state}; }
} // Destruct before checking
V3Global::dumpCheckGlobalTree("begin", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("begin", 0, dumpTreeLevel() >= 3);
}

View File

@ -45,10 +45,12 @@ private:
// AstFTask::user1() -> int. Number of references
const VNUser1InUse m_inuser1;
// STATE
// STATE - across all visitors
std::vector<AstCFunc*> m_cfuncsp; // List of all tasks
// STATE - for current visit position (use VL_RESTORER)
int m_likely = false; // Excuses for branch likely taken
int m_unlikely = false; // Excuses for branch likely not taken
std::vector<AstCFunc*> m_cfuncsp; // List of all tasks
// METHODS

View File

@ -257,5 +257,5 @@ void V3CCtors::cctorsAll() {
UINFO(2, __FUNCTION__ << ": " << endl);
evalAsserts();
{ CCtorsVisitor{v3Global.rootp()}; }
V3Global::dumpCheckGlobalTree("cctors", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("cctors", 0, dumpTreeLevel() >= 3);
}

View File

@ -45,12 +45,16 @@ class CUseVisitor final : public VNVisitor {
const VNUser1InUse m_inuser1;
// MEMBERS
bool m_impOnly = false; // In details needed only for implementation
AstNodeModule* const m_modp; // Current module
std::set<std::pair<VUseType, std::string>> m_didUse; // What we already used
bool m_dtypesImplOnly = false;
// METHODS
void addNewUse(AstNode* nodep, VUseType useType, const string& name) {
if (m_dtypesImplOnly
&& (useType == VUseType::INT_INCLUDE || useType == VUseType::INT_FWD_CLASS))
return;
if (m_didUse.emplace(useType, name).second) {
AstCUse* const newp = new AstCUse{nodep->fileline(), useType, name};
m_modp->addStmtsp(newp);
@ -61,12 +65,29 @@ class CUseVisitor final : public VNVisitor {
// VISITORS
void visit(AstClassRefDType* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
if (!m_impOnly) addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name());
// Need to include extends() when we implement, but no need for pointers to know
VL_RESTORER(m_impOnly);
addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name());
}
void visit(AstCFunc* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
iterateAndNextNull(nodep->argsp());
{
m_impOnly = true;
iterateChildren(nodep->classp()); // This also gets all extend classes
VL_RESTORER(m_dtypesImplOnly);
m_dtypesImplOnly = true;
iterateAndNextNull(nodep->initsp());
iterateAndNextNull(nodep->stmtsp());
iterateAndNextNull(nodep->finalsp());
}
}
void visit(AstCReturn* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
if (m_dtypesImplOnly) {
for (AstNode* exprp = nodep->op1p(); exprp; exprp = exprp->nextp()) {
if (exprp->dtypep()) iterate(exprp->dtypep());
}
} else {
iterateChildren(nodep);
}
}
void visit(AstNodeDType* nodep) override {
@ -79,6 +100,8 @@ class CUseVisitor final : public VNVisitor {
if (stypep && stypep->classOrPackagep()) {
addNewUse(nodep, VUseType::INT_INCLUDE, stypep->classOrPackagep()->name());
iterateChildren(stypep);
} else if (AstClassRefDType* const classp = VN_CAST(nodep->skipRefp(), ClassRefDType)) {
addNewUse(nodep, VUseType::INT_FWD_CLASS, classp->name());
}
}
void visit(AstNode* nodep) override {
@ -115,5 +138,5 @@ void V3CUse::cUseAll() {
// for each output file and put under that
CUseVisitor{modp};
}
V3Global::dumpCheckGlobalTree("cuse", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("cuse", 0, dumpTreeLevel() >= 3);
}

View File

@ -566,7 +566,7 @@ public:
void V3Case::caseAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CaseVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("case", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("case", 0, dumpTreeLevel() >= 3);
}
void V3Case::caseLint(AstNodeCase* nodep) {
UINFO(4, __FUNCTION__ << ": " << endl);

View File

@ -122,6 +122,31 @@ private:
if (nodep->sizeMattersLhs()) ensureCast(nodep->lhsp());
if (nodep->sizeMattersRhs()) ensureCast(nodep->rhsp());
}
void visit(AstNodeCond* nodep) override {
// All class types are castable to each other. If they are of different types,
// a compilation error will be thrown, so an explicit cast is required. Types were
// already checked by V3Width and dtypep of a condition operator is a type of their
// common base class, so both classes can be safetly casted.
const AstClassRefDType* const thenClassDtypep
= VN_CAST(nodep->thenp()->dtypep(), ClassRefDType);
const AstClassRefDType* const elseClassDtypep
= VN_CAST(nodep->elsep()->dtypep(), ClassRefDType);
const bool castRequired = thenClassDtypep && elseClassDtypep
&& (thenClassDtypep->classp() != elseClassDtypep->classp());
if (castRequired) {
const AstClass* const commonBaseClassp
= VN_AS(nodep->dtypep(), ClassRefDType)->classp();
if (thenClassDtypep->classp() != commonBaseClassp) {
AstNodeExpr* thenp = nodep->thenp()->unlinkFrBack();
nodep->thenp(new AstCCast{thenp->fileline(), thenp, nodep});
}
if (elseClassDtypep->classp() != commonBaseClassp) {
AstNodeExpr* elsep = nodep->elsep()->unlinkFrBack();
nodep->elsep(new AstCCast{elsep->fileline(), elsep, nodep});
}
}
visit(static_cast<AstNodeTriop*>(nodep));
}
void visit(AstNodeTriop* nodep) override {
iterateChildren(nodep);
nodep->user1(nodep->lhsp()->user1() | nodep->rhsp()->user1() | nodep->thsp()->user1());
@ -161,7 +186,8 @@ private:
&& !VN_IS(backp, NodeCCall) && !VN_IS(backp, CMethodHard) && !VN_IS(backp, SFormatF)
&& !VN_IS(backp, ArraySel) && !VN_IS(backp, StructSel) && !VN_IS(backp, RedXor)
&& (nodep->varp()->basicp() && !nodep->varp()->basicp()->isTriggerVec()
&& !nodep->varp()->basicp()->isForkSync())
&& !nodep->varp()->basicp()->isForkSync()
&& !nodep->varp()->basicp()->isProcessRef())
&& backp->width() && castSize(nodep) != castSize(nodep->varp())) {
// Cast vars to IData first, else below has upper bits wrongly set
// CData x=3; out = (QData)(x<<30);
@ -208,5 +234,5 @@ public:
void V3Cast::castAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CastVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("cast", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("cast", 0, dumpTreeLevel() >= 3);
}

View File

@ -27,6 +27,7 @@
#include "V3Ast.h"
#include "V3Global.h"
#include "V3UniqueNames.h"
VL_DEFINE_DEBUG_FUNCTIONS;
@ -41,6 +42,7 @@ private:
// MEMBERS
string m_prefix; // String prefix to add to name based on hier
V3UniqueNames m_names; // For unique naming of structs and unions
AstNodeModule* m_modp = nullptr; // Current module
AstNodeModule* m_classPackagep = nullptr; // Package moving into
const AstScope* m_classScopep = nullptr; // Package moving scopes into
@ -62,6 +64,7 @@ private:
AstClassPackage* const packagep
= new AstClassPackage{nodep->fileline(), nodep->origName()};
packagep->name(nodep->name() + "__Vclpkg");
nodep->editCountInc();
nodep->classOrPackagep(packagep);
packagep->classp(nodep);
v3Global.rootp()->addModulesp(packagep);
@ -177,14 +180,15 @@ private:
}
void setStructModulep(AstNodeUOrStructDType* const dtypep) {
// Give it a pointer to its package and a final name
// Give struct a pointer to its package and a final name
dtypep->editCountInc();
dtypep->classOrPackagep(m_classPackagep ? m_classPackagep : m_modp);
dtypep->name(dtypep->name() + (VN_IS(dtypep, UnionDType) ? "__union" : "__struct")
+ cvtToStr(dtypep->uniqueNum()));
dtypep->name(
m_names.get(dtypep->name() + (VN_IS(dtypep, UnionDType) ? "__union" : "__struct")));
for (const AstMemberDType* itemp = dtypep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
AstNodeUOrStructDType* const subp = VN_CAST(itemp->skipRefp(), NodeUOrStructDType);
AstNodeUOrStructDType* const subp = itemp->getChildStructp();
// Recurse only into anonymous unpacked structs inside this definition,
// other unpacked structs will be reached from another typedefs
if (subp && !subp->packed() && subp->name().empty()) setStructModulep(subp);
@ -244,5 +248,5 @@ public:
void V3Class::classAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ClassVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("class", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("class", 0, dumpTreeLevel() >= 3);
}

View File

@ -234,6 +234,11 @@ private:
operandTriop(nodep);
setClean(nodep, nodep->cleanOut());
}
void visit(AstStructSel* nodep) override {
iterateChildren(nodep);
AstStructDType* dtypep = VN_CAST(nodep->dtypep()->skipRefp(), StructDType);
setClean(nodep, dtypep && !dtypep->packed());
}
void visit(AstUCFunc* nodep) override {
iterateChildren(nodep);
computeCppWidth(nodep);
@ -298,7 +303,11 @@ private:
}
void visit(AstWith* nodep) override {
iterateChildren(nodep);
ensureCleanAndNext(nodep->exprp());
setClean(nodep, true);
}
void visit(AstCReturn* nodep) override {
iterateChildren(nodep);
ensureClean(nodep->lhsp());
setClean(nodep, true);
}
void visit(AstIntfRef* nodep) override {
@ -325,5 +334,5 @@ public:
void V3Clean::cleanAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CleanVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("clean", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("clean", 0, dumpTreeLevel() >= 3);
}

View File

@ -224,5 +224,5 @@ public:
void V3Clock::clockAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ClockVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("clock", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("clock", 0, dumpTreeLevel() >= 3);
}

View File

@ -238,5 +238,5 @@ public:
void V3Combine::combineAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
CombineVisitor::apply(nodep);
V3Global::dumpCheckGlobalTree("combine", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("combine", 0, dumpTreeLevel() >= 3);
}

View File

@ -63,6 +63,7 @@ static void makeVlToString(AstIface* nodep) {
}
static void makeVlToString(AstNodeUOrStructDType* nodep) {
AstNodeModule* const modp = nodep->classOrPackagep();
UASSERT_OBJ(modp, nodep, "Unlinked struct package");
AstCFunc* const funcp
= new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"};
funcp->argTypes("const " + EmitCBase::prefixNameProtect(nodep) + "& obj");
@ -81,11 +82,13 @@ static void makeVlToString(AstNodeUOrStructDType* nodep) {
}
stmt += VIdProtect::protect(itemp->prettyName()) + ":\" + ";
if (VN_IS(itemp->dtypep()->skipRefp(), BasicDType) && itemp->isWide()) {
stmt += "VL_TO_STRING_W";
stmt += "VL_TO_STRING_W(";
stmt += cvtToStr(itemp->widthWords());
stmt += ", ";
} else {
stmt += "VL_TO_STRING";
stmt += "VL_TO_STRING(";
}
stmt += "(obj." + itemp->nameProtect() + ");\n";
stmt += "obj." + itemp->nameProtect() + ");\n";
funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt});
}
funcp->addStmtsp(new AstCStmt{nodep->fileline(), "out += \"}\";\n"});
@ -172,5 +175,5 @@ void V3Common::commonAll() {
if (!dtypep->packed()) makeVlToString(dtypep);
}
}
V3Global::dumpCheckGlobalTree("common", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("common", 0, dumpTreeLevel() >= 3);
}

View File

@ -3064,7 +3064,7 @@ private:
&& nodep->displayType() == VDisplayType::DT_WRITE)))
return false;
if ((prevp->filep() && !nodep->filep()) || (!prevp->filep() && nodep->filep())
|| !prevp->filep()->sameTree(nodep->filep()))
|| (prevp->filep() && nodep->filep() && !prevp->filep()->sameTree(nodep->filep())))
return false;
if (!prevp->fmtp() || prevp->fmtp()->nextp() || !nodep->fmtp() || nodep->fmtp()->nextp())
return false;
@ -3405,9 +3405,9 @@ private:
TREEOPS("AstCond {$lhsp.isNeqZero}", "replaceWIteratedRhs(nodep)");
TREEOP ("AstCond{$condp.castNot, $thenp, $elsep}", "AstCond{$condp->castNot()->lhsp(), $elsep, $thenp}");
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isAllOnes, $elsep}", "AstLogOr {$condp, $elsep}"); // a?1:b == a||b
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isZero}", "AstLogAnd{$condp, $thenp}"); // a?b:0 == a&&b
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isZero, !$elsep.isClassHandleValue}", "AstLogAnd{$condp, $thenp}"); // a?b:0 == a&&b
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isAllOnes}", "AstLogOr {AstNot{$condp}, $thenp}"); // a?b:1 == ~a||b
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isZero, $elsep}", "AstLogAnd{AstNot{$condp}, $elsep}"); // a?0:b == ~a&&b
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isZero, !$thenp.isClassHandleValue, $elsep}", "AstLogAnd{AstNot{$condp}, $elsep}"); // a?0:b == ~a&&b
TREEOP ("AstNodeCond{!$condp.width1, operandBoolShift(nodep->condp())}", "replaceBoolShift(nodep->condp())");
// Prefer constants on left, since that often needs a shift, it lets
// constant red remove the shift
@ -3759,7 +3759,7 @@ void V3Const::constifyAllLint(AstNetlist* nodep) {
ConstVisitor visitor{ConstVisitor::PROC_V_WARN, /* globalPass: */ true};
(void)visitor.mainAcceptEdit(nodep);
} // Destruct before checking
V3Global::dumpCheckGlobalTree("const", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("const", 0, dumpTreeLevel() >= 3);
}
void V3Const::constifyCpp(AstNetlist* nodep) {
@ -3768,7 +3768,7 @@ void V3Const::constifyCpp(AstNetlist* nodep) {
ConstVisitor visitor{ConstVisitor::PROC_CPP, /* globalPass: */ true};
(void)visitor.mainAcceptEdit(nodep);
} // Destruct before checking
V3Global::dumpCheckGlobalTree("const_cpp", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("const_cpp", 0, dumpTreeLevel() >= 3);
}
AstNode* V3Const::constifyEdit(AstNode* nodep) {
@ -3792,7 +3792,7 @@ void V3Const::constifyAllLive(AstNetlist* nodep) {
ConstVisitor visitor{ConstVisitor::PROC_LIVE, /* globalPass: */ true};
(void)visitor.mainAcceptEdit(nodep);
} // Destruct before checking
V3Global::dumpCheckGlobalTree("const", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("const", 0, dumpTreeLevel() >= 3);
}
void V3Const::constifyAll(AstNetlist* nodep) {
@ -3802,7 +3802,7 @@ void V3Const::constifyAll(AstNetlist* nodep) {
ConstVisitor visitor{ConstVisitor::PROC_V_EXPENSIVE, /* globalPass: */ true};
(void)visitor.mainAcceptEdit(nodep);
} // Destruct before checking
V3Global::dumpCheckGlobalTree("const", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("const", 0, dumpTreeLevel() >= 3);
}
AstNode* V3Const::constifyExpensiveEdit(AstNode* nodep) {

View File

@ -567,5 +567,5 @@ public:
void V3Coverage::coverage(AstNetlist* rootp) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CoverageVisitor{rootp}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("coverage", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("coverage", 0, dumpTreeLevel() >= 3);
}

View File

@ -117,5 +117,5 @@ public:
void V3CoverageJoin::coverageJoin(AstNetlist* rootp) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CoverageJoinVisitor{rootp}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("coveragejoin", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("coveragejoin", 0, dumpTreeLevel() >= 3);
}

View File

@ -62,9 +62,10 @@ private:
// TYPES
using AssignMap = std::multimap<AstVarScope*, AstNodeAssign*>;
// STATE
AstNodeModule* m_modp = nullptr; // Current module
AstSelLoopVars* m_selloopvarsp = nullptr; // Current loop vars
// STATE - across all visitors
const bool m_elimUserVars; // Allow removal of user's vars
const bool m_elimDTypes; // Allow removal of DTypes
const bool m_elimCells; // Allow removal of Cells
// List of all encountered to avoid another loop through tree
std::vector<AstVar*> m_varsp;
std::vector<AstNode*> m_dtypesp;
@ -73,13 +74,14 @@ private:
std::vector<AstCell*> m_cellsp;
std::vector<AstClass*> m_classesp;
std::vector<AstTypedef*> m_typedefsp;
AssignMap m_assignMap; // List of all simple assignments for each variable
const bool m_elimUserVars; // Allow removal of user's vars
const bool m_elimDTypes; // Allow removal of DTypes
const bool m_elimCells; // Allow removal of Cells
bool m_sideEffect = false; // Side effects discovered in assign RHS
// STATE - for current visit position (use VL_RESTORER)
bool m_inAssign = false; // Currently in an assign
AstNodeModule* m_modp = nullptr; // Current module
AstSelLoopVars* m_selloopvarsp = nullptr; // Current loop vars
// METHODS
void checkAll(AstNode* nodep) {
@ -107,18 +109,16 @@ private:
void visit(AstNodeModule* nodep) override {
if (m_modp) m_modp->user1Inc(); // e.g. Class under Package
VL_RESTORER(m_modp);
{
m_modp = nodep;
if (!nodep->dead()) {
iterateChildren(nodep);
checkAll(nodep);
if (AstClass* const classp = VN_CAST(nodep, Class)) {
if (classp->extendsp()) classp->extendsp()->user1Inc();
if (classp->classOrPackagep()) classp->classOrPackagep()->user1Inc();
m_classesp.push_back(classp);
// TODO we don't reclaim dead classes yet - graph implementation instead?
classp->user1Inc();
}
m_modp = nodep;
if (!nodep->dead()) {
iterateChildren(nodep);
checkAll(nodep);
if (AstClass* const classp = VN_CAST(nodep, Class)) {
if (classp->extendsp()) classp->extendsp()->user1Inc();
if (classp->classOrPackagep()) classp->classOrPackagep()->user1Inc();
m_classesp.push_back(classp);
// TODO we don't reclaim dead classes yet - graph implementation instead?
classp->user1Inc();
}
}
}
@ -282,9 +282,13 @@ private:
// See if simple assignments to variables may be eliminated because
// that variable is never used.
// Similar code in V3Life
VL_RESTORER(m_sideEffect);
const bool assignInAssign = m_inAssign; // Might be Assign(..., ExprStmt(Assign), ...)
{
VL_RESTORER(m_inAssign);
VL_RESTORER(m_sideEffect);
m_inAssign = true;
m_sideEffect = false;
if (assignInAssign) m_sideEffect = true;
iterateAndNextNull(nodep->rhsp());
checkAll(nodep);
// Has to be direct assignment without any EXTRACTing.
@ -299,6 +303,7 @@ private:
}
iterateNull(nodep->timingControlp());
}
if (assignInAssign) m_sideEffect = true; // Parent assign shouldn't optimize
}
//-----
@ -539,29 +544,29 @@ void V3Dead::deadifyModules(AstNetlist* nodep) {
{
DeadVisitor{nodep, false, false, false, false, !v3Global.opt.topIfacesSupported()};
} // Destruct before checking
V3Global::dumpCheckGlobalTree("deadModules", 0, dumpTree() >= 6);
V3Global::dumpCheckGlobalTree("deadModules", 0, dumpTreeLevel() >= 6);
}
void V3Dead::deadifyDTypes(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DeadVisitor{nodep, false, true, false, false, false}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadDtypes", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("deadDtypes", 0, dumpTreeLevel() >= 3);
}
void V3Dead::deadifyDTypesScoped(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DeadVisitor{nodep, false, true, true, false, false}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0, dumpTreeLevel() >= 3);
}
void V3Dead::deadifyAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DeadVisitor{nodep, true, true, false, true, false}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadAll", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("deadAll", 0, dumpTreeLevel() >= 3);
}
void V3Dead::deadifyAllScoped(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DeadVisitor{nodep, true, true, true, true, false}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadAllScoped", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("deadAllScoped", 0, dumpTreeLevel() >= 3);
}

View File

@ -92,21 +92,23 @@ private:
const VNUser4InUse m_inuser4;
const VNUser5InUse m_inuser5;
// STATE
// STATE - across all visitors
std::unordered_map<const AstVarScope*, int> m_scopeVecMap; // Next var number for each scope
std::set<AstSenTree*> m_timingDomains; // Timing resume domains
using VarMap = std::map<const std::pair<AstNodeModule*, std::string>, AstVar*>;
VarMap m_modVarMap; // Table of new var names created under module
VDouble0 m_statSharedSet; // Statistic tracking
// STATE - for current visit position (use VL_RESTORER)
AstActive* m_activep = nullptr; // Current activate
const AstCFunc* m_cfuncp = nullptr; // Current public C Function
AstAssignDly* m_nextDlyp = nullptr; // Next delayed assignment in a list of assignments
AstNodeProcedure* m_procp = nullptr; // Current process
std::set<AstSenTree*> m_timingDomains; // Timing resume domains
bool m_inDly = false; // True in delayed assignments
bool m_inLoop = false; // True in for loops
bool m_inInitial = false; // True in static initializers and initial blocks
bool m_inSuspendableOrFork = false; // True in suspendable processes and forks
bool m_ignoreBlkAndNBlk = false; // Suppress delayed assignment BLKANDNBLK
using VarMap = std::map<const std::pair<AstNodeModule*, std::string>, AstVar*>;
VarMap m_modVarMap; // Table of new var names created under module
VDouble0 m_statSharedSet; // Statistic tracking
std::unordered_map<const AstVarScope*, int> m_scopeVecMap; // Next var number for each scope
// METHODS
@ -424,10 +426,8 @@ private:
}
void visit(AstCFunc* nodep) override {
VL_RESTORER(m_cfuncp);
{
m_cfuncp = nodep;
iterateChildren(nodep);
}
m_cfuncp = nodep;
iterateChildren(nodep);
}
void visit(AstActive* nodep) override {
m_activep = nodep;
@ -443,10 +443,12 @@ private:
void visit(AstNodeProcedure* nodep) override {
VL_RESTORER(m_inSuspendableOrFork);
m_inSuspendableOrFork = nodep->isSuspendable();
m_procp = nodep;
m_timingDomains.clear();
iterateChildren(nodep);
m_procp = nullptr;
{
VL_RESTORER(m_procp);
m_procp = nodep;
m_timingDomains.clear();
iterateChildren(nodep);
}
if (m_timingDomains.empty()) return;
if (auto* const actp = VN_AS(nodep->user3p(), Active)) {
// Merge all timing domains (and possibly the active's domain) to create a sentree for
@ -618,10 +620,8 @@ private:
}
void visit(AstWhile* nodep) override {
VL_RESTORER(m_inLoop);
{
m_inLoop = true;
iterateChildren(nodep);
}
m_inLoop = true;
iterateChildren(nodep);
}
//--------------------
@ -641,5 +641,5 @@ public:
void V3Delayed::delayedAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DelayedVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("delayed", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("delayed", 0, dumpTreeLevel() >= 3);
}

View File

@ -166,5 +166,5 @@ public:
void V3Depth::depthAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DepthVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("depth", 0, dumpTree() >= 6);
V3Global::dumpCheckGlobalTree("depth", 0, dumpTreeLevel() >= 6);
}

View File

@ -127,5 +127,5 @@ public:
void V3DepthBlock::depthBlockAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DepthBlockVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deepblock", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("deepblock", 0, dumpTreeLevel() >= 3);
}

View File

@ -285,5 +285,5 @@ public:
void V3Descope::descopeAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DescopeVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("descope", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("descope", 0, dumpTreeLevel() >= 3);
}

View File

@ -334,17 +334,20 @@ class AstToDfgVisitor final : public VNVisitor {
const uint32_t bEnd = b.m_lsb + bWidth;
const uint32_t overlapEnd = std::min(aEnd, bEnd) - 1;
varp->varp()->v3warn( //
MULTIDRIVEN,
"Bits [" //
<< overlapEnd << ":" << b.m_lsb << "] of signal "
<< varp->varp()->prettyNameQ()
<< " have multiple combinational drivers\n"
<< a.m_fileline->warnOther() << "... Location of first driver\n"
<< a.m_fileline->warnContextPrimary() << '\n'
<< b.m_fileline->warnOther() << "... Location of other driver\n"
<< b.m_fileline->warnContextSecondary() << varp->varp()->warnOther()
<< "... Only the first driver will be respected");
if (a.m_fileline->operatorCompare(*b.m_fileline) != 0) {
varp->varp()->v3warn( //
MULTIDRIVEN,
"Bits [" //
<< overlapEnd << ":" << b.m_lsb << "] of signal "
<< varp->varp()->prettyNameQ()
<< " have multiple combinational drivers\n"
<< a.m_fileline->warnOther() << "... Location of first driver\n"
<< a.m_fileline->warnContextPrimary() << '\n'
<< b.m_fileline->warnOther() << "... Location of other driver\n"
<< b.m_fileline->warnContextSecondary()
<< varp->varp()->warnOther()
<< "... Only the first driver will be respected");
}
// If the first driver completely covers the range of the second driver,
// we can just delete the second driver completely, otherwise adjust the
@ -437,6 +440,7 @@ class AstToDfgVisitor final : public VNVisitor {
void visit(AstCell* nodep) override { markReferenced(nodep); }
void visit(AstNodeProcedure* nodep) override { markReferenced(nodep); }
void visit(AstVar* nodep) override {
if (nodep->isSc()) return;
// No need to (and in fact cannot) handle variables with unsupported dtypes
if (!DfgVertex::isSupportedDType(nodep->dtypep())) return;
// Mark ports as having external references

View File

@ -237,7 +237,7 @@ void V3DfgOptimizer::extract(AstNetlist* netlistp) {
UINFO(2, __FUNCTION__ << ": " << endl);
// Extract more optimization candidates
DataflowExtractVisitor::apply(netlistp);
V3Global::dumpCheckGlobalTree("dfg-extract", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("dfg-extract", 0, dumpTreeLevel() >= 3);
}
void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
@ -267,7 +267,7 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
// Build the DFG of this module
const std::unique_ptr<DfgGraph> dfg{V3DfgPasses::astToDfg(*modp, ctx)};
if (dumpDfg() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-input");
if (dumpDfgLevel() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-input");
// Extract the cyclic sub-graphs. We do this because a lot of the optimizations assume a
// DAG, and large, mostly acyclic graphs could not be optimized due to the presence of
@ -284,7 +284,7 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
// For each cyclic component
for (auto& component : cyclicComponents) {
if (dumpDfg() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
// TODO: Apply optimizations safe for cyclic graphs
// Add back under the main DFG (we will convert everything back in one go)
dfg->addGraph(*component);
@ -292,7 +292,7 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
// For each acyclic component
for (auto& component : acyclicComponents) {
if (dumpDfg() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
// Optimize the component
V3DfgPasses::optimize(*component, ctx);
// Add back under the main DFG (we will convert everything back in one go)
@ -300,9 +300,9 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
}
// Convert back to Ast
if (dumpDfg() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-optimized");
if (dumpDfgLevel() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-optimized");
AstModule* const resultModp = V3DfgPasses::dfgToAst(*dfg, ctx);
UASSERT_OBJ(resultModp == modp, modp, "Should be the same module");
}
V3Global::dumpCheckGlobalTree("dfg-optimize", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("dfg-optimize", 0, dumpTreeLevel() >= 3);
}

View File

@ -285,7 +285,7 @@ void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgOptimizationContext& ctx) {
const auto apply = [&](int dumpLevel, const string& name, std::function<void()> pass) {
pass();
if (dumpDfg() >= dumpLevel) {
if (dumpDfgLevel() >= dumpLevel) {
const string strippedName = VString::removeWhitespace(name);
const string label
= ctx.prefix() + "pass-" + cvtToStr(passNumber) + "-" + strippedName;
@ -294,7 +294,7 @@ void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgOptimizationContext& ctx) {
++passNumber;
};
if (dumpDfg() >= 8) dfg.dumpDotAllVarConesPrefixed(ctx.prefix() + "input");
if (dumpDfgLevel() >= 8) dfg.dumpDotAllVarConesPrefixed(ctx.prefix() + "input");
apply(3, "input ", [&]() {});
apply(4, "inlineVars ", [&]() { inlineVars(dfg); });
apply(4, "cse0 ", [&]() { cse(dfg, ctx.m_cseContext0); });
@ -304,5 +304,5 @@ void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgOptimizationContext& ctx) {
apply(4, "cse1 ", [&]() { cse(dfg, ctx.m_cseContext1); });
}
apply(4, "removeVars ", [&]() { removeVars(dfg, ctx.m_removeVarsContext); });
if (dumpDfg() >= 8) dfg.dumpDotAllVarConesPrefixed(ctx.prefix() + "optimized");
if (dumpDfgLevel() >= 8) dfg.dumpDotAllVarConesPrefixed(ctx.prefix() + "optimized");
}

View File

@ -100,5 +100,5 @@ void V3DupFinder::dumpFile(const string& filename, bool tree) {
}
void V3DupFinder::dumpFilePrefixed(const string& nameComment, bool tree) {
if (dump()) dumpFile(v3Global.debugFilename(nameComment) + ".hash", tree);
if (dumpLevel()) dumpFile(v3Global.debugFilename(nameComment) + ".hash", tree);
}

View File

@ -79,6 +79,10 @@ string EmitCBaseVisitorConst::cFuncArgs(const AstCFunc* nodep) {
args += prefixNameProtect(EmitCParentModule::get(nodep));
args += "* vlSelf";
}
if (nodep->needProcess()) {
if (!args.empty()) args += ", ";
args += "VlProcessRef vlProcess";
}
if (!nodep->argTypes().empty()) {
if (!args.empty()) args += ", ";
args += nodep->argTypes();
@ -224,21 +228,12 @@ void EmitCBaseVisitorConst::emitVarDecl(const AstVar* nodep, bool asRef) {
}
void EmitCBaseVisitorConst::emitModCUse(const AstNodeModule* modp, VUseType useType) {
string nl;
for (AstNode* itemp = modp->stmtsp(); itemp; itemp = itemp->nextp()) {
if (AstCUse* const usep = VN_CAST(itemp, CUse)) {
if (usep->useType() == useType) {
if (usep->useType().isInclude()) {
puts("#include \"" + prefixNameProtect(usep) + ".h\"\n");
}
if (usep->useType().isFwdClass()) {
puts("class " + prefixNameProtect(usep) + ";\n");
}
nl = "\n";
}
}
}
puts(nl);
bool nl = false;
forModCUse(modp, useType, [&](string entry) {
puts(entry);
nl = true;
});
if (nl) puts("\n");
}
void EmitCBaseVisitorConst::emitTextSection(const AstNodeModule* modp, VNType type) {

View File

@ -113,6 +113,21 @@ public:
void emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp, bool withScope);
void emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* modp, bool cLinkage = false);
void emitVarDecl(const AstVar* nodep, bool asRef = false);
template <typename F>
static void forModCUse(const AstNodeModule* modp, VUseType useType, F action) {
for (AstNode* itemp = modp->stmtsp(); itemp; itemp = itemp->nextp()) {
if (AstCUse* const usep = VN_CAST(itemp, CUse)) {
if (usep->useType() == useType) {
if (usep->useType().isInclude()) {
action("#include \"" + prefixNameProtect(usep) + ".h\"\n");
}
if (usep->useType().isFwdClass()) {
action("class " + prefixNameProtect(usep) + ";\n");
}
}
}
}
}
void emitModCUse(const AstNodeModule* modp, VUseType useType);
void emitTextSection(const AstNodeModule* modp, VNType type);

View File

@ -417,6 +417,15 @@ void EmitCFunc::emitCCallArgs(const AstNodeCCall* nodep, const string& selfPoint
puts(selfPointer);
comma = true;
}
if (nodep->funcp()->needProcess()) {
if (comma) puts(", ");
if (VN_IS(nodep->backp(), CAwait)) {
puts("vlProcess");
} else {
puts("std::make_shared<VlProcess>()");
}
comma = true;
}
if (!nodep->argTypes().empty()) {
if (comma) puts(", ");
puts(nodep->argTypes());
@ -695,6 +704,8 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP
return "";
} else if (basicp && basicp->isForkSync()) {
return "";
} else if (basicp && basicp->isProcessRef()) {
return "";
} else if (basicp && basicp->isDelayScheduler()) {
return "";
} else if (basicp && basicp->isTriggerScheduler()) {

View File

@ -354,6 +354,8 @@ public:
&& !VN_IS(nodep->rhsp(), CMethodHard) //
&& !VN_IS(nodep->rhsp(), VarRef) //
&& !VN_IS(nodep->rhsp(), AssocSel) //
&& !VN_IS(nodep->rhsp(), MemberSel) //
&& !VN_IS(nodep->rhsp(), StructSel) //
&& !VN_IS(nodep->rhsp(), ArraySel)) {
// Wide functions assign into the array directly, don't need separate assign statement
m_wideTempRefp = VN_AS(nodep->lhsp(), VarRef);
@ -467,7 +469,7 @@ public:
void visit(AstLambdaArgRef* nodep) override { putbs(nodep->nameProtect()); }
void visit(AstWith* nodep) override {
// With uses a C++11 lambda
putbs("[=](");
putbs("[&](");
if (auto* const argrefp = nodep->indexArgRefp()) {
putbs(argrefp->dtypep()->cType(argrefp->nameProtect(), false, false));
puts(",");
@ -475,10 +477,9 @@ public:
if (auto* const argrefp = nodep->valueArgRefp()) {
putbs(argrefp->dtypep()->cType(argrefp->nameProtect(), false, false));
}
// Probably fragile, V3Task may need to convert to a AstCReturn
puts(") { return ");
puts(") {\n");
iterateAndNextConstNull(nodep->exprp());
puts("; }\n");
puts("}\n");
}
void visit(AstNodeCase* nodep) override { // LCOV_EXCL_LINE
// In V3Case...
@ -792,7 +793,7 @@ public:
void visit(AstSysFuncAsTask* nodep) override {
if (!nodep->lhsp()->isWide()) puts("(void)");
iterateAndNextConstNull(nodep->lhsp());
if (!nodep->lhsp()->isWide()) puts(";");
if (!nodep->lhsp()->isWide()) puts(";\n");
}
void visit(AstStackTraceF* nodep) override { puts("VL_STACKTRACE_N()"); }
void visit(AstStackTraceT* nodep) override { puts("VL_STACKTRACE();\n"); }
@ -1043,7 +1044,9 @@ public:
}
void visit(AstCCast* nodep) override {
// Extending a value of the same word width is just a NOP.
if (nodep->size() <= VL_IDATASIZE) {
if (const AstClassRefDType* const classDtypep = VN_CAST(nodep->dtypep(), ClassRefDType)) {
puts("(" + classDtypep->cType("", false, false) + ")(");
} else if (nodep->size() <= VL_IDATASIZE) {
puts("(IData)(");
} else {
puts("(QData)(");

View File

@ -17,11 +17,15 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Ast.h"
#include "V3EmitC.h"
#include "V3EmitCConstInit.h"
#include "V3Global.h"
#include <algorithm>
#include <cstdint>
#include <set>
#include <string>
#include <vector>
VL_DEFINE_DEBUG_FUNCTIONS;
@ -219,7 +223,7 @@ class EmitCHeader final : public EmitCConstInit {
emitted.insert(sdtypep);
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
AstNodeUOrStructDType* subp = VN_CAST(itemp->skipRefp(), NodeUOrStructDType);
AstNodeUOrStructDType* const subp = itemp->getChildStructp();
if (subp && !subp->packed()) {
// Recurse if it belongs to the current module
if (subp->classOrPackagep() == modp) {
@ -235,6 +239,20 @@ class EmitCHeader final : public EmitCConstInit {
puts(itemp->dtypep()->cType(itemp->nameProtect(), false, false));
puts(";\n");
}
puts("\nbool operator==(const " + EmitCBase::prefixNameProtect(sdtypep)
+ "& rhs) const {\n");
puts("return ");
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
if (itemp != sdtypep->membersp()) puts("\n && ");
puts(itemp->nameProtect() + " == " + "rhs." + itemp->nameProtect());
}
puts(";\n");
puts("}\n");
puts("bool operator!=(const " + EmitCBase::prefixNameProtect(sdtypep)
+ "& rhs) const {\n");
puts("return !(*this == rhs);\n}\n");
puts("};\n");
}
void emitStructs(const AstNodeModule* modp) {
@ -287,11 +305,9 @@ class EmitCHeader final : public EmitCConstInit {
+ ".h\"\n");
}
}
emitModCUse(modp, VUseType::INT_INCLUDE);
// Forward declarations required by this AstNodeModule
puts("\nclass " + symClassName() + ";\n");
emitModCUse(modp, VUseType::INT_FWD_CLASS);
// From `systemc_header
emitTextSection(modp, VNType::atScHdr);
@ -300,6 +316,7 @@ class EmitCHeader final : public EmitCConstInit {
// Open class body {{{
puts("\nclass ");
if (!VN_IS(modp, Class)) puts("alignas(VL_CACHE_LINE_BYTES) ");
puts(prefixNameProtect(modp));
if (const AstClass* const classp = VN_CAST(modp, Class)) {
puts(" : public ");
@ -329,11 +346,7 @@ class EmitCHeader final : public EmitCConstInit {
emitTextSection(modp, VNType::atScInt);
// Close class body
if (!VN_IS(modp, Class)) {
puts("} VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES);\n");
} else {
puts("};\n");
}
puts("};\n");
// }}}
// Emit out of class function declarations
@ -364,6 +377,19 @@ class EmitCHeader final : public EmitCConstInit {
if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n");
if (v3Global.usesTiming()) puts("#include \"verilated_timing.h\"\n");
std::set<string> cuse_set;
auto add_to_cuse_set = [&](string s) { cuse_set.insert(s); };
forModCUse(modp, VUseType::INT_INCLUDE, add_to_cuse_set);
forModCUse(modp, VUseType::INT_FWD_CLASS, add_to_cuse_set);
if (const AstClassPackage* const packagep = VN_CAST(modp, ClassPackage)) {
forModCUse(packagep->classp(), VUseType::INT_INCLUDE, add_to_cuse_set);
forModCUse(packagep->classp(), VUseType::INT_FWD_CLASS, add_to_cuse_set);
}
for (const string& s : cuse_set) puts(s);
puts("\n");
emitAll(modp);
if (const AstClassPackage* const packagep = VN_CAST(modp, ClassPackage)) {

View File

@ -22,6 +22,7 @@
#include "V3EmitCFunc.h"
#include "V3Global.h"
#include "V3String.h"
#include "V3ThreadPool.h"
#include "V3UniqueNames.h"
#include <map>
@ -53,6 +54,7 @@ class EmitCGatherDependencies final : VNVisitorConst {
} else if (const AstNodeUOrStructDType* const dtypep
= VN_CAST(nodep, NodeUOrStructDType)) {
if (!dtypep->packed()) {
UASSERT_OBJ(dtypep->classOrPackagep(), nodep, "Unlinked struct package");
m_dependencies.insert(EmitCBase::prefixNameProtect(dtypep->classOrPackagep()));
}
}
@ -145,7 +147,7 @@ class EmitCGatherDependencies final : VNVisitorConst {
}
public:
static const std::set<std::string> gather(AstCFunc* cfuncp) {
static const std::set<std::string> gather(AstCFunc* cfuncp) VL_MT_STABLE {
const EmitCGatherDependencies visitor{cfuncp};
return std::move(visitor.m_dependencies);
}
@ -564,7 +566,8 @@ class EmitCImp final : EmitCFunc {
~EmitCImp() override = default;
public:
static void main(const AstNodeModule* modp, bool slow, std::deque<AstCFile*>& cfilesr) {
static void main(const AstNodeModule* modp, bool slow,
std::deque<AstCFile*>& cfilesr) VL_MT_STABLE {
EmitCImp{modp, slow, cfilesr};
}
};
@ -908,7 +911,7 @@ class EmitCTrace final : EmitCFunc {
~EmitCTrace() override = default;
public:
static void main(AstNodeModule* modp, bool slow, std::deque<AstCFile*>& cfilesr) {
static void main(AstNodeModule* modp, bool slow, std::deque<AstCFile*>& cfilesr) VL_MT_STABLE {
EmitCTrace{modp, slow, cfilesr};
}
};
@ -921,24 +924,37 @@ void V3EmitC::emitcImp() {
// Make parent module pointers available.
const EmitCParentModule emitCParentModule;
std::list<std::deque<AstCFile*>> cfiles;
std::list<std::future<void>> futures;
// Process each module in turn
for (const AstNode* nodep = v3Global.rootp()->modulesp(); nodep; nodep = nodep->nextp()) {
if (VN_IS(nodep, Class)) continue; // Imped with ClassPackage
const AstNodeModule* const modp = VN_AS(nodep, NodeModule);
cfiles.emplace_back();
EmitCImp::main(modp, /* slow: */ true, cfiles.back());
auto& slowCfilesr = cfiles.back();
futures.push_back(V3ThreadPool::s().enqueue<void>(
[modp, &slowCfilesr]() { EmitCImp::main(modp, /* slow: */ true, slowCfilesr); }));
cfiles.emplace_back();
EmitCImp::main(modp, /* slow: */ false, cfiles.back());
auto& fastCfilesr = cfiles.back();
futures.push_back(V3ThreadPool::s().enqueue<void>(
[modp, &fastCfilesr]() { EmitCImp::main(modp, /* slow: */ false, fastCfilesr); }));
}
// Emit trace routines (currently they can only exist in the top module)
if (v3Global.opt.trace() && !v3Global.opt.lintOnly()) {
cfiles.emplace_back();
EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ true, cfiles.back());
auto& slowCfilesr = cfiles.back();
futures.push_back(V3ThreadPool::s().enqueue<void>([&slowCfilesr]() {
EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ true, slowCfilesr);
}));
cfiles.emplace_back();
EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ false, cfiles.back());
auto& fastCfilesr = cfiles.back();
futures.push_back(V3ThreadPool::s().enqueue<void>([&fastCfilesr]() {
EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ false, fastCfilesr);
}));
}
// Wait for futures
V3ThreadPool::waitForFutures(futures);
for (const auto& collr : cfiles) {
for (const auto cfilep : collr) v3Global.rootp()->addFilesp(cfilep);
}

View File

@ -51,6 +51,14 @@ private:
// Not defining main_time/vl_time_stamp, so
v3Global.opt.addCFlags("-DVL_TIME_CONTEXT"); // On MSVC++ anyways
// Optional main top name argument, with empty string replacement
string topArg;
string topName = v3Global.opt.mainTopName();
if (!topName.empty()) {
if (topName == "-") topName = "";
topArg = ", \"" + topName + "\"";
}
// Heavily commented output, as users are likely to look at or copy this code
ofp()->putsHeader();
puts("// DESCRIPTION: main() calling loop, created with Verilator --main\n");
@ -71,7 +79,7 @@ private:
puts("// Construct the Verilated model, from Vtop.h generated from Verilating\n");
puts("const std::unique_ptr<" + topClassName() + "> topp{new " + topClassName()
+ "{contextp.get()}};\n");
+ "{contextp.get()" + topArg + "}};\n");
puts("\n");
puts("// Simulate until $finish\n");

View File

@ -89,7 +89,7 @@ class EmitCModel final : public EmitCFunc {
puts("\n");
puts("// This class is the main interface to the Verilated model\n");
puts("class " + topClassName() + " VL_NOT_FINAL : ");
puts("class alignas(VL_CACHE_LINE_BYTES) " + topClassName() + " VL_NOT_FINAL : ");
if (optSystemC()) {
// SC_MODULE, but with multiple-inheritance of VerilatedModel
puts("public ::sc_core::sc_module, ");
@ -238,7 +238,7 @@ class EmitCModel final : public EmitCFunc {
puts("std::unique_ptr<VerilatedTraceConfig> traceConfig() const override final;\n");
}
puts("} VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES);\n");
puts("};\n");
ofp()->putsEndGuard();

View File

@ -440,7 +440,8 @@ void EmitCSyms::emitSymHdr() {
}
puts("\n// SYMS CLASS (contains all model state)\n");
puts("class " + symClassName() + " final : public VerilatedSyms {\n");
puts("class alignas(VL_CACHE_LINE_BYTES)" + symClassName()
+ " final : public VerilatedSyms {\n");
ofp()->putsPrivate(false); // public:
puts("// INTERNAL STATE\n");
@ -554,7 +555,7 @@ void EmitCSyms::emitSymHdr() {
puts("void " + protect("__Vserialize") + "(VerilatedSerialize& os);\n");
puts("void " + protect("__Vdeserialize") + "(VerilatedDeserialize& os);\n");
}
puts("} VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES);\n");
puts("};\n");
ofp()->putsEndGuard();
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);

View File

@ -424,6 +424,12 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
iterateAndNextConstNull(nodep->exprsp());
puts(")");
}
void visit(AstExprStmt* nodep) override {
putfs(nodep, "$_EXPRSTMT(\n");
iterateAndNextConstNull(nodep->stmtsp());
putbs(", ");
puts(");\n");
}
void visit(AstCMethodHard* nodep) override {
iterateConst(nodep->fromp());
@ -434,6 +440,15 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
}
puts(")");
}
void visit(AstCMethodCall* nodep) override {
iterateConst(nodep->fromp());
puts("." + nodep->name() + "(");
for (AstNode* pinp = nodep->argsp(); pinp; pinp = pinp->nextp()) {
if (pinp != nodep->argsp()) puts(", ");
iterateConst(pinp);
}
puts(")");
}
// Operators
virtual void emitVerilogFormat(AstNode* nodep, const string& format, AstNode* lhsp = nullptr,

View File

@ -188,7 +188,7 @@ class EmitXmlFileVisitor final : public VNVisitorConst {
void visit(AstVar* nodep) override {
const VVarType typ = nodep->varType();
const string kw = nodep->verilogKwd();
const string vt = nodep->dtypep()->name();
const string vt = nodep->dtypep() ? nodep->dtypep()->name() : "";
outputTag(nodep, "");
if (nodep->isIO()) {
puts(" dir=");
@ -225,7 +225,7 @@ class EmitXmlFileVisitor final : public VNVisitorConst {
void visit(AstPin* nodep) override {
// What we call a pin in verilator is a port in the IEEE spec.
outputTag(nodep, "port"); // IEEE: vpiPort
if (nodep->modVarp()->isIO()) {
if (nodep->modVarp() && nodep->modVarp()->isIO()) {
puts(" direction=\"" + nodep->modVarp()->direction().xmlKwd() + "\"");
}
puts(" portIndex=\"" + cvtToStr(nodep->pinNum()) + "\""); // IEEE: vpiPortIndex
@ -254,7 +254,7 @@ class EmitXmlFileVisitor final : public VNVisitorConst {
void visit(AstNodeCCall* nodep) override {
outputTag(nodep, "");
puts(" func=");
putsQuoted(nodep->funcp()->name());
putsQuoted(nodep->funcp() ? nodep->funcp()->name() : nodep->name());
outputChildrenEnd(nodep, "");
}
@ -401,7 +401,7 @@ private:
}
}
void visit(AstCell* nodep) override {
if (nodep->modp()->dead()) return;
if (nodep->modp() && nodep->modp()->dead()) return;
if (!m_hasChildren) m_os << ">\n";
m_os << "<cell " << nodep->fileline()->xmlDetailedLocation() << " name=\"" << nodep->name()
<< "\""

View File

@ -220,10 +220,10 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra)
tellManual(2);
}
#ifndef V3ERROR_NO_GLOBAL_
if (dumpTree() || debug()) {
if (dumpTreeLevel() || debug()) {
V3Broken::allowMidvisitorCheck(true);
V3ThreadPool::s().requestExclusiveAccess([&]() VL_REQUIRES(m_mutex) {
if (dumpTree()) {
if (dumpTreeLevel()) {
v3Global.rootp()->dumpTreeFile(
v3Global.debugFilename("final.tree", 990));
}

View File

@ -54,17 +54,17 @@ public:
// Boolean information we track per-line, but aren't errors
I_CELLDEFINE, // Inside cell define from `celldefine/`endcelldefine
I_COVERAGE, // Coverage is on/off from /*verilator coverage_on/off*/
I_TRACING, // Tracing is on/off from /*verilator tracing_on/off*/
I_LINT, // All lint messages
I_UNUSED, // Unused genvar, parameter or signal message (Backward Compatibility)
I_DEF_NETTYPE_WIRE, // `default_nettype is WIRE (false=NONE)
I_LINT, // All lint messages
I_TIMING, // Enable timing from /*verilator timing_on/off*/
I_TRACING, // Tracing is on/off from /*verilator tracing_on/off*/
I_UNUSED, // Unused genvar, parameter or signal message (Backward Compatibility)
// Error codes:
E_PORTSHORT, // Error: Output port is connected to a constant, electrical short
E_UNSUPPORTED, // Error: Unsupported (generally)
E_TASKNSVAR, // Error: Task I/O not simple
E_NEEDTIMINGOPT, // Error: --timing/--no-timing option not specified
E_NOTIMING, // Timing control encountered with --no-timing
E_PORTSHORT, // Error: Output port is connected to a constant, electrical short
E_TASKNSVAR, // Error: Task I/O not simple
E_UNSUPPORTED, // Error: Unsupported (generally)
//
// Warning codes:
EC_FIRST_WARN, // Just a code so the program knows where to start warnings
@ -116,6 +116,7 @@ public:
MODDUP, // Duplicate module
MULTIDRIVEN, // Driven from multiple blocks
MULTITOP, // Multiple top level modules
NEWERSTD, // Newer language standard required
NOLATCH, // No latch detected in always_latch block
NULLPORT, // Null port detected in module definition
PINCONNECTEMPTY,// Cell pin connected by name with empty reference
@ -155,10 +156,10 @@ public:
VARHIDDEN, // Hiding variable
WAITCONST, // Wait condition is constant
WIDTH, // Width mismatch
WIDTHTRUNC, // Width mismatch- lhs < rhs
WIDTHEXPAND, // Width mismatch- lhs > rhs
WIDTHXZEXPAND, // Width mismatch- lhs > rhs xz filled
WIDTHCONCAT, // Unsized numbers/parameters in concatenations
WIDTHEXPAND, // Width mismatch- lhs > rhs
WIDTHTRUNC, // Width mismatch- lhs < rhs
WIDTHXZEXPAND, // Width mismatch- lhs > rhs xz filled
ZERODLY, // #0 delay
_ENUM_MAX
// ***Add new elements below also***
@ -180,9 +181,9 @@ public:
// Leading spaces indicate it can't be disabled.
" MIN", " INFO", " FATAL", " FATALEXIT", " FATALSRC", " ERROR", " FIRST_NAMED",
// Boolean
" I_CELLDEFINE", " I_COVERAGE", " I_TRACING", " I_LINT", " I_UNUSED", " I_DEF_NETTYPE_WIRE", " I_TIMING",
" I_CELLDEFINE", " I_COVERAGE", " I_DEF_NETTYPE_WIRE", " I_LINT", " I_TIMING", " I_TRACING", " I_UNUSED",
// Errors
"PORTSHORT", "UNSUPPORTED", "TASKNSVAR", "NEEDTIMINGOPT", "NOTIMING",
"NEEDTIMINGOPT", "NOTIMING", "PORTSHORT", "TASKNSVAR", "UNSUPPORTED",
// Warnings
" EC_FIRST_WARN",
"ALWCOMBORDER", "ASCRANGE", "ASSIGNDLY", "ASSIGNIN", "BADSTDPRAGMA",
@ -195,7 +196,7 @@ public:
"IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE",
"INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE",
"LATCH", "LITENDIAN", "MINTYPMAXDLY", "MODDUP",
"MULTIDRIVEN", "MULTITOP", "NOLATCH", "NULLPORT", "PINCONNECTEMPTY",
"MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOLATCH", "NULLPORT", "PINCONNECTEMPTY",
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PROCASSWIRE",
"PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY",
"SELRANGE", "SHORTREAL", "SPLITVAR", "STATICVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET",
@ -203,7 +204,7 @@ public:
"UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS",
"UNPACKED", "UNSIGNED", "UNUSEDGENVAR", "UNUSEDPARAM", "UNUSEDSIGNAL",
"USERERROR", "USERFATAL", "USERINFO", "USERWARN",
"VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHTRUNC", "WIDTHEXPAND", "WIDTHXZEXPAND", "WIDTHCONCAT", "ZERODLY",
"VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHCONCAT", "WIDTHEXPAND", "WIDTHTRUNC", "WIDTHXZEXPAND", "ZERODLY",
" MAX"
};
// clang-format on
@ -233,9 +234,9 @@ public:
return (m_e == ALWCOMBORDER || m_e == ASCRANGE || m_e == BSSPACE || m_e == CASEINCOMPLETE
|| m_e == CASEOVERLAP || m_e == CASEWITHX || m_e == CASEX || m_e == CASTCONST
|| m_e == CMPCONST || m_e == COLONPLUS || m_e == IMPLICIT || m_e == IMPLICITSTATIC
|| m_e == LATCH || m_e == PINMISSING || m_e == REALCVT || m_e == STATICVAR
|| m_e == UNSIGNED || m_e == WIDTH || m_e == WIDTHTRUNC || m_e == WIDTHEXPAND
|| m_e == WIDTHXZEXPAND);
|| m_e == LATCH || m_e == NEWERSTD || m_e == PINMISSING || m_e == REALCVT
|| m_e == STATICVAR || m_e == UNSIGNED || m_e == WIDTH || m_e == WIDTHTRUNC
|| m_e == WIDTHEXPAND || m_e == WIDTHXZEXPAND);
}
// Warnings that are style only
bool styleError() const VL_MT_SAFE {
@ -666,12 +667,10 @@ inline void v3errorEndFatal(std::ostringstream& sstr)
static_assert(true, "")
// Takes an optional "name" (as __VA_ARGS__)
#define VL_DEFINE_DUMP(...) \
VL_ATTR_UNUSED static int dump##__VA_ARGS__() { \
#define VL_DEFINE_DUMP(func, tag) \
VL_ATTR_UNUSED static int dump##func() { \
static int level = -1; \
if (VL_UNLIKELY(level < 0)) { \
std::string tag{VL_STRINGIFY(__VA_ARGS__)}; \
tag[0] = std::tolower(tag[0]); \
const unsigned dumpTag = v3Global.opt.dumpLevel(tag); \
const unsigned dumpSrc = v3Global.opt.dumpSrcLevel(__FILE__); \
const unsigned dumpLevel = dumpTag >= dumpSrc ? dumpTag : dumpSrc; \
@ -685,11 +684,11 @@ inline void v3errorEndFatal(std::ostringstream& sstr)
// Define debug*() and dump*() routines. This needs to be added to every compilation unit so that
// --debugi-<tag/srcfile> and --dumpi-<tag/srcfile> can be used to control debug prints and dumping
#define VL_DEFINE_DEBUG_FUNCTIONS \
VL_DEFINE_DEBUG(); /* Define 'int debug()' */ \
VL_DEFINE_DUMP(); /* Define 'int dump()' */ \
VL_DEFINE_DUMP(Dfg); /* Define 'int dumpDfg()' */ \
VL_DEFINE_DUMP(Graph); /* Define 'int dumpGraph()' */ \
VL_DEFINE_DUMP(Tree); /* Define 'int dumpTree()' */ \
VL_DEFINE_DEBUG(); /* Define 'int debug()' for --debugi */ \
VL_DEFINE_DUMP(Level, ""); /* Define 'int dumpLevel()' for --dumpi */ \
VL_DEFINE_DUMP(DfgLevel, "dfg"); /* Define 'int dumpDfgLevel()' for --dumpi-level */ \
VL_DEFINE_DUMP(GraphLevel, "graph"); /* Define 'int dumpGraphLevel()' for dumpi-graph */ \
VL_DEFINE_DUMP(TreeLevel, "tree"); /* Define 'int dumpTreeLevel()' for dumpi-tree */ \
static_assert(true, "")
//----------------------------------------------------------------------

View File

@ -39,6 +39,38 @@
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Find nodes with side effects, to mark as non-expandable
class ExpandOkVisitor final : public VNVisitor {
private:
// NODE STATE
// AstNode::user2() -> bool. Is pure (along with all children)
const VNUser2InUse m_inuser2;
// STATE - for current visit position (use VL_RESTORER)
// Tracks similar to AstNode::isTreePureRecurse(), but avoid O(n^2)
// False = pure, as nodes that ExpandVisitor inserts preserve pureness
bool m_isImpure = true; // Currently pure
void visit(AstNode* nodep) override {
bool selfImpure = !nodep->isPure();
{
VL_RESTORER(m_isImpure);
m_isImpure = false;
iterateChildren(nodep);
selfImpure |= m_isImpure;
nodep->user2(selfImpure);
}
m_isImpure |= selfImpure;
}
public:
// CONSTRUCTORS
explicit ExpandOkVisitor(AstNetlist* nodep) { iterate(nodep); }
~ExpandOkVisitor() = default;
};
//######################################################################
// Expand state, as a visitor of each AstNode
@ -48,15 +80,24 @@ private:
// AstNode::user1() -> bool. Processed
const VNUser1InUse m_inuser1;
// STATE
// STATE - for current visit position (use VL_RESTORER)
AstNode* m_stmtp = nullptr; // Current statement
// STATE - across all visitors
VDouble0 m_statWides; // Statistic tracking
VDouble0 m_statWideWords; // Statistic tracking
VDouble0 m_statWideLimited; // Statistic tracking
// METHODS
// Use state that ExpandOkVisitor calculated
bool isImpure(AstNode* nodep) {
const bool impure = nodep->user2();
if (impure) UINFO(9, " impure " << nodep << endl);
return impure;
}
bool doExpand(AstNode* nodep) {
bool doExpandWide(AstNode* nodep) {
if (isImpure(nodep)) return false;
++m_statWides;
if (nodep->widthWords() <= v3Global.opt.expandLimit()) {
m_statWideWords += nodep->widthWords();
@ -225,7 +266,7 @@ private:
bool expandWide(AstNodeAssign* nodep, AstConst* rhsp) {
UINFO(8, " Wordize ASSIGN(CONST) " << nodep << endl);
if (!doExpand(nodep)) return false;
if (!doExpandWide(nodep)) return false;
// -> {for each_word{ ASSIGN(WORDSEL(wide,#),WORDSEL(CONST,#))}}
if (rhsp->num().isFourState()) {
rhsp->v3warn(E_UNSUPPORTED, // LCOV_EXCL_LINE // impossible?
@ -241,7 +282,7 @@ private:
//-------- Uniops
bool expandWide(AstNodeAssign* nodep, AstVarRef* rhsp) {
UINFO(8, " Wordize ASSIGN(VARREF) " << nodep << endl);
if (!doExpand(nodep)) return false;
if (!doExpandWide(nodep)) return false;
for (int w = 0; w < nodep->widthWords(); ++w) {
addWordAssign(nodep, w, newAstWordSelClone(rhsp, w));
}
@ -251,7 +292,7 @@ private:
UINFO(8, " Wordize ASSIGN(ARRAYSEL) " << nodep << endl);
UASSERT_OBJ(!VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType), nodep,
"ArraySel with unpacked arrays should have been removed in V3Slice");
if (!doExpand(nodep)) return false;
if (!doExpandWide(nodep)) return false;
for (int w = 0; w < nodep->widthWords(); ++w) {
addWordAssign(nodep, w, newAstWordSelClone(rhsp, w));
}
@ -260,7 +301,7 @@ private:
bool expandWide(AstNodeAssign* nodep, AstNot* rhsp) {
UINFO(8, " Wordize ASSIGN(NOT) " << nodep << endl);
// -> {for each_word{ ASSIGN(WORDSEL(wide,#),NOT(WORDSEL(lhs,#))) }}
if (!doExpand(nodep)) return false;
if (!doExpandWide(nodep)) return false;
FileLine* const fl = rhsp->fileline();
for (int w = 0; w < nodep->widthWords(); ++w) {
addWordAssign(nodep, w, new AstNot{fl, newAstWordSelClone(rhsp->lhsp(), w)});
@ -270,7 +311,7 @@ private:
//-------- Biops
bool expandWide(AstNodeAssign* nodep, AstAnd* rhsp) {
UINFO(8, " Wordize ASSIGN(AND) " << nodep << endl);
if (!doExpand(nodep)) return false;
if (!doExpandWide(nodep)) return false;
FileLine* const fl = nodep->fileline();
for (int w = 0; w < nodep->widthWords(); ++w) {
addWordAssign(nodep, w,
@ -281,7 +322,7 @@ private:
}
bool expandWide(AstNodeAssign* nodep, AstOr* rhsp) {
UINFO(8, " Wordize ASSIGN(OR) " << nodep << endl);
if (!doExpand(nodep)) return false;
if (!doExpandWide(nodep)) return false;
FileLine* const fl = nodep->fileline();
for (int w = 0; w < nodep->widthWords(); ++w) {
addWordAssign(nodep, w,
@ -292,7 +333,7 @@ private:
}
bool expandWide(AstNodeAssign* nodep, AstXor* rhsp) {
UINFO(8, " Wordize ASSIGN(XOR) " << nodep << endl);
if (!doExpand(nodep)) return false;
if (!doExpandWide(nodep)) return false;
FileLine* const fl = nodep->fileline();
for (int w = 0; w < nodep->widthWords(); ++w) {
addWordAssign(nodep, w,
@ -304,7 +345,7 @@ private:
//-------- Triops
bool expandWide(AstNodeAssign* nodep, AstNodeCond* rhsp) {
UINFO(8, " Wordize ASSIGN(COND) " << nodep << endl);
if (!doExpand(nodep)) return false;
if (!doExpandWide(nodep)) return false;
FileLine* const fl = nodep->fileline();
for (int w = 0; w < nodep->widthWords(); ++w) {
addWordAssign(nodep, w,
@ -322,6 +363,7 @@ private:
if (nodep->isWide()) {
// See under ASSIGN(EXTEND)
} else {
if (isImpure(nodep)) return;
AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack();
AstNodeExpr* newp = lhsp;
if (nodep->isQuad()) {
@ -343,7 +385,7 @@ private:
}
bool expandWide(AstNodeAssign* nodep, AstExtend* rhsp) {
UINFO(8, " Wordize ASSIGN(EXTEND) " << nodep << endl);
if (!doExpand(nodep)) return false;
if (!doExpandWide(nodep)) return false;
AstNodeExpr* const rlhsp = rhsp->lhsp();
for (int w = 0; w < rlhsp->widthWords(); ++w) {
addWordAssign(nodep, w, newAstWordSelClone(rlhsp, w));
@ -365,6 +407,7 @@ private:
} else if (nodep->isWide()) {
// See under ASSIGN(WIDE)
} else if (nodep->fromp()->isWide()) {
if (isImpure(nodep)) return;
UINFO(8, " SEL(wide) " << nodep << endl);
UASSERT_OBJ(nodep->widthConst() <= 64, nodep, "Inconsistent width");
// Selection amounts
@ -386,7 +429,7 @@ private:
const uint32_t midMsbOffset
= std::min<uint32_t>(nodep->widthConst(), VL_EDATASIZE) - 1;
AstNodeExpr* const midMsbp = new AstAdd{lfl, new AstConst{lfl, midMsbOffset},
nodep->lsbp()->cloneTree(false)};
nodep->lsbp()->cloneTree(true)};
AstNodeExpr* midwordp = // SEL(from,[midwordnum])
newWordSel(ffl, nodep->fromp()->cloneTree(true), midMsbp, 0);
// newWordSel clones the index, so delete it
@ -414,7 +457,7 @@ private:
if (nodep->widthConst() > VL_EDATASIZE) {
const uint32_t hiMsbOffset = nodep->widthConst() - 1;
AstNodeExpr* const hiMsbp = new AstAdd{lfl, new AstConst{lfl, hiMsbOffset},
nodep->lsbp()->cloneTree(false)};
nodep->lsbp()->cloneTree(true)};
AstNodeExpr* hiwordp = // SEL(from,[hiwordnum])
newWordSel(ffl, nodep->fromp()->cloneTree(true), hiMsbp);
// newWordSel clones the index, so delete it
@ -439,15 +482,16 @@ private:
newp->dtypeFrom(nodep);
VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep);
} else { // Long/Quad from Long/Quad
// No isImpure() check - can handle side effects in below
UINFO(8, " SEL->SHIFT " << nodep << endl);
FileLine* const fl = nodep->fileline();
AstNodeExpr* fromp = nodep->fromp()->unlinkFrBack();
AstNodeExpr* const lsbp = nodep->lsbp()->unlinkFrBack();
if (nodep->isQuad() && !fromp->isQuad()) { fromp = new AstCCast{fl, fromp, nodep}; }
if (nodep->isQuad() && !fromp->isQuad()) fromp = new AstCCast{fl, fromp, nodep};
// {large}>>32 requires 64-bit shift operation; then cast
AstNodeExpr* newp = new AstShiftR{fl, fromp, dropCondBound(lsbp), fromp->width()};
newp->dtypeFrom(fromp);
if (!nodep->isQuad() && fromp->isQuad()) { newp = new AstCCast{fl, newp, nodep}; }
if (!nodep->isQuad() && fromp->isQuad()) newp = new AstCCast{fl, newp, nodep};
newp->dtypeFrom(nodep);
VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep);
}
@ -455,7 +499,7 @@ private:
bool expandWide(AstNodeAssign* nodep, AstSel* rhsp) {
UASSERT_OBJ(nodep->widthMin() == rhsp->widthConst(), nodep, "Width mismatch");
if (!doExpand(nodep)) return false;
if (!doExpandWide(nodep)) return false;
if (VN_IS(rhsp->lsbp(), Const) && VL_BITBIT_E(rhsp->lsbConst()) == 0) {
const int lsb = rhsp->lsbConst();
UINFO(8, " Wordize ASSIGN(SEL,align) " << nodep << endl);
@ -499,6 +543,7 @@ private:
// rhsp: may be allones and can remove AND NOT gate
// lsbp: constant or variable
// Yuk.
if (isImpure(nodep)) return false;
FileLine* const nfl = nodep->fileline();
FileLine* const lfl = lhsp->fileline();
const bool destwide = lhsp->fromp()->isWide();
@ -649,13 +694,14 @@ private:
if (nodep->isWide()) {
// See under ASSIGN(WIDE)
} else {
// No isImpure() check - can handle side effects in below
UINFO(8, " CONCAT " << nodep << endl);
FileLine* const fl = nodep->fileline();
AstNodeExpr* lhsp = nodep->lhsp()->unlinkFrBack();
AstNodeExpr* rhsp = nodep->rhsp()->unlinkFrBack();
const uint32_t rhsshift = rhsp->widthMin();
if (nodep->isQuad() && !lhsp->isQuad()) { lhsp = new AstCCast{fl, lhsp, nodep}; }
if (nodep->isQuad() && !rhsp->isQuad()) { rhsp = new AstCCast{fl, rhsp, nodep}; }
if (nodep->isQuad() && !lhsp->isQuad()) lhsp = new AstCCast{fl, lhsp, nodep};
if (nodep->isQuad() && !rhsp->isQuad()) rhsp = new AstCCast{fl, rhsp, nodep};
AstNodeExpr* const newp = new AstOr{
fl, new AstShiftL{fl, lhsp, new AstConst{fl, rhsshift}, nodep->width()}, rhsp};
newp->dtypeFrom(nodep); // Unsigned
@ -664,7 +710,7 @@ private:
}
bool expandWide(AstNodeAssign* nodep, AstConcat* rhsp) {
UINFO(8, " Wordize ASSIGN(CONCAT) " << nodep << endl);
if (!doExpand(rhsp)) return false;
if (!doExpandWide(rhsp)) return false;
FileLine* const fl = rhsp->fileline();
// Lhs or Rhs may be word, long, or quad.
// newAstWordSelClone nicely abstracts the difference.
@ -686,6 +732,7 @@ private:
if (nodep->isWide()) {
// See under ASSIGN(WIDE)
} else {
if (isImpure(nodep)) return;
FileLine* const fl = nodep->fileline();
AstNodeExpr* lhsp = nodep->lhsp()->unlinkFrBack();
AstNodeExpr* newp;
@ -719,7 +766,7 @@ private:
}
bool expandWide(AstNodeAssign* nodep, AstReplicate* rhsp) {
UINFO(8, " Wordize ASSIGN(REPLICATE) " << nodep << endl);
if (!doExpand(rhsp)) return false;
if (!doExpandWide(rhsp)) return false;
FileLine* const fl = nodep->fileline();
AstNodeExpr* const lhsp = rhsp->lhsp();
const int lhswidth = lhsp->widthMin();
@ -748,6 +795,7 @@ private:
if (nodep->user1SetOnce()) return; // Process once
iterateChildren(nodep);
if (nodep->lhsp()->isWide()) {
if (isImpure(nodep)) return;
UINFO(8, " Wordize EQ/NEQ " << nodep << endl);
// -> (0=={or{for each_word{WORDSEL(lhs,#)^WORDSEL(rhs,#)}}}
FileLine* const fl = nodep->fileline();
@ -773,6 +821,7 @@ private:
iterateChildren(nodep);
FileLine* const fl = nodep->fileline();
if (nodep->lhsp()->isWide()) {
if (isImpure(nodep)) return;
UINFO(8, " Wordize REDOR " << nodep << endl);
// -> (0!={or{for each_word{WORDSEL(lhs,#)}}}
AstNodeExpr* newp = nullptr;
@ -783,6 +832,7 @@ private:
newp = new AstNeq{fl, new AstConst{fl, AstConst::SizedEData{}, 0}, newp};
VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep);
} else {
// No isImpure() check - can handle side effects in below
UINFO(8, " REDOR->EQ " << nodep << endl);
AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack();
AstNodeExpr* const newp = new AstNeq{
@ -795,6 +845,7 @@ private:
iterateChildren(nodep);
FileLine* const fl = nodep->fileline();
if (nodep->lhsp()->isWide()) {
if (isImpure(nodep)) return;
UINFO(8, " Wordize REDAND " << nodep << endl);
// -> (0!={and{for each_word{WORDSEL(lhs,#)}}}
AstNodeExpr* newp = nullptr;
@ -814,6 +865,7 @@ private:
newp};
VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep);
} else {
// No isImpure() check - can handle side effects in below
UINFO(8, " REDAND->EQ " << nodep << endl);
AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack();
AstNodeExpr* const newp = new AstEq{fl, new AstConst{fl, wordMask(lhsp)}, lhsp};
@ -824,6 +876,7 @@ private:
if (nodep->user1SetOnce()) return; // Process once
iterateChildren(nodep);
if (nodep->lhsp()->isWide()) {
if (isImpure(nodep)) return;
UINFO(8, " Wordize REDXOR " << nodep << endl);
// -> (0!={redxor{for each_word{XOR(WORDSEL(lhs,#))}}}
FileLine* const fl = nodep->fileline();
@ -906,14 +959,14 @@ public:
}
};
//----------------------------------------------------------------------
// Top loop
//######################################################################
// Expand class functions
void V3Expand::expandAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ExpandVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("expand", 0, dumpTree() >= 3);
{
ExpandOkVisitor okVisitor{nodep};
ExpandVisitor{nodep};
} // Destruct before checking
V3Global::dumpCheckGlobalTree("expand", 0, dumpTreeLevel() >= 3);
}

View File

@ -253,16 +253,33 @@ class ForceConvertVisitor final : public VNVisitor {
resetRdp->rhsp()->foreach([this](AstNodeVarRef* refp) {
if (refp->access() != VAccess::WRITE) return;
AstVarScope* const vscp = refp->varScopep();
AstVarScope* const newVscp
= vscp->varp()->isContinuously() ? vscp : getForceComponents(vscp).m_valVscp;
AstVarRef* const newpRefp = new AstVarRef{refp->fileline(), newVscp, VAccess::READ};
FileLine* const flp = new FileLine{refp->fileline()};
AstVarRef* const newpRefp = new AstVarRef{refp->fileline(), vscp, VAccess::READ};
newpRefp->user2(1); // Don't replace this read ref with the read signal
refp->replaceWith(newpRefp);
if (vscp->varp()->isContinuously()) {
refp->replaceWith(newpRefp);
} else if (isRangedDType(vscp)) {
refp->replaceWith(new AstOr{
flp,
new AstAnd{
flp, new AstVarRef{flp, getForceComponents(vscp).m_enVscp, VAccess::READ},
new AstVarRef{flp, getForceComponents(vscp).m_valVscp, VAccess::READ}},
new AstAnd{
flp,
new AstNot{flp, new AstVarRef{flp, getForceComponents(vscp).m_enVscp,
VAccess::READ}},
newpRefp}});
} else {
refp->replaceWith(new AstCond{
flp, new AstVarRef{flp, getForceComponents(vscp).m_enVscp, VAccess::READ},
new AstVarRef{flp, getForceComponents(vscp).m_valVscp, VAccess::READ},
newpRefp});
}
VL_DO_DANGLING(refp->deleteTree(), refp);
});
resetEnp->addNext(resetRdp);
relinker.relink(resetEnp);
resetRdp->addNext(resetEnp);
relinker.relink(resetRdp);
}
void visit(AstVarScope* nodep) override {
@ -314,5 +331,5 @@ void V3Force::forceAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
if (!v3Global.hasForceableSignals()) return;
ForceConvertVisitor::apply(nodep);
V3Global::dumpCheckGlobalTree("force", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("force", 0, dumpTreeLevel() >= 3);
}

View File

@ -53,7 +53,7 @@ class GateLogicVertex;
class GateVarVertex;
class GateGraphBaseVisitor VL_NOT_FINAL {
public:
V3Graph* m_graphp; // Graph this class is visiting
V3Graph* const m_graphp; // Graph this class is visiting
explicit GateGraphBaseVisitor(V3Graph* graphp)
: m_graphp{graphp} {}
virtual ~GateGraphBaseVisitor() = default;
@ -199,10 +199,11 @@ private:
GateVarRefList m_rhsVarRefs; // VarRefs on rhs of assignment
AstNode* m_substTreep = nullptr; // What to replace the variable with
// STATE
bool m_buffersOnly; // Set when we only allow simple buffering, no equations (for clocks)
const bool
m_buffersOnly; // Set when we only allow simple buffering, no equations (for clocks)
const AstNodeVarRef* m_lhsVarRef
= nullptr; // VarRef on lhs of assignment (what we're replacing)
bool m_dedupe; // Set when we use isGateDedupable instead of isGateOptimizable
const bool m_dedupe; // Set when we use isGateDedupable instead of isGateOptimizable
int m_ops = 0; // Operation count
// METHODS
@ -277,9 +278,9 @@ private:
public:
// CONSTRUCTORS
GateOkVisitor(AstNode* nodep, bool buffersOnly, bool dedupe) {
m_buffersOnly = buffersOnly;
m_dedupe = dedupe;
GateOkVisitor(AstNode* nodep, bool buffersOnly, bool dedupe)
: m_buffersOnly{buffersOnly}
, m_dedupe{dedupe} {
// Iterate
iterateConst(nodep);
// Check results
@ -332,6 +333,7 @@ private:
AstActive* m_activep = nullptr; // Current active
bool m_activeReducible = true; // Is activation block reducible?
bool m_inSenItem = false; // Underneath AstSenItem; any varrefs are clocks
bool m_inExprStmt = false; // Underneath ExprStmt; don't optimize LHS vars
bool m_inSlow = false; // Inside a slow structure
std::vector<AstNode*> m_optimized; // Logic blocks optimized
@ -421,12 +423,12 @@ private:
// VISITORS
void visit(AstNetlist* nodep) override {
iterateChildren(nodep);
if (dumpGraph() >= 3) m_graph.dumpDotFilePrefixed("gate_pre");
if (dumpGraphLevel() >= 3) m_graph.dumpDotFilePrefixed("gate_pre");
warnSignals(); // Before loss of sync/async pointers
// Decompose clock vectors -- need to do this before removing redundant edges
decomposeClkVectors();
m_graph.removeRedundantEdgesSum(&V3GraphEdge::followAlwaysTrue);
if (dumpGraph() >= 6) m_graph.dumpDotFilePrefixed("gate_simp");
if (dumpGraphLevel() >= 6) m_graph.dumpDotFilePrefixed("gate_simp");
// Find gate interconnect and optimize
m_graph.userClearVertices(); // vertex->user(): bool. Indicates we've set it as consumed
// Get rid of buffers first,
@ -438,43 +440,42 @@ private:
// Remove redundant logic
if (v3Global.opt.fDedupe()) {
dedupe();
if (dumpGraph() >= 6) m_graph.dumpDotFilePrefixed("gate_dedup");
if (dumpGraphLevel() >= 6) m_graph.dumpDotFilePrefixed("gate_dedup");
}
if (v3Global.opt.fAssemble()) {
mergeAssigns();
if (dumpGraph() >= 6) m_graph.dumpDotFilePrefixed("gate_assm");
if (dumpGraphLevel() >= 6) m_graph.dumpDotFilePrefixed("gate_assm");
}
// Consumption warnings
consumedMark();
if (dumpGraph() >= 3) m_graph.dumpDotFilePrefixed("gate_opt");
if (dumpGraphLevel() >= 3) m_graph.dumpDotFilePrefixed("gate_opt");
// Rewrite assignments
consumedMove();
}
void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
{
m_modp = nodep;
m_activeReducible = true;
iterateChildren(nodep);
}
VL_RESTORER(m_activeReducible);
m_modp = nodep;
m_activeReducible = true;
iterateChildren(nodep);
}
void visit(AstScope* nodep) override {
UINFO(4, " SCOPE " << nodep << endl);
VL_RESTORER(m_scopep);
m_scopep = nodep;
m_logicVertexp = nullptr;
iterateChildren(nodep);
m_scopep = nullptr;
}
void visit(AstActive* nodep) override {
// Create required blocks and add to module
UINFO(4, " BLOCK " << nodep << endl);
VL_RESTORER(m_activep);
VL_RESTORER(m_activeReducible);
m_activeReducible = !(nodep->hasClocked()); // Seq logic outputs aren't reducible
m_activep = nodep;
AstNode::user2ClearTree();
iterateChildren(nodep);
AstNode::user2ClearTree();
m_activep = nullptr;
m_activeReducible = true;
}
void visit(AstNodeVarRef* nodep) override {
if (m_scopep) {
@ -498,6 +499,10 @@ private:
// the weight will increase
if (nodep->access().isWriteOrRW()) {
new V3GraphEdge{&m_graph, m_logicVertexp, vvertexp, 1};
if (m_inExprStmt) {
m_logicVertexp->clearReducibleAndDedupable("LHS var in ExprStmt");
m_logicVertexp->setConsumed("LHS var in ExprStmt");
}
}
if (nodep->access().isReadOrRW()) {
new V3GraphEdge{&m_graph, vvertexp, m_logicVertexp, 1};
@ -506,31 +511,31 @@ private:
}
void visit(AstAlwaysPublic* nodep) override {
VL_RESTORER(m_inSlow);
{
m_inSlow = true;
iterateNewStmt(nodep, "AlwaysPublic", nullptr);
}
m_inSlow = true;
iterateNewStmt(nodep, "AlwaysPublic", nullptr);
}
void visit(AstCFunc* nodep) override {
iterateNewStmt(nodep, "User C Function", "User C Function");
}
void visit(AstClocking* nodep) override { iterateNewStmt(nodep, nullptr, nullptr); }
void visit(AstExprStmt* nodep) override {
VL_RESTORER(m_inExprStmt);
m_inExprStmt = true;
iterateChildren(nodep);
}
void visit(AstSenItem* nodep) override {
VL_RESTORER(m_inSenItem);
m_inSenItem = true;
if (m_logicVertexp) { // Already under logic; presumably a SenGate
iterateChildren(nodep);
} else { // Standalone item, probably right under a SenTree
iterateNewStmt(nodep, nullptr, nullptr);
}
m_inSenItem = false;
}
void visit(AstNodeProcedure* nodep) override {
VL_RESTORER(m_inSlow);
{
m_inSlow = VN_IS(nodep, Initial) || VN_IS(nodep, Final);
iterateNewStmt(nodep, (nodep->isJustOneBodyStmt() ? nullptr : "Multiple Stmts"),
nullptr);
}
m_inSlow = VN_IS(nodep, Initial) || VN_IS(nodep, Final);
iterateNewStmt(nodep, (nodep->isJustOneBodyStmt() ? nullptr : "Multiple Stmts"), nullptr);
}
void visit(AstAssignAlias* nodep) override { //
iterateNewStmt(nodep, nullptr, nullptr);
@ -543,10 +548,8 @@ private:
}
void visit(AstTraceDecl* nodep) override {
VL_RESTORER(m_inSlow);
{
m_inSlow = true;
iterateNewStmt(nodep, "Tracing", "Tracing");
}
m_inSlow = true;
iterateNewStmt(nodep, "Tracing", "Tracing");
}
void visit(AstConcat* nodep) override {
UASSERT_OBJ(!(VN_IS(nodep->backp(), NodeAssign)
@ -1269,9 +1272,7 @@ private:
public:
explicit GateMergeAssignsGraphVisitor(V3Graph* graphp)
: GateGraphBaseVisitor{graphp} {
m_graphp = graphp; // In base
}
: GateGraphBaseVisitor{graphp} {}
void mergeAssignsTree(GateVarVertex* vvertexp) { vvertexp->accept(*this); }
VDouble0 numMergedAssigns() { return m_numMergedAssigns; }
};
@ -1370,8 +1371,8 @@ private:
if (vsp->user2SetOnce()) return VNUser{0};
UINFO(9, "CLK DECOMP Var - " << vvertexp << " : " << vsp << endl);
if (vsp->varp()->width() > 1) {
m_seen_clk_vectors++;
m_total_seen_clk_vectors++;
++m_seen_clk_vectors;
++m_total_seen_clk_vectors;
}
const GateClkDecompState* const currState = reinterpret_cast<GateClkDecompState*>(vu.c());
GateClkDecompState nextState{currState->m_offset, vsp};
@ -1488,5 +1489,5 @@ void GateVisitor::decomposeClkVectors() {
void V3Gate::gateAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ const GateVisitor visitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("gate", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("gate", 0, dumpTreeLevel() >= 3);
}

View File

@ -544,28 +544,28 @@ void GraphAcyc::main() {
// edges (and thus can't represent loops - if we did the unbreakable
// marking right, anyways)
buildGraph(m_origGraphp);
if (dumpGraph() >= 6) m_breakGraph.dumpDotFilePrefixed("acyc_pre");
if (dumpGraphLevel() >= 6) m_breakGraph.dumpDotFilePrefixed("acyc_pre");
// Perform simple optimizations before any cuttings
simplify(false);
if (dumpGraph() >= 5) m_breakGraph.dumpDotFilePrefixed("acyc_simp");
if (dumpGraphLevel() >= 5) m_breakGraph.dumpDotFilePrefixed("acyc_simp");
UINFO(4, " Cutting trivial loops\n");
simplify(true);
if (dumpGraph() >= 6) m_breakGraph.dumpDotFilePrefixed("acyc_mid");
if (dumpGraphLevel() >= 6) m_breakGraph.dumpDotFilePrefixed("acyc_mid");
UINFO(4, " Ranking\n");
m_breakGraph.rank(&V3GraphEdge::followNotCutable);
if (dumpGraph() >= 6) m_breakGraph.dumpDotFilePrefixed("acyc_rank");
if (dumpGraphLevel() >= 6) m_breakGraph.dumpDotFilePrefixed("acyc_rank");
UINFO(4, " Placement\n");
place();
if (dumpGraph() >= 6) m_breakGraph.dumpDotFilePrefixed("acyc_place");
if (dumpGraphLevel() >= 6) m_breakGraph.dumpDotFilePrefixed("acyc_place");
UINFO(4, " Final Ranking\n");
// Only needed to assert there are no loops in completed graph
m_breakGraph.rank(&V3GraphEdge::followAlwaysTrue);
if (dumpGraph() >= 6) m_breakGraph.dumpDotFilePrefixed("acyc_done");
if (dumpGraphLevel() >= 6) m_breakGraph.dumpDotFilePrefixed("acyc_done");
}
void V3Graph::acyclic(V3EdgeFuncP edgeFuncp) {

View File

@ -37,7 +37,7 @@ protected:
// Utilities
void dumpSelf() {
if (dumpGraph() >= 9) m_graph.dumpDotFilePrefixed("v3graphtest_" + name());
if (dumpGraphLevel() >= 9) m_graph.dumpDotFilePrefixed("v3graphtest_" + name());
}
public:

View File

@ -307,7 +307,7 @@ private:
}
void visit(AstNodeModule* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, false, [=]() { //
m_hash += nodep->origName();
m_hash += nodep->name();
});
}
void visit(AstNodePreSel* nodep) override {

View File

@ -727,5 +727,5 @@ void V3Inline::inlineAll(AstNetlist* nodep) {
}
{ InlineIntfRefVisitor{nodep}; }
V3Global::dumpCheckGlobalTree("inline", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("inline", 0, dumpTreeLevel() >= 3);
}

View File

@ -517,10 +517,10 @@ public:
// otherwise done
if (pinVarp->direction() == VDirection::INPUT
&& cellp->modp()->unconnectedDrive().isSetTrue()) {
pinp->exprp(new AstConst{pinp->fileline(), AstConst::StringToParse{}, "'1"});
pinp->exprp(new AstConst{pinp->fileline(), AstConst::All1{}});
} else if (pinVarp->direction() == VDirection::INPUT
&& cellp->modp()->unconnectedDrive().isSetFalse()) {
pinp->exprp(new AstConst{pinp->fileline(), AstConst::StringToParse{}, "'0"});
pinp->exprp(new AstConst{pinp->fileline(), AstConst::All0{}});
} else {
return nullptr;
}
@ -616,11 +616,11 @@ void V3Inst::checkOutputShort(AstPin* nodep) {
void V3Inst::instAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ InstVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("inst", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("inst", 0, dumpTreeLevel() >= 3);
}
void V3Inst::dearrayAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ InstDeVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("dearray", 0, dumpTree() >= 6);
V3Global::dumpCheckGlobalTree("dearray", 0, dumpTreeLevel() >= 6);
}

View File

@ -515,5 +515,5 @@ void V3Life::lifeAll(AstNetlist* nodep) {
LifeState state;
LifeTopVisitor{nodep, &state};
} // Destruct before checking
V3Global::dumpCheckGlobalTree("life", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("life", 0, dumpTreeLevel() >= 3);
}

View File

@ -369,5 +369,5 @@ void V3LifePost::lifepostAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
// Mark redundant AssignPost
{ LifePostDlyVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("life_post", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("life_post", 0, dumpTreeLevel() >= 3);
}

View File

@ -116,6 +116,7 @@ private:
VSymGraph m_mods; // Symbol table of all module names
LinkCellsGraph m_graph; // Linked graph of all cell interconnects
LibraryVertex* m_libVertexp = nullptr; // Vertex at root of all libraries
int m_dedupNum = 0; // Package dedup number
const V3GraphVertex* m_topVertexp = nullptr; // Vertex of top module
std::unordered_set<string> m_declfnWarned; // Files we issued DECLFILENAME on
string m_origTopModuleName; // original name of the top module
@ -167,7 +168,7 @@ private:
iterateChildren(nodep);
// Find levels in graph
m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue);
if (dumpGraph()) m_graph.dumpDotFilePrefixed("linkcells");
if (dumpGraphLevel()) m_graph.dumpDotFilePrefixed("linkcells");
m_graph.rank();
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
if (const LinkCellsVertex* const vvertexp = dynamic_cast<LinkCellsVertex*>(itp)) {
@ -504,8 +505,13 @@ private:
<< "... Location of original declaration\n"
<< foundp->warnContextSecondary());
}
nodep->unlinkFrBack();
VL_DO_DANGLING(pushDeletep(nodep), nodep);
if (VN_IS(nodep, Package)) {
// Packages may be imported, we instead rename to be unique
nodep->name(nodep->name() + "__Vdedup" + cvtToStr(m_dedupNum++));
} else {
nodep->unlinkFrBack();
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
} else if (!foundp) {
m_mods.rootp()->insert(nodep->name(), new VSymEnt{&m_mods, nodep});
}

View File

@ -158,15 +158,13 @@ private:
std::array<ScopeAliasMap, SAMN__MAX> m_scopeAliasMap; // Map of <lhs,rhs> aliases
std::vector<VSymEnt*> m_ifaceVarSyms; // List of AstIfaceRefDType's to be imported
IfaceModSyms m_ifaceModSyms; // List of AstIface+Symbols to be processed
bool m_forPrimary; // First link
bool m_forPrearray; // Compress cell__[array] refs
bool m_forScopeCreation; // Remove VarXRefs for V3Scope
const VLinkDotStep m_step; // Operational step
public:
// METHODS
void dumpSelf(const string& nameComment = "linkdot", bool force = false) {
if (dump() >= 6 || force) {
if (dumpLevel() >= 6 || force) {
const string filename = v3Global.debugFilename(nameComment) + ".txt";
const std::unique_ptr<std::ofstream> logp{V3File::new_ofstream(filename)};
if (logp->fail()) v3fatal("Can't write " << filename);
@ -193,7 +191,7 @@ public:
}
void preErrorDump() {
static bool diddump = false;
if (!diddump && dumpTree()) {
if (!diddump && dumpTreeLevel()) {
diddump = true;
dumpSelf("linkdot-preerr", true);
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("linkdot-preerr.tree"));
@ -202,11 +200,9 @@ public:
// CONSTRUCTORS
LinkDotState(AstNetlist* rootp, VLinkDotStep step)
: m_syms{rootp} {
: m_syms{rootp}
, m_step(step) {
UINFO(4, __FUNCTION__ << ": " << endl);
m_forPrimary = (step == LDS_PRIMARY);
m_forPrearray = (step == LDS_PARAMED || step == LDS_PRIMARY);
m_forScopeCreation = (step == LDS_SCOPED);
s_errorThisp = this;
V3Error::errorExitCb(preErrorDumpHandler); // If get error, dump self
}
@ -217,9 +213,10 @@ public:
// ACCESSORS
VSymGraph* symsp() { return &m_syms; }
bool forPrimary() const { return m_forPrimary; }
bool forPrearray() const { return m_forPrearray; }
bool forScopeCreation() const { return m_forScopeCreation; }
int stepNumber() const { return int(m_step); }
bool forPrimary() const { return m_step == LDS_PRIMARY; }
bool forPrearray() const { return m_step == LDS_PARAMED || m_step == LDS_PRIMARY; }
bool forScopeCreation() const { return m_step == LDS_SCOPED; }
// METHODS
static string nodeTextType(AstNode* nodep) {
@ -590,7 +587,7 @@ public:
baddot = ident; // So user can see where they botched it
okSymp = lookupSymp;
string altIdent;
if (m_forPrearray) {
if (forPrearray()) {
// GENFOR Begin is foo__BRA__##__KET__ after we've genloop unrolled,
// but presently should be just "foo".
// Likewise cell foo__[array] before we've expanded arrays is just foo
@ -620,7 +617,7 @@ public:
else if (ident == "$root") {
lookupSymp = rootEntp();
// We've added the '$root' module, now everything else is one lower
if (!m_forPrearray) {
if (!forPrearray()) {
lookupSymp = lookupSymp->findIdFlat(ident);
UASSERT(lookupSymp, "Cannot find $root module under netlist");
}
@ -1017,9 +1014,12 @@ class LinkDotFindVisitor final : public VNVisitor {
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
if (VN_IS(stmtp, Var) || VN_IS(stmtp, Foreach)) {
std::string name;
const std::string stepStr = m_statep->forPrimary()
? ""
: std::to_string(m_statep->stepNumber()) + "_";
do {
++m_modBlockNum;
name = "unnamedblk" + cvtToStr(m_modBlockNum);
name = "unnamedblk" + stepStr + cvtToStr(m_modBlockNum);
// Increment again if earlier pass of V3LinkDot claimed this name
} while (m_curSymp->findIdFlat(name));
nodep->name(name);
@ -1174,6 +1174,7 @@ class LinkDotFindVisitor final : public VNVisitor {
}
AstVar* const newvarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, varname,
VFlagChildDType{}, dtypep};
newvarp->lifetime(VLifetime::STATIC);
nodep->varp(newvarp);
iterate(nodep->exprp());
}
@ -1211,7 +1212,8 @@ class LinkDotFindVisitor final : public VNVisitor {
return;
}
const bool nansiBad
= ((findvarp->isDeclTyped() && nodep->isDeclTyped())
= (((findvarp->isDeclTyped() || findvarp->isNet())
&& (nodep->isDeclTyped() || nodep->isNet()))
|| (findvarp->isIO() && nodep->isIO())); // e.g. !(output && output)
const bool ansiBad
= findvarp->isAnsi() || nodep->isAnsi(); // dup illegal with ANSI
@ -2019,9 +2021,10 @@ private:
int m_modportNum = 0; // Uniqueify modport numbers
bool m_inSens = false; // True if in senitem
std::set<std::string> m_ifClassImpNames; // Names imported from interface class
std::set<AstClass*> m_extendsParam; // Classes that has a parameter as its super class
bool m_insideClassExtParam = false; // Inside a class that extends a parameter.
// It may be set only in linkDotPrimary.
std::set<AstClass*> m_extendsParam; // Classes that have a parameterized super class
// (except the default instances)
// They are added to the set only in linkDotPrimary.
bool m_insideClassExtParam = false; // Inside a class from m_extendsParam
struct DotStates {
DotPosition m_dotPos; // Scope part of dotted resolution
@ -2770,49 +2773,59 @@ private:
}
}
}
// Don't throw error ifthe reference is inside a class that extends a param, because
// some members can't be linked in such a case. m_insideClassExtParam may be true only
// in the first stage of linking.
if (!ok && !m_insideClassExtParam) {
// Cells/interfaces can't be implicit
const bool isCell = foundp ? VN_IS(foundp->nodep(), Cell) : false;
const bool checkImplicit = (!m_ds.m_dotp && m_ds.m_dotText == "" && !isCell);
const bool err = !(checkImplicit && m_statep->implicitOk(m_modp, nodep->name()));
if (err) {
if (foundp) {
nodep->v3error("Found definition of '"
<< m_ds.m_dotText << (m_ds.m_dotText == "" ? "" : ".")
<< nodep->prettyName() << "'"
<< " as a " << foundp->nodep()->typeName()
<< " but expected a " << expectWhat);
} else if (m_ds.m_dotText == "") {
UINFO(7, " ErrParseRef curSymp=se" << cvtToHex(m_curSymp)
<< " ds=" << m_ds.ascii() << endl);
const string suggest = m_statep->suggestSymFallback(
m_ds.m_dotSymp, nodep->name(), VNodeMatcher{});
nodep->v3error("Can't find definition of "
<< expectWhat << ": " << nodep->prettyNameQ() << '\n'
<< (suggest.empty() ? "" : nodep->warnMore() + suggest));
} else {
nodep->v3error("Can't find definition of "
<< (!baddot.empty() ? AstNode::prettyNameQ(baddot)
: nodep->prettyNameQ())
<< " in dotted " << expectWhat << ": '"
<< m_ds.m_dotText + "." + nodep->prettyName() << "'");
if (okSymp) {
okSymp->cellErrorScopes(nodep, AstNode::prettyName(m_ds.m_dotText));
if (!ok) {
if (m_insideClassExtParam) {
// Don't throw error if the reference is inside a class that extends a param,
// because some members can't be linked in such a case. m_insideClassExtParam
// may be true only in the first stage of linking.
// Mark that the Dot statement can't be resolved.
m_ds.m_unresolvedClass = true;
// If the symbol was a scope name, it would be resolved.
if (m_ds.m_dotPos == DP_SCOPE) m_ds.m_dotPos = DP_MEMBER;
} else {
// Cells/interfaces can't be implicit
const bool isCell = foundp ? VN_IS(foundp->nodep(), Cell) : false;
const bool checkImplicit = (!m_ds.m_dotp && m_ds.m_dotText == "" && !isCell);
const bool err
= !(checkImplicit && m_statep->implicitOk(m_modp, nodep->name()));
if (err) {
if (foundp) {
nodep->v3error("Found definition of '"
<< m_ds.m_dotText << (m_ds.m_dotText == "" ? "" : ".")
<< nodep->prettyName() << "'"
<< " as a " << foundp->nodep()->typeName()
<< " but expected a " << expectWhat);
} else if (m_ds.m_dotText == "") {
UINFO(7, " ErrParseRef curSymp=se"
<< cvtToHex(m_curSymp) << " ds=" << m_ds.ascii() << endl);
const string suggest = m_statep->suggestSymFallback(
m_ds.m_dotSymp, nodep->name(), VNodeMatcher{});
nodep->v3error(
"Can't find definition of "
<< expectWhat << ": " << nodep->prettyNameQ() << '\n'
<< (suggest.empty() ? "" : nodep->warnMore() + suggest));
} else {
nodep->v3error("Can't find definition of "
<< (!baddot.empty() ? AstNode::prettyNameQ(baddot)
: nodep->prettyNameQ())
<< " in dotted " << expectWhat << ": '"
<< m_ds.m_dotText + "." + nodep->prettyName() << "'");
if (okSymp) {
okSymp->cellErrorScopes(nodep,
AstNode::prettyName(m_ds.m_dotText));
}
}
m_ds.m_dotErr = true;
}
if (checkImplicit) {
// Create if implicit, and also if error (so only complain once)
// Else if a scope is allowed, making a signal won't help error cascade
AstVarRef* const newp
= new AstVarRef{nodep->fileline(), nodep->name(), VAccess::READ};
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
createImplicitVar(m_curSymp, newp, m_modp, m_modSymp, err);
}
m_ds.m_dotErr = true;
}
if (checkImplicit) {
// Create if implicit, and also if error (so only complain once)
// Else if a scope is allowed, making a signal won't help error cascade
AstVarRef* const newp
= new AstVarRef{nodep->fileline(), nodep->name(), VAccess::READ};
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
createImplicitVar(m_curSymp, newp, m_modp, m_modSymp, err);
}
}
}
@ -3028,6 +3041,10 @@ private:
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
} else if (m_ds.m_dotp && m_ds.m_dotPos == DP_SCOPE) {
// HERE function() . method_called_on_function_return_value()
m_ds.m_dotPos = DP_MEMBER;
m_ds.m_dotText = "";
} else {
checkNoDot(nodep);
}
@ -3109,15 +3126,16 @@ private:
}
} else if (VN_IS(nodep, New) && m_statep->forPrearray()) {
// Resolved in V3Width
} else if (nodep->name() == "randomize" || nodep->name() == "srandom") {
} else if (nodep->name() == "randomize" || nodep->name() == "srandom"
|| nodep->name() == "get_randstate"
|| nodep->name() == "set_randstate") {
// Resolved in V3Width
} else if (nodep->dotted() == "") {
if (nodep->pli()) {
if (v3Global.opt.bboxSys()) {
AstNode* newp;
if (VN_IS(nodep, FuncRef)) {
newp = new AstConst{nodep->fileline(), AstConst::StringToParse{},
"'0"};
newp = new AstConst{nodep->fileline(), AstConst::All0{}};
} else {
AstNode* outp = nullptr;
while (nodep->pinsp()) {
@ -3162,6 +3180,11 @@ private:
void visit(AstSelBit* nodep) override {
if (nodep->user3SetOnce()) return;
iterateAndNextNull(nodep->fromp());
if (m_ds.m_unresolvedClass) {
UASSERT_OBJ(m_ds.m_dotPos != DP_SCOPE, nodep,
"Object of unresolved class on scope position in dotted reference");
return;
}
if (m_ds.m_dotPos
== DP_SCOPE) { // Already under dot, so this is {modulepart} DOT {modulepart}
UINFO(9, " deferring until after a V3Param pass: " << nodep << endl);
@ -3228,7 +3251,16 @@ private:
UINFO(5, " " << nodep << endl);
checkNoDot(nodep);
if (nodep->isExternDef()) {
if (!m_curSymp->findIdFallback("extern " + nodep->name())) {
if (const VSymEnt* const foundp
= m_curSymp->findIdFallback("extern " + nodep->name())) {
const AstNodeFTask* const funcProtop = VN_AS(foundp->nodep(), NodeFTask);
// Copy specifiers.
// External definition cannot have any specifiers, so no value will be overwritten.
nodep->isHideLocal(funcProtop->isHideLocal());
nodep->isHideProtected(funcProtop->isHideProtected());
nodep->isVirtual(funcProtop->isVirtual());
nodep->lifetime(funcProtop->lifetime());
} else {
nodep->v3error("extern not found that declares " + nodep->prettyNameQ());
}
}
@ -3271,6 +3303,86 @@ private:
// No checknodot(nodep), visit(AstScope) will check for LambdaArgRef
iterateChildren(nodep);
}
void visit(AstClassExtends* nodep) override {
// Resolve the symbol and get the class.
// If it is a parameterized case, the class will be resolved after V3Param.cpp
if (nodep->user3SetOnce()) return;
// If the class is resolved, there is nothing more to do
if (nodep->classOrNullp()) return;
if (m_statep->forPrimary()) {
AstNode* cprp = nodep->classOrPkgsp();
VSymEnt* lookSymp = m_curSymp;
if (AstDot* const dotp = VN_CAST(cprp, Dot)) {
dotp->user3(true);
if (AstClassOrPackageRef* lookNodep = VN_CAST(dotp->lhsp(), ClassOrPackageRef)) {
iterate(lookNodep);
cprp = dotp->rhsp();
lookSymp = m_statep->getNodeSym(lookNodep->classOrPackagep());
} else {
dotp->lhsp()->v3error("Attempting to extend" // LCOV_EXCL_LINE
" using non-class under dot");
}
}
AstClassOrPackageRef* const cpackagerefp = VN_CAST(cprp, ClassOrPackageRef);
if (VL_UNCOVERABLE(!cpackagerefp)) {
// Linking the extend gives an error before this is hit
nodep->v3error("Attempting to extend using non-class"); // LCOV_EXCL_LINE
return;
}
VSymEnt* const foundp = lookSymp->findIdFallback(cpackagerefp->name());
if (foundp) {
if (AstClass* const classp = VN_CAST(foundp->nodep(), Class)) {
AstPin* paramsp = cpackagerefp->paramsp();
if (paramsp) {
paramsp = paramsp->cloneTree(true);
nodep->parameterized(true);
}
nodep->childDTypep(new AstClassRefDType{nodep->fileline(), classp, paramsp});
// Link pins
iterate(nodep->childDTypep());
} else if (AstParamTypeDType* const paramp
= VN_CAST(foundp->nodep(), ParamTypeDType)) {
AstRefDType* const refParamp
= new AstRefDType{nodep->fileline(), paramp->name()};
refParamp->refDTypep(paramp);
nodep->childDTypep(refParamp);
nodep->parameterized(true);
} else {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: " << foundp->nodep()->prettyTypeName()
<< " in AstClassExtends");
return;
}
} else {
const string suggest = m_statep->suggestSymFallback(
m_curSymp, cpackagerefp->name(), LinkNodeMatcherClass{});
cpackagerefp->v3error(
"Class for '" << nodep->verilogKwd() // extends/implements
<< "' not found: " << cpackagerefp->prettyNameQ() << '\n'
<< (suggest.empty() ? "" : cpackagerefp->warnMore() + suggest));
return;
}
if (!nodep->childDTypep()) nodep->v3error("Attempting to extend using non-class");
nodep->classOrPkgsp()->unlinkFrBack()->deleteTree();
} else {
// Probably a parameter
if (AstRefDType* const refp = VN_CAST(nodep->childDTypep(), RefDType)) {
if (AstClassRefDType* classRefp = VN_CAST(refp->skipRefp(), ClassRefDType)) {
// Resolved to a class reference.
refp->replaceWith(classRefp->cloneTree(false));
} else {
// Unable to resolve the ref type to a class reference.
// Get the value of type parameter passed to the class instance,
// to print the helpful error message.
const AstNodeDType* typep = refp->refDTypep();
if (const AstParamTypeDType* const paramp = VN_CAST(typep, ParamTypeDType)) {
typep = paramp->subDTypep();
}
typep->v3error("Attempting to extend using non-class");
}
}
}
}
void visit(AstClass* nodep) override {
if (nodep->user3SetOnce()) return;
UINFO(5, " " << nodep << endl);
@ -3293,110 +3405,50 @@ private:
cextp->v3error("Multiple inheritance illegal on non-interface classes"
" (IEEE 1800-2017 8.13)");
}
if (cextp->childDTypep() || cextp->dtypep()) {
iterate(cextp);
if (m_statep->forPrimary()) {
if (cextp->parameterized()) {
// Parameters in extends statement.
// The class can't be resolved in the current pass.
m_extendsParam.insert(nodep);
m_insideClassExtParam = true;
}
if (AstClassRefDType* const classRefp
= VN_CAST(cextp->childDTypep(), ClassRefDType)) {
AstClass* const classp = classRefp->classp();
if (classp != nodep) iterate(classp);
if (m_extendsParam.find(classp) != m_extendsParam.end()) {
// One of its super classes has parameters in extends statement.
// Some links may not be resolved in the first pass.
m_extendsParam.insert(nodep);
m_insideClassExtParam = true;
}
}
}
if (AstClass* const classp = cextp->classOrNullp()) {
// Already converted. Update symbol table to link unlinked members.
// Base class has to be visited in a case if its extends statement
// needs to be handled. Recursive inheritance was already checked.
iterate(cextp->classp());
if (classp == nodep) {
cextp->v3error("Attempting to extend class " << nodep->prettyNameQ()
<< " from itself");
} else if (cextp->isImplements() && !classp->isInterfaceClass()) {
cextp->v3error("Attempting to implement from non-interface class "
<< classp->prettyNameQ() << '\n'
<< "... Suggest use 'extends'");
} else if (!cextp->isImplements() && !nodep->isInterfaceClass()
&& classp->isInterfaceClass()) {
cextp->v3error("Attempting to extend from interface class "
<< classp->prettyNameQ() << '\n'
<< "... Suggest use 'implements'");
}
classp->isExtended(true);
nodep->isExtended(true);
iterate(classp);
importSymbolsFromExtended(nodep, cextp);
continue;
}
AstNode* cprp = cextp->classOrPkgsp();
VSymEnt* lookSymp = m_curSymp;
if (AstDot* const dotp = VN_CAST(cprp, Dot)) {
dotp->user3(true);
if (AstClassOrPackageRef* lookNodep
= VN_CAST(dotp->lhsp(), ClassOrPackageRef)) {
iterate(lookNodep);
cprp = dotp->rhsp();
lookSymp = m_statep->getNodeSym(lookNodep->classOrPackagep());
} else {
dotp->lhsp()->v3error("Attempting to extend" // LCOV_EXCL_LINE
" using non-class under dot");
}
}
AstClassOrPackageRef* const cpackagerefp = VN_CAST(cprp, ClassOrPackageRef);
if (VL_UNCOVERABLE(!cpackagerefp)) {
// Linking the extend gives an error before this is hit
cextp->v3error("Attempting to extend using non-class"); // LCOV_EXCL_LINE
} else {
VSymEnt* const foundp = lookSymp->findIdFallback(cpackagerefp->name());
if (foundp) {
AstClassRefDType* classRefDtypep = nullptr;
AstClass* classp = VN_CAST(foundp->nodep(), Class);
if (classp) {
if (classp != nodep) {
// Case with recursive inheritance is handled later in this
// function
iterate(classp);
}
if (m_statep->forPrimary()
&& m_extendsParam.find(classp) != m_extendsParam.end()) {
// Has a parameter as its base class
m_extendsParam.insert(nodep);
m_insideClassExtParam = true;
}
AstPin* paramsp = cpackagerefp->paramsp();
if (paramsp) paramsp = paramsp->cloneTree(true);
classRefDtypep
= new AstClassRefDType{nodep->fileline(), classp, paramsp};
} else if (AstParamTypeDType* const paramp
= VN_CAST(foundp->nodep(), ParamTypeDType)) {
if (m_statep->forPrimary()) {
// Extending has to be handled after V3Param.cpp
m_extendsParam.insert(nodep);
m_insideClassExtParam = true;
continue;
} else {
AstNodeDType* const paramTypep = paramp->subDTypep();
classRefDtypep
= VN_CAST(paramTypep->cloneTree(false), ClassRefDType);
if (!classRefDtypep) {
paramTypep->v3error("Attempting to extend using non-class");
} else {
classp = classRefDtypep->classp();
}
}
} else {
cextp->v3warn(E_UNSUPPORTED,
"Unsupported: " << foundp->nodep()->prettyTypeName()
<< " in AstClassExtends");
}
if (classp) {
UINFO(8, "Import to " << nodep << " from export class " << classp
<< endl);
if (classp == nodep) {
cextp->v3error("Attempting to extend class "
<< nodep->prettyNameQ() << " from itself");
} else if (cextp->isImplements() && !classp->isInterfaceClass()) {
cextp->v3error("Attempting to implement from non-interface class "
<< classp->prettyNameQ() << '\n'
<< "... Suggest use 'extends'");
} else if (!cextp->isImplements() && !nodep->isInterfaceClass()
&& classp->isInterfaceClass()) {
cextp->v3error("Attempting to extend from interface class "
<< classp->prettyNameQ() << '\n'
<< "... Suggest use 'implements'");
} else {
cextp->childDTypep(classRefDtypep);
classp->isExtended(true);
nodep->isExtended(true);
importSymbolsFromExtended(nodep, cextp);
VL_DO_DANGLING(cextp->classOrPkgsp()->unlinkFrBack()->deleteTree(),
cpackagerefp);
}
}
} else {
const string suggest = m_statep->suggestSymFallback(
m_curSymp, cpackagerefp->name(), LinkNodeMatcherClass{});
cpackagerefp->v3error(
"Class for '"
<< cextp->verilogKwd() // extends/implements
<< "' not found: " << cpackagerefp->prettyNameQ() << '\n'
<< (suggest.empty() ? "" : cpackagerefp->warnMore() + suggest));
}
}
}
m_ds.m_dotSymp = m_curSymp;
@ -3424,6 +3476,12 @@ private:
if (nodep->user3SetOnce()) return;
if (AstNode* const cpackagep = nodep->classOrPackageOpp()) {
if (AstClassOrPackageRef* const cpackagerefp = VN_CAST(cpackagep, ClassOrPackageRef)) {
if (cpackagerefp->paramsp()) {
// Unable to link before the instantiation of parameter classes.
// The class reference node has to be visited to properly link parameters.
iterate(cpackagep);
return;
}
nodep->classOrPackagep(cpackagerefp->classOrPackagep());
if (!VN_IS(nodep->classOrPackagep(), Class)
&& !VN_IS(nodep->classOrPackagep(), Package)) {
@ -3483,6 +3541,8 @@ private:
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
return;
} else if (m_insideClassExtParam) {
return;
} else {
if (foundp) UINFO(1, "Found sym node: " << foundp->nodep() << endl);
nodep->v3error("Can't find typedef: " << nodep->prettyNameQ());
@ -3555,18 +3615,18 @@ public:
// Link class functions
void V3LinkDot::linkDotGuts(AstNetlist* rootp, VLinkDotStep step) {
if (debug() >= 5 || dumpTree() >= 9) {
if (debug() >= 5 || dumpTreeLevel() >= 9) {
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot.tree"));
}
LinkDotState state(rootp, step);
const LinkDotFindVisitor visitor{rootp, &state};
if (debug() >= 5 || dumpTree() >= 9) {
if (debug() >= 5 || dumpTreeLevel() >= 9) {
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-find.tree"));
}
if (step == LDS_PRIMARY || step == LDS_PARAMED) {
// Initial link stage, resolve parameters
const LinkDotParamVisitor visitors{rootp, &state};
if (debug() >= 5 || dumpTree() >= 9) {
if (debug() >= 5 || dumpTreeLevel() >= 9) {
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-param.tree"));
}
} else if (step == LDS_ARRAYED) {
@ -3575,7 +3635,7 @@ void V3LinkDot::linkDotGuts(AstNetlist* rootp, VLinkDotStep step) {
// process AstScope's. This needs to be separate pass after whole hierarchy graph created.
const LinkDotScopeVisitor visitors{rootp, &state};
v3Global.assertScoped(true);
if (debug() >= 5 || dumpTree() >= 9) {
if (debug() >= 5 || dumpTreeLevel() >= 9) {
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-scoped.tree"));
}
} else {
@ -3592,23 +3652,23 @@ void V3LinkDot::linkDotGuts(AstNetlist* rootp, VLinkDotStep step) {
void V3LinkDot::linkDotPrimary(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
linkDotGuts(nodep, LDS_PRIMARY);
V3Global::dumpCheckGlobalTree("linkdot", 0, dumpTree() >= 6);
V3Global::dumpCheckGlobalTree("linkdot", 0, dumpTreeLevel() >= 6);
}
void V3LinkDot::linkDotParamed(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
linkDotGuts(nodep, LDS_PARAMED);
V3Global::dumpCheckGlobalTree("linkdotparam", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("linkdotparam", 0, dumpTreeLevel() >= 3);
}
void V3LinkDot::linkDotArrayed(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
linkDotGuts(nodep, LDS_ARRAYED);
V3Global::dumpCheckGlobalTree("linkdot", 0, dumpTree() >= 6);
V3Global::dumpCheckGlobalTree("linkdot", 0, dumpTreeLevel() >= 6);
}
void V3LinkDot::linkDotScope(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
linkDotGuts(nodep, LDS_SCOPED);
V3Global::dumpCheckGlobalTree("linkdot", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("linkdot", 0, dumpTreeLevel() >= 3);
}

View File

@ -69,7 +69,6 @@ private:
// METHODS
void insertBeforeStmt(AstNode* nodep, AstNode* newp) {
// Return node that must be visited, if any
// See also AstNode::addBeforeStmt; this predates that function
if (debug() >= 9) newp->dumpTree("- newstmt: ");
UASSERT_OBJ(m_insStmtp, nodep, "Function not underneath a statement");
if (m_insMode == IM_BEFORE) {
@ -310,5 +309,5 @@ public:
void V3LinkInc::linkIncrements(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ LinkIncVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("linkinc", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("linkinc", 0, dumpTreeLevel() >= 3);
}

View File

@ -351,5 +351,5 @@ public:
void V3LinkJump::linkJump(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ LinkJumpVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("linkjump", 0, dumpTree() >= 3);
V3Global::dumpCheckGlobalTree("linkjump", 0, dumpTreeLevel() >= 3);
}

View File

@ -319,7 +319,7 @@ public:
void V3LinkLValue::linkLValue(AstNetlist* nodep) {
UINFO(4, __FUNCTION__ << ": " << endl);
{ LinkLValueVisitor{nodep, VAccess::NOCHANGE}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("linklvalue", 0, dumpTree() >= 6);
V3Global::dumpCheckGlobalTree("linklvalue", 0, dumpTreeLevel() >= 6);
}
void V3LinkLValue::linkLValueSet(AstNode* nodep) {
// Called by later link functions when it is known a node needs

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