Merge from master for release.
This commit is contained in:
commit
4f13c4d1bb
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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 }}
|
||||
|
|
@ -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
64
Changes
|
|
@ -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
|
||||
==========================
|
||||
|
||||
|
|
|
|||
10
Makefile.in
10
Makefile.in
|
|
@ -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
|
||||
|
|
|
|||
15
README.rst
15
README.rst
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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}};
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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():
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@
|
|||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Error.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
29
src/V3Ast.h
29
src/V3Ast.h
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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(); }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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)(");
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
<< "\""
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, "")
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
|
|
|||
103
src/V3Expand.cpp
103
src/V3Expand.cpp
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -727,5 +727,5 @@ void V3Inline::inlineAll(AstNetlist* nodep) {
|
|||
}
|
||||
|
||||
{ InlineIntfRefVisitor{nodep}; }
|
||||
V3Global::dumpCheckGlobalTree("inline", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("inline", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue