Merge from master for release.
This commit is contained in:
commit
e6b0bdd4df
|
|
@ -36,9 +36,9 @@ jobs:
|
|||
m32: [0, 1]
|
||||
exclude:
|
||||
# Build pull requests only with ubuntu-22.04 and without m32
|
||||
- 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' }}
|
||||
# - 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 clang++
|
||||
- {os: ubuntu-18.04, m32: 1}
|
||||
- {os: ubuntu-20.04, m32: 1}
|
||||
|
|
@ -107,9 +107,9 @@ jobs:
|
|||
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, vltmt-0, vltmt-1]
|
||||
exclude:
|
||||
# Build pull requests only with ubuntu-22.04 and without m32
|
||||
- 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' }}
|
||||
# - 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
|
||||
- {os: ubuntu-18.04, m32: 1}
|
||||
- {os: ubuntu-20.04, m32: 1}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
cmake_minimum_required(VERSION 3.15)
|
||||
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
|
||||
project(Verilator
|
||||
VERSION 5.012
|
||||
VERSION 5.014
|
||||
HOMEPAGE_URL https://verilator.org
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
|
|
|||
49
Changes
49
Changes
|
|
@ -8,6 +8,55 @@ The changes in each Verilator version are described below. The
|
|||
contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
|
||||
Verilator 5.014 2023-08-06
|
||||
==========================
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Deprecation planned for 32-bit pointer -m32 mode (#4268).
|
||||
* Deprecate CMake config below version 3.13 (#4389) (#4390). [Vito Gamberini]
|
||||
* Support some stream operations on queues (#4292). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support property declaration with empty parentheses (#4313) (#4317). [Anthony Donlon]
|
||||
* Support locator methods with "with" on assoc arrays (#4335). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support string replication with variable (#4341). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Support more types in wait (#4374). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Support static method calls as default values of function arguments (#4378). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Add GENUNNAMED lint warning. [Srinivasan Venkataramanan, Deepa Palaniappan]
|
||||
* Add MISINDENT lint warning for misleading indentation.
|
||||
* Fix 'VlForkSync' redeclaration (#4277). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Fix processes that can outlive their parents (#4253). [Krzysztof Boronski, Antmicro Ltd]
|
||||
* Fix duplicate fork names (#4295). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix splitting coroutines (#4297) (#4307). [Jiamin Zhu]
|
||||
* Fix error when multiple duplicate DPI exports (#4301).
|
||||
* Fix class reference assignment checking (#4296). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix handling of ref types in initial values of type parameters (#4304). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix comparison of string parameters (#4308). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix state update for always processes (#4311). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix multiple edge timing controls in class methods (#4318) (#4320) (#4344). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Fix implicit calls of base class constructors with optional arguments (#4319). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix propagation of process requirement (#4321). [Krzysztof Boroński]
|
||||
* Fix unhandled overloads in V3InstrCount (#4324). [Krzysztof Boroński]
|
||||
* Fix selects of static members (#4326). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix references to members of results of static methods (#4327). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix unique..with method on queues of class objects (#4328). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix queue slicing (#4329). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix wildcard referring types (#4336) (#4342). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix comparison of class objects (#4346). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix unexpected RefDType on assoc arrays (#4337). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix cmake astgen for Rocky Linux 8.7 (#4343). [Julian Daube]
|
||||
* Fix class timescale in class packages (#4348). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Fix string concatenations (#4354). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix unlinked task error from broken context (#4355) (#4402). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix selects on unpacked structs (#4359). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix select operation on assoc array with wide keys (#4360). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix non-public methods with wide output (#4364). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix handling of super.new calls (#4366). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix assign to input var in methods (#4367). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix VlProcess not found (#4368). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix order of evaluation of function calls in statements (#4375). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix config_build.h issues (#4380) (#4381). [Andrew Miloradovsky]
|
||||
|
||||
|
||||
Verilator 5.012 2023-06-13
|
||||
==========================
|
||||
|
||||
|
|
|
|||
|
|
@ -449,8 +449,8 @@ install-msg:
|
|||
|
||||
IN_WILD := ${srcdir}/*.in ${srcdir}/*/*.in
|
||||
|
||||
# autoheader might not change config_build.h.in, so touch it
|
||||
${srcdir}/config_build.h: ${srcdir}/config_build.h.in configure
|
||||
# autoheader might not change config_package.h.in, so touch it
|
||||
${srcdir}/config_package.h: ${srcdir}/config_package.h.in configure
|
||||
cd ${srcdir} && autoheader
|
||||
touch $@
|
||||
Makefile: Makefile.in config.status $(IN_WILD)
|
||||
|
|
|
|||
|
|
@ -1,20 +1,22 @@
|
|||
.. Github doesn't render images unless absolute URL
|
||||
.. Do not know of a conditional tag, "only: github" nor "github display" works
|
||||
|
||||
.. image:: https://img.shields.io/badge/Website-Verilator.org-181717.svg
|
||||
:target: https://verilator.org
|
||||
.. image:: https://img.shields.io/badge/License-LGPL%20v3-blue.svg
|
||||
:target: https://www.gnu.org/licenses/lgpl-3.0]
|
||||
:target: https://www.gnu.org/licenses/lgpl-3.0
|
||||
.. image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg
|
||||
:target: https://opensource.org/licenses/Artistic-2.0
|
||||
.. image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages
|
||||
:target: https://repology.org/project/verilator/versions
|
||||
.. image:: https://img.shields.io/docker/pulls/verilator/verilator
|
||||
:target: https://hub.docker.com/r/verilator/verilator
|
||||
.. image:: https://api.codacy.com/project/badge/Grade/fa78caa433c84a4ab9049c43e9debc6f
|
||||
:target: https://www.codacy.com/gh/verilator/verilator
|
||||
.. image:: https://codecov.io/gh/verilator/verilator/branch/master/graph/badge.svg
|
||||
: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
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@
|
|||
# Then 'make maintainer-dist'
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[5.012 2023-06-13],
|
||||
AC_INIT([Verilator],[5.014 2023-08-06],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
|
||||
AC_CONFIG_HEADERS(src/config_build.h)
|
||||
AC_CONFIG_HEADERS(src/config_package.h)
|
||||
AC_CONFIG_FILES(Makefile src/Makefile src/Makefile_obj include/verilated.mk include/verilated_config.h verilator.pc verilator-config.cmake verilator-config-version.cmake)
|
||||
|
||||
# Version
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@ Aliaksei Chapyzhenka
|
|||
Ameya Vikram Singh
|
||||
Andreas Kuster
|
||||
Andrei Kostovski
|
||||
Andrew Miloradovsky
|
||||
Andrew Nolte
|
||||
Anthony Donlon
|
||||
Arkadiusz Kozdra
|
||||
Aylon Chaim Porat
|
||||
Cameron Kirk
|
||||
|
|
@ -38,6 +40,7 @@ Fan Shupei
|
|||
february cozzocrea
|
||||
Felix Neumärker
|
||||
Felix Yan
|
||||
G-A. Kamendje
|
||||
Garrett Smith
|
||||
Geza Lore
|
||||
Gianfranco Costamagna
|
||||
|
|
@ -46,7 +49,6 @@ Gökçe Aydos
|
|||
Graham Rushton
|
||||
Guokai Chen
|
||||
Gustav Svensk
|
||||
G-A. Kamendje
|
||||
Harald Heckmann
|
||||
Hennadii Chernyshchyk
|
||||
Howard Su
|
||||
|
|
@ -54,9 +56,9 @@ Huang Rui
|
|||
Huanghuang Zhou
|
||||
HungMingWu
|
||||
HyungKi Jeong
|
||||
Ilya Barkov
|
||||
Iru Cai
|
||||
Ivan Vnučec
|
||||
Ilya Barkov
|
||||
Iztok Jeras
|
||||
Jake Merdich
|
||||
James Hanlon
|
||||
|
|
@ -71,17 +73,18 @@ Jeremy Bennett
|
|||
Jesse Taube
|
||||
Jevin Sweval
|
||||
Jiacheng Qian
|
||||
Jiamin Zhu
|
||||
Jiuyang Liu
|
||||
Joey Liu
|
||||
John Coiner
|
||||
John Demme
|
||||
John Wehle
|
||||
Jiamin Zhu
|
||||
Jonathan Drolet
|
||||
Jose Loyola
|
||||
Joseph Nwabueze
|
||||
Josep Sans
|
||||
Joseph Nwabueze
|
||||
Josh Redford
|
||||
Julian Daube
|
||||
Julie Schwartz
|
||||
Julien Margetts
|
||||
Kaleb Barrett
|
||||
|
|
@ -91,6 +94,7 @@ Keith Colbert
|
|||
Kevin Kiningham
|
||||
Kritik Bhimani
|
||||
Krzysztof Bieganski
|
||||
Krzysztof Boronski
|
||||
Krzysztof Boroński
|
||||
Kuba Ober
|
||||
Larry Doolittle
|
||||
|
|
@ -162,15 +166,17 @@ Varun Koyyalagunta
|
|||
Vassilis Papaefstathiou
|
||||
Veripool API Bot
|
||||
Victor Besyakov
|
||||
Vito Gamberini
|
||||
William D. Jones
|
||||
Wilson Snyder
|
||||
Xi Zhang
|
||||
Yinan Xu
|
||||
Yoda Lee
|
||||
Yossi Nivin
|
||||
Yu-Sheng Lin
|
||||
Yuri Victorovich
|
||||
Yutetsu TAKATSUKASA
|
||||
Yu-Sheng Lin
|
||||
Yves Mathieu
|
||||
Zhanglei Wang
|
||||
Zixi Li
|
||||
أحمد المحمودي
|
||||
|
|
|
|||
|
|
@ -18,12 +18,16 @@
|
|||
RST2HTML = rst2html
|
||||
PYTHON3 = python3
|
||||
DOXYGEN = doxygen
|
||||
SPHINXOPTS ?= -c guide
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
|
||||
SOURCEDIR = guide
|
||||
BUILDDIR = _build
|
||||
|
||||
SPHINXOPTS ?= -c guide
|
||||
ifneq ($(VERILATOR_ANALYTICS_ID),)
|
||||
SPHINXOPTS += -D html_theme_options.analytics_id=$(VERILATOR_ANALYTICS_ID)
|
||||
endif
|
||||
|
||||
######################################################################
|
||||
|
||||
.SUFFIXES:
|
||||
|
|
|
|||
|
|
@ -115,7 +115,6 @@ html_theme = 'sphinx_rtd_theme'
|
|||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
||||
|
||||
html_theme_options = {
|
||||
'analytics_id': 'G-D27B0C9QEB',
|
||||
'logo_only': True,
|
||||
'style_nav_header_background': "#45acf8", # Default is #2980B9
|
||||
# 'canonical_url':
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ C++11 compiler support
|
|||
Verilator will require C++20 or newer compilers for both compiling
|
||||
Verilator and compiling all Verilated models no sooner than January 2025.
|
||||
|
||||
Verilated_heavy.h
|
||||
The legacy "verilated_heavy.h" include was replaced with just including
|
||||
"verilated.h". Verilated_heavy.h is planned for removal no sooner than
|
||||
July 2022.
|
||||
32-bit compiler support
|
||||
Verilator currently regresses both 64-bit and 32-bit pointer modes (GCC's
|
||||
`-m64` and `-m32`). Support for 32-bit `-m32` mode will be deprecated no
|
||||
sooner than January 2024.
|
||||
|
||||
Option `-O<letter>`
|
||||
The debug `-O<letter>` options have been replaced with
|
||||
|
|
|
|||
|
|
@ -1539,7 +1539,7 @@ Summary:
|
|||
|
||||
Disable all code style related warning messages (note that by default, they are
|
||||
already disabled). This is equivalent to ``-Wno-DECLFILENAME -Wno-DEFPARAM
|
||||
-Wno-EOFNEWLINE -Wno-IMPORTSTAR -Wno-INCABSPATH -Wno-PINCONNECTEMPTY
|
||||
-Wno-EOFNEWLINE -Wno-GENUNNAMED -Wno-IMPORTSTAR -Wno-INCABSPATH -Wno-PINCONNECTEMPTY
|
||||
-Wno-PINNOCONNECT -Wno-SYNCASYNCNET -Wno-UNDRIVEN
|
||||
-Wno-UNUSEDGENVAR -Wno-UNUSEDPARAM -Wno-UNUSEDSIGNAL
|
||||
-Wno-VARHIDDEN``.
|
||||
|
|
@ -1562,16 +1562,17 @@ Summary:
|
|||
|
||||
Enable all lint-related warning messages (note that by default, they are already
|
||||
enabled), but do not affect style messages. This is equivalent to
|
||||
``-Wwarn-ALWCOMBORDER -Wwarn-BSSPACE -Wwarn-CASEINCOMPLETE
|
||||
-Wwarn-CASEOVERLAP -Wwarn-CASEX -Wwarn-CASTCONST -Wwarn-CASEWITHX -Wwarn-CMPCONST
|
||||
-Wwarn-COLONPLUS -Wwarn-IMPLICIT -Wwarn-ASCRANGE
|
||||
-Wwarn-PINMISSING -Wwarn-REALCVT -Wwarn-UNSIGNED -Wwarn-WIDTH``.
|
||||
``-Wwarn-ALWCOMBORDER -Wwarn-ASCRANGE -Wwarn-BSSPACE -Wwarn-CASEINCOMPLETE
|
||||
-Wwarn-CASEOVERLAP -Wwarn-CASEWITHX -Wwarn-CASEX -Wwarn-CASTCONST -Wwarn-CMPCONST
|
||||
-Wwarn-COLONPLUS -Wwarn-IMPLICIT -Wwarn-IMPLICITSTATIC -Wwarn-LATCH -Wwarn-MISINDENT
|
||||
-Wwarn-NEWERSTD -Wwarn-PINMISSING -Wwarn-REALCVT -Wwarn-STATICVAR -Wwarn-UNSIGNED
|
||||
-Wwarn-WIDTHTRUNC -Wwarn-WIDTHEXPAND -Wwarn-WIDTHXZEXPAND``.
|
||||
|
||||
.. option:: -Wwarn-style
|
||||
|
||||
Enable all code style-related warning messages. This is equivalent to
|
||||
``-Wwarn ASSIGNDLY -Wwarn-DECLFILENAME -Wwarn-DEFPARAM -Wwarn-EOFNEWLINE
|
||||
-Wwarn-INCABSPATH -Wwarn-PINNOCONNECT -Wwarn-SYNCASYNCNET -Wwarn-UNDRIVEN
|
||||
-Wwarn-GENUNNAMED -Wwarn-INCABSPATH -Wwarn-PINNOCONNECT -Wwarn-SYNCASYNCNET -Wwarn-UNDRIVEN
|
||||
-Wwarn-UNUSEDGENVAR -Wwarn-UNUSEDPARAM -Wwarn-UNUSEDSIGNAL -Wwarn-VARHIDDEN``.
|
||||
|
||||
.. option:: --x-assign 0
|
||||
|
|
|
|||
|
|
@ -486,7 +486,7 @@ From the sc_main.cpp file, you'd then:
|
|||
|
||||
#include "Vour.h"
|
||||
#include "Vour_our.h"
|
||||
cout << "clock is " << top->our->clk << endl;
|
||||
std::cout << "clock is " << top->our->clk << std::endl;
|
||||
|
||||
|
||||
In this example, clk is a bool you can read or set as any other variable.
|
||||
|
|
|
|||
|
|
@ -500,7 +500,7 @@ feedback-directed optimization. See the appropriate compiler
|
|||
documentation.
|
||||
|
||||
|
||||
.. _Runtime Debugging
|
||||
.. _Runtime Debugging:
|
||||
|
||||
Runtime Debugging
|
||||
=================
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ compiled on the different target system.
|
|||
|
||||
To support this, none of the files that Verilator produces will reference
|
||||
any configure-generated build-system-specific files, such as
|
||||
:file:`config.h` (which is renamed in Verilator to :file:`config_build.h`
|
||||
:file:`config.h` (which is renamed in Verilator to :file:`config_package.h`
|
||||
to reduce confusion.) The disadvantage of this approach is that
|
||||
:file:`include/verilatedos.h` must self-detect the requirements of the
|
||||
target system, rather than using configure.
|
||||
|
|
|
|||
|
|
@ -670,6 +670,52 @@ List Of Warnings
|
|||
used as a clock.
|
||||
|
||||
|
||||
.. option:: GENUNNAMED
|
||||
|
||||
Warns that a generate block was unnamed and "genblk" will be used per
|
||||
IEEE.
|
||||
|
||||
The potential issue is that adding additional generate blocks will
|
||||
renumber the assigned names, which may cause eventual problems with
|
||||
synthesis constraints or other tools that depend on hierarchical paths
|
||||
remaining consistent.
|
||||
|
||||
Blocks that are empty may not be reported with this warning, as no
|
||||
scopes are created for empty blocks, so there is no harm in having them
|
||||
unnamed.
|
||||
|
||||
Disabled by default as this is a code-style warning; it will simulate
|
||||
correctly.
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 2
|
||||
|
||||
generate
|
||||
if (PARAM == 1) begin //<--- Warning
|
||||
end
|
||||
|
||||
Results in:
|
||||
|
||||
.. code-block::
|
||||
|
||||
%Warning-GENUNNAMED: example.v:2:9: Unnamed generate block (IEEE 1800-2017 27.6)
|
||||
|
||||
To fix this assign a label (often with the naming convention prefix of
|
||||
:code:`gen_` or :code:`g_`), for example:
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 2
|
||||
|
||||
generate
|
||||
if (PARAM == 1) begin : gen_param_1 //<--- Repaired
|
||||
end
|
||||
|
||||
Other tools with similar warnings: Verible's generate-label, "All
|
||||
generate block statements must have a label."
|
||||
|
||||
|
||||
.. option:: HIERBLOCK
|
||||
|
||||
Warns that the top module is marked as a hierarchy block by the
|
||||
|
|
@ -876,6 +922,102 @@ List Of Warnings
|
|||
Ignoring this warning will only suppress the lint check; it will
|
||||
simulate correctly.
|
||||
|
||||
.. option:: LIFETIME
|
||||
|
||||
Error when a variable is referenced in a process that can outlive the process
|
||||
in which it was declared. This can happen when using 'fork..join_none' or
|
||||
'fork..join_any' blocks, which spawn process that can outlive their parents.
|
||||
This error occurs only when Verilator can't replace the reference with a
|
||||
reference to copy of this variable, local to the forked process. For example:
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 3
|
||||
|
||||
task foo(int local_var);
|
||||
fork
|
||||
#10 local_var++;
|
||||
#20 $display("local_var = %d", local_var);
|
||||
join_none
|
||||
endtask
|
||||
|
||||
In the example above 'local_var' exists only within scope of 'foo', once foo
|
||||
finishes, the stack frame containing 'i' gets removed. However, the process
|
||||
forked from foo continues, as it contains a delay. After 10 units of time
|
||||
pass, this process attempts to modify 'local_var'. However, this variable no
|
||||
longer exits. It can't be made local to the forked process upon spawning, because
|
||||
it's modified and can be referenced somewhere else, for example in the other
|
||||
forked process, that was delayed by 20 units of time in this example. Thus,
|
||||
there's no viable stack allocation for it.
|
||||
|
||||
In order to fix it, if the intent is not to share the variable's state outside
|
||||
of the process, then create a local copy of the variable.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 4
|
||||
|
||||
task foo(int local_var);
|
||||
fork
|
||||
#10 begin
|
||||
int forked_var = local_var;
|
||||
forked_var++;
|
||||
end
|
||||
#20 begin
|
||||
// Note that we are going to print the original value here,
|
||||
// as `forked_var`is a local copy that was initialized while
|
||||
// `foo` was still alive.
|
||||
int forked_var = local_var;
|
||||
$display("forked_var = %d", forked_var)
|
||||
end
|
||||
join_none
|
||||
endtask
|
||||
|
||||
If you need to share its state, another strategy is to ensure it's allocated
|
||||
statically:
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 1
|
||||
|
||||
int static_var;
|
||||
|
||||
task foo();
|
||||
fork
|
||||
#10 static_var++;
|
||||
#20 $display("static_var = %d", static_var);
|
||||
join_none
|
||||
endtask
|
||||
|
||||
However, if you need to be able to instantiate at runtime, the solution would be to
|
||||
wrap it in an object, since the forked process can hold a reference to that object
|
||||
and ensure that the variable stays alive this way:
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 2
|
||||
|
||||
class Wrapper;
|
||||
int m_var;
|
||||
|
||||
// Here we implicitly hold a reference to `this`
|
||||
task foo();
|
||||
fork
|
||||
#10 m_var++;
|
||||
#20 $display("this.m_var = %d", m_var);
|
||||
join_none
|
||||
endtask
|
||||
endclass
|
||||
|
||||
// Here we explicitly hold a handle to an object
|
||||
task bar(Wrapper wrapper);
|
||||
fork
|
||||
#10 wrapper.m_var++;
|
||||
#20 $display("wrapper.m_var = %d", wrapper.m_var);
|
||||
join_none
|
||||
endtask
|
||||
|
||||
.. option:: LITENDIAN
|
||||
|
||||
|
|
@ -897,6 +1039,51 @@ List Of Warnings
|
|||
unsupported. Verilator uses only the typical delay value.
|
||||
|
||||
|
||||
.. option:: MISINDENT
|
||||
|
||||
Warns that the indentation of a statement is misleading, suggesting the
|
||||
statement is part of a previous :code:`if` or :code:`while` block while
|
||||
it is not.
|
||||
|
||||
Verilator suppresses this check when there is an inconsistent mix of
|
||||
spaces and tabs, as it cannot ensure the width of tabs. Verilator also
|
||||
ignores blocks with :code:`begin`/:code:`end`, as the :code:`end`
|
||||
visually indicates the earlier statement's end.
|
||||
|
||||
Ignoring this warning will only suppress the lint check; it will
|
||||
simulate correctly.
|
||||
|
||||
For example
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 3
|
||||
|
||||
if (something)
|
||||
statement_in_if;
|
||||
statement_not_in_if; //<--- Warning
|
||||
|
||||
Results in:
|
||||
|
||||
.. code-block::
|
||||
|
||||
%Warning-MISINDENT: example.v:3:9: Misleading indentation
|
||||
|
||||
To fix this repair the indentation to match the correct earlier
|
||||
statement, for example:
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 3
|
||||
|
||||
if (something)
|
||||
statement_in_if;
|
||||
statement_not_in_if; //<--- Repaired
|
||||
|
||||
Other tools with similar warnings: GCC -Wmisleading-indentation,
|
||||
clang-tidy readability-misleading-indentation.
|
||||
|
||||
|
||||
.. option:: MODDUP
|
||||
|
||||
.. TODO better example
|
||||
|
|
|
|||
|
|
@ -2123,7 +2123,7 @@ Or in a hand-written C++ wrapper:
|
|||
::
|
||||
|
||||
#ifdef TEST_VERBOSE
|
||||
cout << "Read a=" << a << endl;
|
||||
std::cout << "Read a=" << a << std::endl;
|
||||
#endif
|
||||
|
||||
A filename that should be used to check the output results is given with
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ Das
|
|||
Dcache
|
||||
Deadman
|
||||
Debacker
|
||||
Deepa
|
||||
Defparams
|
||||
Denio
|
||||
Deprecations
|
||||
|
|
@ -246,6 +247,7 @@ Olof
|
|||
Olofsson
|
||||
Oyvind
|
||||
PLI
|
||||
Palaniappan
|
||||
Patricio
|
||||
Petr
|
||||
Piechotka
|
||||
|
|
@ -376,6 +378,7 @@ Wfuture
|
|||
Whatson
|
||||
Wildman
|
||||
Wim
|
||||
Wmisleading
|
||||
Wno
|
||||
Wojciech
|
||||
Wolfel
|
||||
|
|
@ -410,6 +413,7 @@ architected
|
|||
args
|
||||
arrarys
|
||||
assertOn
|
||||
astgen
|
||||
async
|
||||
ato
|
||||
atoi
|
||||
|
|
@ -905,6 +909,7 @@ undef
|
|||
undefineall
|
||||
undriven
|
||||
ungetc
|
||||
unhandled
|
||||
uniquified
|
||||
unistd
|
||||
unlink
|
||||
|
|
@ -971,4 +976,3 @@ ypq
|
|||
yurivict
|
||||
zdave
|
||||
Øyvind
|
||||
|
||||
|
|
|
|||
|
|
@ -1896,6 +1896,12 @@ std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_PURE {
|
|||
return std::string{destout, len};
|
||||
}
|
||||
|
||||
std::string VL_CVT_PACK_STR_ND(const VlQueue<std::string>& q) VL_PURE {
|
||||
std::string output;
|
||||
for (const std::string& s : q) output += s;
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE {
|
||||
std::string lstring = lhs;
|
||||
const int32_t rhs_s = rhs; // To signed value
|
||||
|
|
|
|||
|
|
@ -210,8 +210,8 @@ private:
|
|||
|
||||
std::string result = prefix + "*" + suffix;
|
||||
|
||||
// cout << "\nch pre="<<prefix<<" s="<<suffix<<"\nch a="<<old<<"\nch b="<<add
|
||||
// <<"\ncho="<<result<<endl;
|
||||
// std::cout << "\nch pre=" << prefix << " s=" << suffix << "\nch a="
|
||||
// << old << "\nch b=" << add << "\ncho=" << result << std::endl;
|
||||
return result;
|
||||
}
|
||||
bool itemMatchesString(VerilatedCovImpItem* itemp, const std::string& match)
|
||||
|
|
@ -316,7 +316,7 @@ public:
|
|||
valps[2] = page_default.c_str();
|
||||
|
||||
// Keys -> strings
|
||||
std::string keys[VerilatedCovConst::MAX_KEYS];
|
||||
std::array<std::string, VerilatedCovConst::MAX_KEYS> keys;
|
||||
for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
|
||||
if (ckeyps[i] && ckeyps[i][0]) keys[i] = ckeyps[i];
|
||||
}
|
||||
|
|
@ -337,7 +337,7 @@ public:
|
|||
const std::string key = keys[i];
|
||||
if (!keys[i].empty()) {
|
||||
const std::string val = valps[i];
|
||||
// cout<<" "<<__FUNCTION__<<" "<<key<<" = "<<val<<endl;
|
||||
// std::cout << " " << __FUNCTION__ << " " << key << " = " << val << std::endl;
|
||||
m_insertp->m_keys[addKeynum] = valueIndex(key);
|
||||
m_insertp->m_vals[addKeynum] = valueIndex(val);
|
||||
++addKeynum;
|
||||
|
|
|
|||
|
|
@ -2196,6 +2196,7 @@ extern IData VL_DIST_UNIFORM(IData& seedr, IData ustart, IData uend) VL_MT_SAFE;
|
|||
// Conversion functions
|
||||
|
||||
extern std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_PURE;
|
||||
extern std::string VL_CVT_PACK_STR_ND(const VlQueue<std::string>& q) VL_PURE;
|
||||
inline std::string VL_CVT_PACK_STR_NQ(QData lhs) VL_PURE {
|
||||
VlWide<VL_WQ_WORDS_E> lw;
|
||||
VL_SET_WQ(lw, lhs);
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
// Copyright 2010-2023 by Wilson Snyder. This program is free software; you can
|
||||
// redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
///
|
||||
/// \file
|
||||
/// \brief Verilated old string and data-type header
|
||||
///
|
||||
/// This file is deprecated, and provided for backwards compatibility.
|
||||
/// Include verilated.h instead.
|
||||
///
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_VERILATED_HEAVY_H_
|
||||
#define VERILATOR_VERILATED_HEAVY_H_
|
||||
|
||||
#ifdef VL_NO_LEGACY
|
||||
#error "Include <verilated.h> instead of <verilated_heavy.h>"
|
||||
#endif
|
||||
|
||||
#include "verilated.h"
|
||||
|
||||
#endif // Guard
|
||||
|
|
@ -86,36 +86,6 @@ 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
|
||||
|
|
|
|||
|
|
@ -73,6 +73,36 @@ extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
|
|||
#define VL_OUT(name, msb, lsb) IData name ///< Declare output signal, 17-32 bits
|
||||
#define VL_OUTW(name, msb, lsb, words) VlWide<words> name ///< Declare output signal, 65+ bits
|
||||
|
||||
//===================================================================
|
||||
// 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"); }
|
||||
|
||||
//===================================================================
|
||||
// Activity trigger vector
|
||||
|
||||
|
|
@ -487,7 +517,7 @@ public:
|
|||
template <typename Func>
|
||||
VlQueue unique(Func with_func) const {
|
||||
VlQueue out;
|
||||
std::set<T_Value> saw;
|
||||
std::set<decltype(with_func(0, m_deque[0]))> saw;
|
||||
for (const auto& i : m_deque) {
|
||||
const auto i_mapped = with_func(0, i);
|
||||
const auto it = saw.find(i_mapped);
|
||||
|
|
@ -516,7 +546,7 @@ public:
|
|||
VlQueue<IData> unique_index(Func with_func) const {
|
||||
VlQueue<IData> out;
|
||||
IData index = 0;
|
||||
std::unordered_set<T_Value> saw;
|
||||
std::set<decltype(with_func(0, m_deque[0]))> saw;
|
||||
for (const auto& i : m_deque) {
|
||||
const auto i_mapped = with_func(index, i);
|
||||
auto it = saw.find(i_mapped);
|
||||
|
|
@ -830,6 +860,22 @@ public:
|
|||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> unique(Func with_func) const {
|
||||
VlQueue<T_Value> out;
|
||||
T_Key default_key;
|
||||
using WithType = decltype(with_func(m_map.begin()->first, m_map.begin()->second));
|
||||
std::set<WithType> saw;
|
||||
for (const auto& i : m_map) {
|
||||
const auto i_mapped = with_func(default_key, i.second);
|
||||
const auto it = saw.find(i_mapped);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i_mapped);
|
||||
out.push_back(i.second);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
VlQueue<T_Key> unique_index() const {
|
||||
VlQueue<T_Key> out;
|
||||
std::set<T_Key> saw;
|
||||
|
|
@ -843,6 +889,21 @@ public:
|
|||
return out;
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Key> unique_index(Func with_func) const {
|
||||
VlQueue<T_Key> out;
|
||||
using WithType = decltype(with_func(m_map.begin()->first, m_map.begin()->second));
|
||||
std::set<WithType> saw;
|
||||
for (const auto& i : m_map) {
|
||||
const auto i_mapped = with_func(i.first, i.second);
|
||||
auto it = saw.find(i_mapped);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i_mapped);
|
||||
out.push_back(i.first);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> find(Func with_func) const {
|
||||
VlQueue<T_Value> out;
|
||||
for (const auto& i : m_map)
|
||||
|
|
@ -903,6 +964,16 @@ public:
|
|||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> min(Func with_func) const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
const auto it = std::min_element(
|
||||
m_map.begin(), m_map.end(),
|
||||
[&with_func](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
|
||||
return with_func(a.first, a.second) < with_func(b.first, b.second);
|
||||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
VlQueue<T_Value> max() const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
const auto it = std::max_element(
|
||||
|
|
@ -912,6 +983,16 @@ public:
|
|||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> max(Func with_func) const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
const auto it = std::max_element(
|
||||
m_map.begin(), m_map.end(),
|
||||
[&with_func](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
|
||||
return with_func(a.first, a.second) < with_func(b.first, b.second);
|
||||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
|
||||
T_Value r_sum() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
|
|
@ -1457,8 +1538,14 @@ public:
|
|||
// For 'if (ptr)...'
|
||||
operator bool() const { return m_objp; }
|
||||
// In SV A == B iff both are handles to the same object (IEEE 1800-2017 8.4)
|
||||
bool operator==(const VlClassRef& rhs) const { return m_objp == rhs.m_objp; };
|
||||
bool operator!=(const VlClassRef& rhs) const { return m_objp != rhs.m_objp; };
|
||||
template <typename T_OtherClass>
|
||||
bool operator==(const VlClassRef<T_OtherClass>& rhs) const {
|
||||
return m_objp == rhs.m_objp;
|
||||
};
|
||||
template <typename T_OtherClass>
|
||||
bool operator!=(const VlClassRef<T_OtherClass>& rhs) const {
|
||||
return m_objp != rhs.m_objp;
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
|
|
|
|||
|
|
@ -2307,7 +2307,7 @@ PLI_INT32 vpi_flush(void) {
|
|||
VerilatedVpiImp::assertOneCheck();
|
||||
VL_VPI_ERROR_RESET_();
|
||||
Verilated::runFlushCallbacks();
|
||||
return 0;
|
||||
return 0; // Gcc coverage bug // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
PLI_INT32 vpi_mcd_flush(PLI_UINT32 mcd) {
|
||||
|
|
|
|||
|
|
@ -119,6 +119,10 @@
|
|||
// Allowed on: function, method. (-fthread-safety)
|
||||
#define VL_RETURN_CAPABILITY(x) \
|
||||
VL_CLANG_ATTR(lock_returned(x))
|
||||
// Assert that capability is already held.
|
||||
// Allowed on: function, method. (-fthread-safety)
|
||||
#define VL_ASSERT_CAPABILITY(x) \
|
||||
VL_CLANG_ATTR(assert_capability(x))
|
||||
|
||||
// Defaults for unsupported compiler features
|
||||
#ifndef VL_ATTR_ALWINLINE
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
*~
|
||||
*.old
|
||||
config_build.h
|
||||
config_package.h
|
||||
Makefile
|
||||
Makefile_obj
|
||||
.objcache*
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ set(HEADERS
|
|||
V3File.h
|
||||
V3FileLine.h
|
||||
V3Force.h
|
||||
V3Fork.h
|
||||
V3FunctionTraits.h
|
||||
V3Gate.h
|
||||
V3Global.h
|
||||
|
|
@ -227,6 +228,7 @@ set(COMMON_SOURCES
|
|||
V3File.cpp
|
||||
V3FileLine.cpp
|
||||
V3Force.cpp
|
||||
V3Fork.cpp
|
||||
V3Gate.cpp
|
||||
V3Global.cpp
|
||||
V3Graph.cpp
|
||||
|
|
@ -322,7 +324,7 @@ file(TO_NATIVE_PATH ${srcdir}/../bin/verilator_includer VERILATOR_INCLUDER)
|
|||
file(TO_NATIVE_PATH ${srcdir}/vlcovgen VLCOVGEN)
|
||||
file(TO_NATIVE_PATH ${srcdir}/config_rev CONFIG_REV)
|
||||
|
||||
configure_file(config_build.h.in config_build.h @ONLY)
|
||||
configure_file(config_package.h.in config_package.h @ONLY)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT V3Ast__gen_forward_class_decls.h V3Dfg__gen_forward_class_decls.h
|
||||
|
|
@ -414,7 +416,7 @@ foreach(astgen_name ${ASTGENERATED_NAMES})
|
|||
MAIN_DEPENDENCY ${astgen_name}.cpp
|
||||
DEPENDS ${ASTGEN} V3Ast.h
|
||||
COMMAND ${PYTHON3} ARGS
|
||||
${ASTGEN} -I"${srcdir}" --astdef V3AstNodeDType.h --astdef V3AstNodeExpr.h --astdef V3AstNodeOther.h --dfgdef V3DfgVertices.h ${astgen_name}.cpp
|
||||
${ASTGEN} -I "${srcdir}" --astdef V3AstNodeDType.h --astdef V3AstNodeExpr.h --astdef V3AstNodeOther.h --dfgdef V3DfgVertices.h ${astgen_name}.cpp
|
||||
)
|
||||
list(APPEND GENERATED_FILES ${astgen_name}__gen.cpp)
|
||||
endforeach()
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ clean mostlyclean distclean maintainer-clean::
|
|||
-rm -f .objcache*
|
||||
|
||||
distclean maintainer-clean::
|
||||
-rm -f Makefile Makefile_obj config_build.h
|
||||
-rm -f Makefile Makefile_obj config_package.h
|
||||
|
||||
maintainer-clean::
|
||||
-rm -f config_rev.h
|
||||
|
|
|
|||
|
|
@ -205,6 +205,7 @@ RAW_OBJS = \
|
|||
V3File.o \
|
||||
V3FileLine.o \
|
||||
V3Force.o \
|
||||
V3Fork.o \
|
||||
V3Gate.o \
|
||||
V3Global.o \
|
||||
V3Graph.o \
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ private:
|
|||
sentreep->unlinkFrBack();
|
||||
if (m_procedurep) {
|
||||
// To support this need queue of asserts to activate
|
||||
nodep->v3error("Unsupported: Procedural concurent assertion with"
|
||||
nodep->v3error("Unsupported: Procedural concurrent assertion with"
|
||||
" clocking event inside always (IEEE 1800-2917 16.14.6)");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,9 +125,7 @@ string AstNode::encodeNumber(int64_t num) {
|
|||
}
|
||||
}
|
||||
|
||||
string AstNode::nameProtect() const VL_MT_STABLE {
|
||||
return VIdProtect::protectIf(name(), protect());
|
||||
}
|
||||
string AstNode::nameProtect() const { return VIdProtect::protectIf(name(), protect()); }
|
||||
string AstNode::origNameProtect() const { return VIdProtect::protectIf(origName(), protect()); }
|
||||
|
||||
string AstNode::shortName() const {
|
||||
|
|
@ -285,7 +283,7 @@ string AstNode::vpiName(const string& namein) {
|
|||
return pretty;
|
||||
}
|
||||
|
||||
string AstNode::prettyTypeName() const VL_MT_STABLE {
|
||||
string AstNode::prettyTypeName() const {
|
||||
if (name() == "") return typeName();
|
||||
return std::string{typeName()} + " '" + prettyName() + "'";
|
||||
}
|
||||
|
|
@ -1081,10 +1079,12 @@ bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ig
|
|||
// private: Return true if the two trees are identical
|
||||
if (!node1p && !node2p) return true;
|
||||
if (!node1p || !node2p) return false;
|
||||
if (node1p->type() != node2p->type() || node1p->dtypep() != node2p->dtypep()
|
||||
|| !node1p->same(node2p) || (gateOnly && !node1p->isGateOptimizable())) {
|
||||
return false;
|
||||
}
|
||||
if (node1p->type() != node2p->type()) return false;
|
||||
UASSERT_OBJ(
|
||||
(!node1p->dtypep() && !node2p->dtypep()) || (node1p->dtypep() && node2p->dtypep()), node1p,
|
||||
"Comparison of a node with dtypep() with a node without dtypep()\n-node2=" << node2p);
|
||||
if (node1p->dtypep() && !node1p->dtypep()->similarDType(node2p->dtypep())) return false;
|
||||
if (!node1p->same(node2p) || (gateOnly && !node1p->isGateOptimizable())) return false;
|
||||
return (sameTreeIter(node1p->m_op1p, node2p->m_op1p, false, gateOnly)
|
||||
&& sameTreeIter(node1p->m_op2p, node2p->m_op2p, false, gateOnly)
|
||||
&& sameTreeIter(node1p->m_op3p, node2p->m_op3p, false, gateOnly)
|
||||
|
|
@ -1406,6 +1406,9 @@ AstNodeDType* AstNode::findQueueIndexDType() const {
|
|||
AstNodeDType* AstNode::findVoidDType() const {
|
||||
return v3Global.rootp()->typeTablep()->findVoidDType(fileline());
|
||||
}
|
||||
AstNodeDType* AstNode::findStreamDType() const {
|
||||
return v3Global.rootp()->typeTablep()->findStreamDType(fileline());
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// VNDeleter
|
||||
|
|
|
|||
45
src/V3Ast.h
45
src/V3Ast.h
|
|
@ -82,6 +82,17 @@ using MTaskIdSet = std::set<int>; // Set of mtaskIds for Var sorting
|
|||
// Ast<nodetypename>*' is returned.
|
||||
#define VN_AS(nodep, nodetypename) (AstNode::privateAs<Ast##nodetypename, decltype(nodep)>(nodep))
|
||||
|
||||
// Same as VN_AS, but only checks the type in debug builds. Type checking is less critical in node
|
||||
// child getters (the strongly-typed functions that wrap op*p pointers). This is because the op*p
|
||||
// pointers are usually populated by code that already asserts the correct type. Having fewer type
|
||||
// assertions yields better performance in release builds.
|
||||
#ifdef VL_DEBUG
|
||||
#define VN_DBG_AS(nodep, nodetypename) VN_AS(nodep, nodetypename)
|
||||
#else
|
||||
#define VN_DBG_AS(nodep, nodetypename) \
|
||||
(AstNode::unsafePrivateAs<Ast##nodetypename, decltype(nodep)>(nodep))
|
||||
#endif
|
||||
|
||||
// (V)erilator (N)ode deleted: Pointer to deleted AstNode (for assertions only)
|
||||
#define VN_DELETED(nodep) VL_UNLIKELY((uint64_t)(nodep) == 0x1)
|
||||
|
||||
|
|
@ -1686,7 +1697,7 @@ public:
|
|||
static constexpr int INSTR_COUNT_PLI = 20; // PLI routines
|
||||
|
||||
// ACCESSORS
|
||||
virtual string name() const VL_MT_STABLE { return ""; }
|
||||
virtual string name() const { return ""; }
|
||||
virtual string origName() const { return ""; }
|
||||
virtual void name(const string& name) {
|
||||
this->v3fatalSrc("name() called on object without name() method");
|
||||
|
|
@ -1694,7 +1705,7 @@ public:
|
|||
virtual void tag(const string& text) {}
|
||||
virtual string tag() const { return ""; }
|
||||
virtual string verilogKwd() const { return ""; }
|
||||
string nameProtect() const VL_MT_STABLE; // Name with --protect-id applied
|
||||
string nameProtect() const; // Name with --protect-id applied
|
||||
string origNameProtect() const; // origName with --protect-id applied
|
||||
string shortName() const; // Name with __PVT__ removed for concatenating scopes
|
||||
static string dedotName(const string& namein); // Name with dots removed
|
||||
|
|
@ -1707,10 +1718,10 @@ public:
|
|||
static string encodeName(const string& namein);
|
||||
static string encodeNumber(int64_t num); // Encode number into internal C representation
|
||||
static string vcdName(const string& namein); // Name for printing out to vcd files
|
||||
string prettyName() const VL_MT_STABLE { return prettyName(name()); }
|
||||
string prettyName() const { return prettyName(name()); }
|
||||
string prettyNameQ() const { return prettyNameQ(name()); }
|
||||
// "VARREF" for error messages (NOT dtype's pretty name)
|
||||
string prettyTypeName() const VL_MT_STABLE;
|
||||
string prettyTypeName() const;
|
||||
virtual string prettyOperatorName() const { return "operator " + prettyTypeName(); }
|
||||
FileLine* fileline() const VL_MT_SAFE { return m_fileline; }
|
||||
void fileline(FileLine* fl) { m_fileline = fl; }
|
||||
|
|
@ -1867,6 +1878,7 @@ public:
|
|||
void dtypeSetUInt64() { dtypep(findUInt64DType()); } // Twostate
|
||||
void dtypeSetEmptyQueue() { dtypep(findEmptyQueueDType()); }
|
||||
void dtypeSetVoid() { dtypep(findVoidDType()); }
|
||||
void dtypeSetStream() { dtypep(findStreamDType()); }
|
||||
|
||||
// Data type locators
|
||||
AstNodeDType* findBitDType() const { return findBasicDType(VBasicDTypeKwd::LOGIC); }
|
||||
|
|
@ -1878,6 +1890,7 @@ public:
|
|||
AstNodeDType* findCHandleDType() const { return findBasicDType(VBasicDTypeKwd::CHANDLE); }
|
||||
AstNodeDType* findEmptyQueueDType() const;
|
||||
AstNodeDType* findVoidDType() const;
|
||||
AstNodeDType* findStreamDType() const;
|
||||
AstNodeDType* findQueueIndexDType() const;
|
||||
AstNodeDType* findBitDType(int width, int widthMin, VSigning numeric) const;
|
||||
AstNodeDType* findLogicDType(int width, int widthMin, VSigning numeric) const;
|
||||
|
|
@ -2065,24 +2078,34 @@ public:
|
|||
return nodep && privateTypeTest<T>(nodep) ? reinterpret_cast<const T*>(nodep) : nullptr;
|
||||
}
|
||||
|
||||
// For use via the VN_AS macro only
|
||||
// For use via privateAs or the VN_DBG_AS macro only
|
||||
template <typename T, typename E>
|
||||
static T* privateAs(AstNode* nodep) VL_PURE {
|
||||
static T* unsafePrivateAs(AstNode* nodep) VL_PURE {
|
||||
static_assert(!uselessCast<T, E>(), "Unnecessary VN_AS, node known to have target type.");
|
||||
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_AS, node cannot be this type.");
|
||||
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
|
||||
"AstNode is not of expected type, but instead has type '" << nodep->typeName()
|
||||
<< "'");
|
||||
return reinterpret_cast<T*>(nodep);
|
||||
}
|
||||
template <typename T, typename E>
|
||||
static const T* privateAs(const AstNode* nodep) VL_PURE {
|
||||
static const T* unsafePrivateAs(const AstNode* nodep) VL_PURE {
|
||||
static_assert(!uselessCast<T, E>(), "Unnecessary VN_AS, node known to have target type.");
|
||||
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_AS, node cannot be this type.");
|
||||
return reinterpret_cast<const T*>(nodep);
|
||||
}
|
||||
|
||||
// For use via the VN_AS macro only
|
||||
template <typename T, typename E>
|
||||
static T* privateAs(AstNode* nodep) VL_PURE {
|
||||
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
|
||||
"AstNode is not of expected type, but instead has type '" << nodep->typeName()
|
||||
<< "'");
|
||||
return reinterpret_cast<const T*>(nodep);
|
||||
return unsafePrivateAs<T, E>(nodep);
|
||||
}
|
||||
template <typename T, typename E>
|
||||
static const T* privateAs(const AstNode* nodep) VL_PURE {
|
||||
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
|
||||
"AstNode is not of expected type, but instead has type '" << nodep->typeName()
|
||||
<< "'");
|
||||
return unsafePrivateAs<T, E>(nodep);
|
||||
}
|
||||
|
||||
// Predicate that returns true if the given 'nodep' might have a descendant of type 'T_Node'.
|
||||
|
|
|
|||
|
|
@ -154,12 +154,15 @@ AstCExpr::AstCExpr(FileLine* fl, const string& textStmt, int setwidth, bool clea
|
|||
}
|
||||
|
||||
AstVarRef::AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarRef(fl, varp->name(), varp, access) {}
|
||||
: ASTGEN_SUPER_VarRef(fl, varp, access) {}
|
||||
// This form only allowed post-link (see above)
|
||||
AstVarRef::AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarRef(fl, varscp->varp()->name(), varscp->varp(), access) {
|
||||
: ASTGEN_SUPER_VarRef(fl, varscp->varp(), access) {
|
||||
varScopep(varscp);
|
||||
}
|
||||
|
||||
string AstVarRef::name() const { return varp() ? varp()->name() : "<null>"; }
|
||||
|
||||
bool AstVarRef::same(const AstVarRef* samep) const {
|
||||
if (varScopep()) {
|
||||
return (varScopep() == samep->varScopep() && access() == samep->access());
|
||||
|
|
@ -179,7 +182,8 @@ bool AstVarRef::sameNoLvalue(AstVarRef* samep) const {
|
|||
}
|
||||
|
||||
AstVarXRef::AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarXRef(fl, varp->name(), varp, access)
|
||||
: ASTGEN_SUPER_VarXRef(fl, varp, access)
|
||||
, m_name{varp->name()}
|
||||
, m_dotted{dotted} {
|
||||
dtypeFrom(varp);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,13 +126,13 @@ public:
|
|||
const char* charIQWN() const {
|
||||
return (isString() ? "N" : isWide() ? "W" : isQuad() ? "Q" : "I");
|
||||
}
|
||||
string cType(const string& name, bool forFunc, bool isRef) const VL_MT_STABLE;
|
||||
string cType(const string& name, bool forFunc, bool isRef) const;
|
||||
// Represents a C++ LiteralType? (can be constexpr)
|
||||
bool isLiteralType() const VL_MT_STABLE;
|
||||
|
||||
private:
|
||||
class CTypeRecursed;
|
||||
CTypeRecursed cTypeRecurse(bool compound) const VL_MT_STABLE;
|
||||
CTypeRecursed cTypeRecurse(bool compound) const;
|
||||
};
|
||||
class AstNodeArrayDType VL_NOT_FINAL : public AstNodeDType {
|
||||
// Array data type, ie "some_dtype var_name [2:0]"
|
||||
|
|
@ -199,12 +199,9 @@ class AstNodeUOrStructDType VL_NOT_FINAL : public AstNodeDType {
|
|||
// A struct or union; common handling
|
||||
// @astgen op1 := membersp : List[AstMemberDType]
|
||||
private:
|
||||
// TYPES
|
||||
using MemberNameMap = std::map<const std::string, AstMemberDType*>;
|
||||
// MEMBERS
|
||||
string m_name; // Name from upper typedef, if any
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
|
||||
MemberNameMap m_members;
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Package it will be emitted with
|
||||
const int m_uniqueNum;
|
||||
bool m_packed;
|
||||
bool m_isFourstate = false; // V3Width computes
|
||||
|
|
@ -250,12 +247,6 @@ public:
|
|||
static bool packedUnsup() { return true; }
|
||||
void isFourstate(bool flag) { m_isFourstate = flag; }
|
||||
bool isFourstate() const override VL_MT_SAFE { return m_isFourstate; }
|
||||
void clearCache() { m_members.clear(); }
|
||||
void repairMemberCache();
|
||||
AstMemberDType* findMember(const string& name) const {
|
||||
const auto it = m_members.find(name);
|
||||
return (it == m_members.end()) ? nullptr : it->second;
|
||||
}
|
||||
static int lo() { return 0; }
|
||||
int hi() const { return dtypep()->width() - 1; } // Packed classes look like arrays
|
||||
VNumRange declRange() const { return VNumRange{hi(), lo()}; }
|
||||
|
|
@ -1062,7 +1053,7 @@ private:
|
|||
// Post-width typedefs are removed and point to type directly
|
||||
AstNodeDType* m_refDTypep = nullptr; // data type pointed to, BELOW the AstTypedef
|
||||
string m_name; // Name of an AstTypedef
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Class/package in which it was defined
|
||||
public:
|
||||
AstRefDType(FileLine* fl, const string& name)
|
||||
: ASTGEN_SUPER_RefDType(fl)
|
||||
|
|
@ -1077,6 +1068,7 @@ public:
|
|||
AstRefDType(FileLine* fl, FlagTypeOfExpr, AstNode* typeofp)
|
||||
: ASTGEN_SUPER_RefDType(fl) {
|
||||
this->typeofp(typeofp);
|
||||
if (AstNodeDType* const dtp = VN_CAST(typeofp, NodeDType)) refDTypep(dtp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstRefDType;
|
||||
// METHODS
|
||||
|
|
@ -1188,6 +1180,34 @@ public:
|
|||
int widthTotalBytes() const override { return sizeof(std::map<std::string, std::string>); }
|
||||
bool isCompound() const override { return true; }
|
||||
};
|
||||
class AstStreamDType final : public AstNodeDType {
|
||||
// Stream data type, used only as data type of stream operations
|
||||
// Should behave like AstPackArrayDType, but it doesn't have a size
|
||||
public:
|
||||
explicit AstStreamDType(FileLine* fl)
|
||||
: ASTGEN_SUPER_StreamDType(fl) {
|
||||
dtypep(this);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstStreamDType;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
bool hasDType() const override { return true; }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
bool undead() const override { return true; }
|
||||
AstNodeDType* subDTypep() const override VL_MT_SAFE { return nullptr; }
|
||||
AstNodeDType* virtRefDTypep() const override { return nullptr; }
|
||||
void virtRefDTypep(AstNodeDType* nodep) override {}
|
||||
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
// cppcheck-suppress csyleCast
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||
// cppcheck-suppress csyleCast
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
// cppcheck-suppress csyleCast
|
||||
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
int widthAlignBytes() const override { return 1; }
|
||||
int widthTotalBytes() const override { return 1; }
|
||||
bool isCompound() const override { return false; }
|
||||
};
|
||||
class AstUnsizedArrayDType final : public AstNodeDType {
|
||||
// Unsized/open-range Array data type, ie "some_dtype var_name []"
|
||||
// @astgen op1 := childDTypep : Optional[AstNodeDType] // moved to refDTypep() in V3Width
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ class AstNodeFTaskRef VL_NOT_FINAL : public AstNodeExpr {
|
|||
// @astgen op3 := pinsp : List[AstNodeExpr]
|
||||
// @astgen op4 := scopeNamep : Optional[AstScopeName]
|
||||
AstNodeFTask* m_taskp = nullptr; // [AfterLink] Pointer to task referenced
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Class/package of the task
|
||||
string m_name; // Name of variable
|
||||
string m_dotted; // Dotted part of scope the name()ed task/func is under or ""
|
||||
string m_inlinedDots; // Dotted hierarchy flattened out
|
||||
|
|
@ -449,21 +449,18 @@ class AstNodeVarRef VL_NOT_FINAL : public AstNodeExpr {
|
|||
VAccess m_access; // Left hand side assignment
|
||||
AstVar* m_varp; // [AfterLink] Pointer to variable itself
|
||||
AstVarScope* m_varScopep = nullptr; // Varscope for hierarchy
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
|
||||
string m_name; // Name of variable
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Class/package of the variable
|
||||
string m_selfPointer; // Output code object pointer (e.g.: 'this')
|
||||
|
||||
protected:
|
||||
AstNodeVarRef(VNType t, FileLine* fl, const string& name, const VAccess& access)
|
||||
AstNodeVarRef(VNType t, FileLine* fl, const VAccess& access)
|
||||
: AstNodeExpr{t, fl}
|
||||
, m_access{access}
|
||||
, m_name{name} {
|
||||
, m_access{access} {
|
||||
varp(nullptr);
|
||||
}
|
||||
AstNodeVarRef(VNType t, FileLine* fl, const string& name, AstVar* varp, const VAccess& access)
|
||||
AstNodeVarRef(VNType t, FileLine* fl, AstVar* varp, const VAccess& access)
|
||||
: AstNodeExpr{t, fl}
|
||||
, m_access{access}
|
||||
, m_name{name} {
|
||||
, m_access{access} {
|
||||
// May have varp==nullptr
|
||||
this->varp(varp);
|
||||
}
|
||||
|
|
@ -474,8 +471,6 @@ public:
|
|||
const char* broken() const override;
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
void cloneRelink() override;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
void name(const string& name) override { m_name = name; }
|
||||
VAccess access() const { return m_access; }
|
||||
void access(const VAccess& flag) { m_access = flag; } // Avoid using this; Set in constructor
|
||||
AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable
|
||||
|
|
@ -721,7 +716,7 @@ class AstClassOrPackageRef final : public AstNodeExpr {
|
|||
private:
|
||||
string m_name;
|
||||
// Node not NodeModule to appease some early parser usage
|
||||
AstNode* m_classOrPackageNodep; // Package hierarchy
|
||||
AstNode* m_classOrPackageNodep; // Pointer to class/package referenced
|
||||
public:
|
||||
AstClassOrPackageRef(FileLine* fl, const string& name, AstNode* classOrPackageNodep,
|
||||
AstPin* paramsp)
|
||||
|
|
@ -1062,7 +1057,7 @@ public:
|
|||
};
|
||||
class AstEnumItemRef final : public AstNodeExpr {
|
||||
AstEnumItem* m_itemp; // [AfterLink] Pointer to item
|
||||
AstNodeModule* m_classOrPackagep; // Package hierarchy
|
||||
AstNodeModule* m_classOrPackagep; // Class/package in which it was defined
|
||||
public:
|
||||
AstEnumItemRef(FileLine* fl, AstEnumItem* itemp, AstNodeModule* classOrPackagep)
|
||||
: ASTGEN_SUPER_EnumItemRef(fl)
|
||||
|
|
@ -1428,6 +1423,7 @@ class AstMemberSel final : public AstNodeExpr {
|
|||
// @astgen op1 := fromp : AstNodeExpr
|
||||
// Don't need the class we are extracting from, as the "fromp()"'s datatype can get us to it
|
||||
string m_name;
|
||||
VAccess m_access; // Read or write, as in AstNodeVarRef
|
||||
AstVar* m_varp = nullptr; // Post link, variable within class that is target of selection
|
||||
public:
|
||||
AstMemberSel(FileLine* fl, AstNodeExpr* fromp, VFlagChildDType, const string& name)
|
||||
|
|
@ -1448,10 +1444,12 @@ public:
|
|||
void dump(std::ostream& str) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
void name(const string& name) override { m_name = name; }
|
||||
VAccess access() const { return m_access; }
|
||||
void access(const VAccess& flag) { m_access = flag; }
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool same(const AstNode* samep) const override { return true; } // dtype comparison does it
|
||||
bool same(const AstNode* samep) const override;
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
AstVar* varp() const { return m_varp; }
|
||||
void varp(AstVar* nodep) { m_varp = nodep; }
|
||||
|
|
@ -1911,7 +1909,7 @@ public:
|
|||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstStructSel final : public AstNodeExpr {
|
||||
// Unpacked struct member access
|
||||
// Unpacked struct/union member access
|
||||
// Parents: math|stmt
|
||||
// Children: varref, math
|
||||
// @astgen op1 := fromp : AstNodeExpr
|
||||
|
|
@ -1929,7 +1927,10 @@ 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 {
|
||||
// Not a union
|
||||
return VN_IS(fromp()->dtypep()->skipRefp(), StructDType);
|
||||
}
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstStructSel* const sp = static_cast<const AstStructSel*>(samep);
|
||||
return m_name == sp->m_name;
|
||||
|
|
@ -4671,7 +4672,7 @@ public:
|
|||
ASTGEN_MEMBERS_AstCvtPackString;
|
||||
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAssign(lhs); }
|
||||
string emitVerilog() override { return "%f$_CAST(%l)"; }
|
||||
string emitC() override { return "VL_CVT_PACK_STR_N%lq(%lW, %li)"; }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
|
|
@ -5349,15 +5350,15 @@ public:
|
|||
class AstVarRef final : public AstNodeVarRef {
|
||||
// A reference to a variable (lvalue or rvalue)
|
||||
public:
|
||||
AstVarRef(FileLine* fl, const string& name, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarRef(fl, name, nullptr, access) {}
|
||||
// This form only allowed post-link because output/wire compression may
|
||||
// lead to deletion of AstVar's
|
||||
inline AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access);
|
||||
// This form only allowed post-link (see above)
|
||||
inline AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access);
|
||||
ASTGEN_MEMBERS_AstVarRef;
|
||||
inline string name() const override; // * = Var name
|
||||
void dump(std::ostream& str) const override;
|
||||
const char* broken() const override;
|
||||
bool same(const AstNode* samep) const override;
|
||||
inline bool same(const AstVarRef* samep) const;
|
||||
inline bool sameNoLvalue(AstVarRef* samep) const;
|
||||
|
|
@ -5369,14 +5370,17 @@ public:
|
|||
class AstVarXRef final : public AstNodeVarRef {
|
||||
// A VarRef to something in another module before AstScope.
|
||||
// Includes pin on a cell, as part of a ASSIGN statement to connect I/Os until AstScope
|
||||
string m_name;
|
||||
string m_dotted; // Dotted part of scope the name()'ed reference is under or ""
|
||||
string m_inlinedDots; // Dotted hierarchy flattened out
|
||||
public:
|
||||
AstVarXRef(FileLine* fl, const string& name, const string& dotted, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarXRef(fl, name, nullptr, access)
|
||||
: ASTGEN_SUPER_VarXRef(fl, nullptr, access)
|
||||
, m_name{name}
|
||||
, m_dotted{dotted} {}
|
||||
inline AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access);
|
||||
ASTGEN_MEMBERS_AstVarXRef;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
void dump(std::ostream& str) const override;
|
||||
string dotted() const { return m_dotted; }
|
||||
void dotted(const string& dotted) { m_dotted = dotted; }
|
||||
|
|
|
|||
|
|
@ -86,7 +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
|
||||
bool m_needProcess : 1; // Implements part of a process that allocates std::process
|
||||
VLifetime m_lifetime; // Lifetime
|
||||
protected:
|
||||
AstNodeFTask(VNType t, FileLine* fl, const string& name, AstNode* stmtsp)
|
||||
|
|
@ -112,7 +112,7 @@ protected:
|
|||
, m_recursive{false}
|
||||
, m_underGenerate{false}
|
||||
, m_virtual{false}
|
||||
, m_fromStd{false} {
|
||||
, m_needProcess{false} {
|
||||
addStmtsp(stmtsp);
|
||||
cname(name); // Might be overridden by dpi import/export
|
||||
}
|
||||
|
|
@ -172,8 +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 setNeedProcess() { m_needProcess = true; }
|
||||
bool needProcess() const { return m_needProcess; }
|
||||
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
|
||||
VLifetime lifetime() const { return m_lifetime; }
|
||||
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
|
||||
|
|
@ -1513,6 +1513,7 @@ class AstTypeTable final : public AstNode {
|
|||
AstEmptyQueueDType* m_emptyQueuep = nullptr;
|
||||
AstQueueDType* m_queueIndexp = nullptr;
|
||||
AstVoidDType* m_voidp = nullptr;
|
||||
AstStreamDType* m_streamp = nullptr;
|
||||
AstBasicDType* m_basicps[VBasicDTypeKwd::_ENUM_MAX]{};
|
||||
//
|
||||
using DetailedMap = std::map<VBasicTypeKey, AstBasicDType*>;
|
||||
|
|
@ -1538,6 +1539,7 @@ public:
|
|||
AstEmptyQueueDType* findEmptyQueueDType(FileLine* fl);
|
||||
AstQueueDType* findQueueIndexDType(FileLine* fl);
|
||||
AstVoidDType* findVoidDType(FileLine* fl);
|
||||
AstStreamDType* findStreamDType(FileLine* fl);
|
||||
void clearCache();
|
||||
void repairCache();
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
|
|
@ -1810,7 +1812,7 @@ public:
|
|||
string dpiTmpVarType(const string& varName) const;
|
||||
// Return Verilator internal type for argument: CData, SData, IData, WData
|
||||
string vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc = "",
|
||||
bool asRef = false) const VL_MT_STABLE;
|
||||
bool asRef = false) const;
|
||||
string vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc
|
||||
string vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc
|
||||
string vlPropDecl(const string& propName) const; // Return VerilatorVarProps declaration
|
||||
|
|
@ -2135,17 +2137,13 @@ public:
|
|||
// === AstNodeModule ===
|
||||
class AstClass final : public AstNodeModule {
|
||||
// @astgen op4 := extendsp : List[AstClassExtends]
|
||||
// TYPES
|
||||
using MemberNameMap = std::map<const std::string, AstNode*>;
|
||||
// MEMBERS
|
||||
MemberNameMap m_members; // Members or method children
|
||||
AstClassPackage* m_classOrPackagep = nullptr; // Class package this is under
|
||||
AstClassPackage* m_classOrPackagep = nullptr; // Package it will be emitted with
|
||||
bool m_extended = false; // Is extension or extended by other classes
|
||||
bool m_interfaceClass = false; // Interface class
|
||||
bool m_needRNG = false; // Need RNG, uses srandom/randomize
|
||||
bool m_virtual = false; // Virtual class
|
||||
bool m_parameterized = false; // Parameterized class
|
||||
void insertCache(AstNode* nodep);
|
||||
|
||||
public:
|
||||
AstClass(FileLine* fl, const string& name)
|
||||
|
|
@ -2160,16 +2158,7 @@ public:
|
|||
AstClassPackage* classOrPackagep() const VL_MT_SAFE { return m_classOrPackagep; }
|
||||
void classOrPackagep(AstClassPackage* classpackagep) { m_classOrPackagep = classpackagep; }
|
||||
AstNode* membersp() const { return stmtsp(); }
|
||||
void addMembersp(AstNode* nodep) {
|
||||
insertCache(nodep);
|
||||
addStmtsp(nodep);
|
||||
}
|
||||
void clearCache() { m_members.clear(); }
|
||||
void repairCache();
|
||||
AstNode* findMember(const string& name) const {
|
||||
const auto it = m_members.find(name);
|
||||
return (it == m_members.end()) ? nullptr : it->second;
|
||||
}
|
||||
void addMembersp(AstNode* nodep) { addStmtsp(nodep); }
|
||||
bool isExtended() const { return m_extended; }
|
||||
void isExtended(bool flag) { m_extended = flag; }
|
||||
bool isInterfaceClass() const { return m_interfaceClass; }
|
||||
|
|
@ -2185,6 +2174,7 @@ public:
|
|||
static bool isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp);
|
||||
// Return the lowest class extended from, or this class
|
||||
AstClass* baseMostClassp();
|
||||
static bool isCacheableChild(const AstNode* nodep);
|
||||
};
|
||||
class AstClassPackage final : public AstNodeModule {
|
||||
// The static information portion of a class (treated similarly to a package)
|
||||
|
|
|
|||
|
|
@ -103,29 +103,8 @@ int AstNodeSel::bitConst() const {
|
|||
return (constp ? constp->toSInt() : 0);
|
||||
}
|
||||
|
||||
void AstNodeUOrStructDType::repairMemberCache() {
|
||||
clearCache();
|
||||
for (AstMemberDType* itemp = membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
if (m_members.find(itemp->name()) != m_members.end()) {
|
||||
itemp->v3error("Duplicate declaration of member name: " << itemp->prettyNameQ());
|
||||
} else {
|
||||
m_members.emplace(itemp->name(), itemp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* AstNodeUOrStructDType::broken() const {
|
||||
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
|
||||
std::unordered_set<AstMemberDType*> exists;
|
||||
for (AstMemberDType* itemp = membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
exists.insert(itemp);
|
||||
}
|
||||
for (MemberNameMap::const_iterator it = m_members.begin(); it != m_members.end(); ++it) {
|
||||
if (VL_UNCOVERABLE(exists.find(it->second) == exists.end())) {
|
||||
this->v3error("Internal: Structure member broken: " << it->first);
|
||||
return "member broken";
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -399,7 +378,7 @@ string AstVar::verilogKwd() const {
|
|||
}
|
||||
|
||||
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc,
|
||||
bool asRef) const VL_MT_STABLE {
|
||||
bool asRef) const {
|
||||
UASSERT_OBJ(!forReturn, this,
|
||||
"Internal data is never passed as return, but as first argument");
|
||||
string ostatic;
|
||||
|
|
@ -716,12 +695,12 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef) const VL_MT_STABLE {
|
||||
string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef) const {
|
||||
const CTypeRecursed info = cTypeRecurse(false);
|
||||
return info.render(name, isRef);
|
||||
}
|
||||
|
||||
AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const VL_MT_STABLE {
|
||||
AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
|
||||
// Legacy compound argument currently just passed through and unused
|
||||
CTypeRecursed info;
|
||||
|
||||
|
|
@ -1071,6 +1050,15 @@ AstVoidDType* AstTypeTable::findVoidDType(FileLine* fl) {
|
|||
return m_voidp;
|
||||
}
|
||||
|
||||
AstStreamDType* AstTypeTable::findStreamDType(FileLine* fl) {
|
||||
if (VL_UNLIKELY(!m_streamp)) {
|
||||
AstStreamDType* const newp = new AstStreamDType{fl};
|
||||
addTypesp(newp);
|
||||
m_streamp = newp;
|
||||
}
|
||||
return m_streamp;
|
||||
}
|
||||
|
||||
AstQueueDType* AstTypeTable::findQueueIndexDType(FileLine* fl) {
|
||||
if (VL_UNLIKELY(!m_queueIndexp)) {
|
||||
AstQueueDType* const newp = new AstQueueDType{fl, AstNode::findUInt32DType(), nullptr};
|
||||
|
|
@ -1418,29 +1406,10 @@ const char* AstClassPackage::broken() const {
|
|||
void AstClassPackage::cloneRelink() {
|
||||
if (m_classp && m_classp->clonep()) m_classp = m_classp->clonep();
|
||||
}
|
||||
void AstClass::insertCache(AstNode* nodep) {
|
||||
const bool doit = (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef)
|
||||
|| (VN_IS(nodep, NodeFTask) && !VN_AS(nodep, NodeFTask)->isExternProto())
|
||||
|| VN_IS(nodep, CFunc));
|
||||
if (doit) {
|
||||
if (m_members.find(nodep->name()) != m_members.end()) {
|
||||
nodep->v3error("Duplicate declaration of member name: " << nodep->prettyNameQ());
|
||||
} else {
|
||||
m_members.emplace(nodep->name(), nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
void AstClass::repairCache() {
|
||||
clearCache();
|
||||
for (auto* itemp = membersp(); itemp; itemp = itemp->nextp()) {
|
||||
if (const auto* const scopep = VN_CAST(itemp, Scope)) {
|
||||
for (auto* blockp = scopep->blocksp(); blockp; blockp = blockp->nextp()) {
|
||||
insertCache(blockp);
|
||||
}
|
||||
} else {
|
||||
insertCache(itemp);
|
||||
}
|
||||
}
|
||||
bool AstClass::isCacheableChild(const AstNode* nodep) {
|
||||
return (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef)
|
||||
|| (VN_IS(nodep, NodeFTask) && !VN_AS(nodep, NodeFTask)->isExternProto())
|
||||
|| VN_IS(nodep, CFunc));
|
||||
}
|
||||
AstClass* AstClass::baseMostClassp() {
|
||||
AstClass* basep = this;
|
||||
|
|
@ -1667,6 +1636,12 @@ AstNodeUOrStructDType* AstMemberDType::getChildStructp() const {
|
|||
return VN_CAST(subdtp, NodeUOrStructDType); // Maybe nullptr
|
||||
}
|
||||
|
||||
bool AstMemberSel::same(const AstNode* samep) const {
|
||||
const AstMemberSel* const sp = static_cast<const AstMemberSel*>(samep);
|
||||
return sp != nullptr && access() == sp->access() && fromp()->same(sp->fromp())
|
||||
&& name() == sp->name() && varp()->same(sp->varp());
|
||||
}
|
||||
|
||||
void AstMemberSel::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
str << " -> ";
|
||||
|
|
@ -2055,6 +2030,10 @@ void AstVoidDType::dumpSmall(std::ostream& str) const {
|
|||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "void";
|
||||
}
|
||||
void AstStreamDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "stream";
|
||||
}
|
||||
void AstVarScope::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (isTrace()) str << " [T]";
|
||||
|
|
@ -2097,6 +2076,10 @@ void AstVarRef::dump(std::ostream& str) const {
|
|||
str << "UNLINKED";
|
||||
}
|
||||
}
|
||||
const char* AstVarRef::broken() const {
|
||||
BROKEN_RTN(!varp());
|
||||
return AstNodeVarRef::broken();
|
||||
}
|
||||
bool AstVarRef::same(const AstNode* samep) const {
|
||||
return same(static_cast<const AstVarRef*>(samep));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,39 +40,6 @@ 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
|
||||
|
|
@ -105,7 +72,6 @@ private:
|
|||
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
|
||||
int m_ifDepth = 0; // Current if depth
|
||||
bool m_keepBegins = false; // True if begins should not be inlined
|
||||
std::set<AstVar*> m_staticFuncVars; // Static variables from m_ftaskp
|
||||
|
||||
// METHODS
|
||||
|
||||
|
|
@ -212,7 +178,10 @@ private:
|
|||
m_ftaskp = nodep;
|
||||
m_liftedp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
RenameStaticVisitor{m_staticFuncVars, m_ftaskp, nodep};
|
||||
nodep->foreach([&](AstInitialStatic* const initp) {
|
||||
initp->unlinkFrBack();
|
||||
m_ftaskp->addHereThisAsNext(initp);
|
||||
});
|
||||
if (m_liftedp) {
|
||||
// Place lifted nodes at beginning of stmtsp, so Var nodes appear before referenced
|
||||
if (AstNode* const stmtsp = nodep->stmtsp()) {
|
||||
|
|
@ -222,7 +191,6 @@ private:
|
|||
nodep->addStmtsp(m_liftedp);
|
||||
m_liftedp = nullptr;
|
||||
}
|
||||
m_staticFuncVars.clear();
|
||||
m_ftaskp = nullptr;
|
||||
}
|
||||
}
|
||||
|
|
@ -266,7 +234,6 @@ private:
|
|||
nodep->name(newName);
|
||||
nodep->unlinkFrBack();
|
||||
m_ftaskp->addHereThisAsNext(nodep);
|
||||
m_staticFuncVars.insert(nodep);
|
||||
nodep->funcLocal(false);
|
||||
} else if (m_unnamedScope != "") {
|
||||
// Rename it
|
||||
|
|
@ -377,7 +344,6 @@ private:
|
|||
void visit(AstVarRef* nodep) override {
|
||||
if (nodep->varp()->user1()) { // It was converted
|
||||
UINFO(9, " relinVarRef " << nodep << endl);
|
||||
nodep->name(nodep->varp()->name());
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ private:
|
|||
nodep->editCountInc();
|
||||
nodep->classOrPackagep(packagep);
|
||||
packagep->classp(nodep);
|
||||
packagep->timeunit(nodep->timeunit());
|
||||
v3Global.rootp()->addModulesp(packagep);
|
||||
// Add package to hierarchy
|
||||
AstCell* const cellp = new AstCell{packagep->fileline(),
|
||||
|
|
@ -105,7 +106,6 @@ private:
|
|||
m_prefix = nodep->name() + "__02e"; // .
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
nodep->repairCache();
|
||||
}
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
// Visit for NodeModules that are not AstClass (AstClass is-a AstNodeModule)
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ private:
|
|||
|| VN_IS(nodep->dtypep()->skipRefp(), DynArrayDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), ClassRefDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), QueueDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), StreamDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), VoidDType)) {
|
||||
} else {
|
||||
|
|
@ -234,11 +235,6 @@ 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);
|
||||
|
|
|
|||
|
|
@ -2166,7 +2166,12 @@ private:
|
|||
AstNodeExpr* const srcp = nodep->rhsp()->unlinkFrBack();
|
||||
// Connect the rhs to the stream operator and update its width
|
||||
VN_AS(streamp, StreamL)->lhsp(srcp);
|
||||
streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(), VSigning::UNSIGNED);
|
||||
if (VN_IS(srcp->dtypep(), DynArrayDType) || VN_IS(srcp->dtypep(), QueueDType)
|
||||
|| VN_IS(srcp->dtypep(), UnpackArrayDType)) {
|
||||
streamp->dtypeSetStream();
|
||||
} else {
|
||||
streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(), VSigning::UNSIGNED);
|
||||
}
|
||||
// Shrink the RHS if necessary
|
||||
if (sWidth > dWidth) {
|
||||
streamp = new AstSel{streamp->fileline(), streamp, sWidth - dWidth, dWidth};
|
||||
|
|
@ -3650,6 +3655,7 @@ private:
|
|||
public:
|
||||
// Processing Mode Enum
|
||||
enum ProcMode : uint8_t {
|
||||
PROC_PARAMS_NOWARN,
|
||||
PROC_PARAMS,
|
||||
PROC_GENERATE,
|
||||
PROC_LIVE,
|
||||
|
|
@ -3665,16 +3671,18 @@ public:
|
|||
, m_concswapNames{globalPass ? ("__Vconcswap_" + cvtToStr(s_globalPassNum++)) : ""} {
|
||||
// clang-format off
|
||||
switch (pmode) {
|
||||
case PROC_PARAMS: m_doV = true; m_doNConst = true; m_params = true;
|
||||
m_required = true; break;
|
||||
case PROC_GENERATE: m_doV = true; m_doNConst = true; m_params = true;
|
||||
m_required = true; m_doGenerate = true; break;
|
||||
case PROC_LIVE: break;
|
||||
case PROC_V_WARN: m_doV = true; m_doNConst = true; m_warn = true; break;
|
||||
case PROC_V_NOWARN: m_doV = true; m_doNConst = true; break;
|
||||
case PROC_V_EXPENSIVE: m_doV = true; m_doNConst = true; m_doExpensive = true; break;
|
||||
case PROC_CPP: m_doV = false; m_doNConst = true; m_doCpp = true; break;
|
||||
default: v3fatalSrc("Bad case"); break;
|
||||
case PROC_PARAMS_NOWARN: m_doV = true; m_doNConst = true; m_params = true;
|
||||
m_required = false; break;
|
||||
case PROC_PARAMS: m_doV = true; m_doNConst = true; m_params = true;
|
||||
m_required = true; break;
|
||||
case PROC_GENERATE: m_doV = true; m_doNConst = true; m_params = true;
|
||||
m_required = true; m_doGenerate = true; break;
|
||||
case PROC_LIVE: break;
|
||||
case PROC_V_WARN: m_doV = true; m_doNConst = true; m_warn = true; break;
|
||||
case PROC_V_NOWARN: m_doV = true; m_doNConst = true; break;
|
||||
case PROC_V_EXPENSIVE: m_doV = true; m_doNConst = true; m_doExpensive = true; break;
|
||||
case PROC_CPP: m_doV = false; m_doNConst = true; m_doCpp = true; break;
|
||||
default: v3fatalSrc("Bad case"); break;
|
||||
}
|
||||
// clang-format on
|
||||
}
|
||||
|
|
@ -3722,6 +3730,29 @@ AstNode* V3Const::constifyParamsEdit(AstNode* nodep) {
|
|||
return nodep;
|
||||
}
|
||||
|
||||
//! Constify this cell node's parameter list if possible
|
||||
//! @return Pointer to the edited node.
|
||||
AstNode* V3Const::constifyParamsNoWarnEdit(AstNode* nodep) {
|
||||
// if (debug() > 0) nodep->dumpTree("- forceConPRE : ");
|
||||
// Resize even if the node already has a width, because buried in the tree
|
||||
// we may have a node we just created with signing, etc, that isn't sized yet.
|
||||
|
||||
// Make sure we've sized everything first
|
||||
nodep = V3Width::widthParamsEdit(nodep);
|
||||
ConstVisitor visitor{ConstVisitor::PROC_PARAMS_NOWARN, /* globalPass: */ false};
|
||||
if (AstVar* const varp = VN_CAST(nodep, Var)) {
|
||||
// If a var wants to be constified, it's really a param, and
|
||||
// we want the value to be constant. We aren't passed just the
|
||||
// init value because we need widthing above to handle the var's type.
|
||||
if (varp->valuep()) visitor.mainAcceptEdit(varp->valuep());
|
||||
} else {
|
||||
nodep = visitor.mainAcceptEdit(nodep);
|
||||
}
|
||||
// Because we do edits, nodep links may get trashed and core dump this.
|
||||
// if (debug() > 0) nodep->dumpTree("- forceConDONE: ");
|
||||
return nodep;
|
||||
}
|
||||
|
||||
//! Force this cell node's parameter list to become a constant inside generate.
|
||||
//! If we are inside a generated "if", "case" or "for", we don't want to
|
||||
//! trigger warnings when we deal with the width. It is possible that these
|
||||
|
|
|
|||
|
|
@ -30,6 +30,10 @@ public:
|
|||
static AstNodeExpr* constifyParamsEdit(AstNodeExpr* exprp) {
|
||||
return VN_AS(constifyParamsEdit(static_cast<AstNode*>(exprp)), NodeExpr);
|
||||
}
|
||||
static AstNode* constifyParamsNoWarnEdit(AstNode* nodep);
|
||||
static AstNodeExpr* constifyParamsNoWarnEdit(AstNodeExpr* exprp) {
|
||||
return VN_AS(constifyParamsNoWarnEdit(static_cast<AstNode*>(exprp)), NodeExpr);
|
||||
}
|
||||
static AstNode* constifyGenerateParamsEdit(AstNode* nodep);
|
||||
// Only do constant pushing, without removing dead logic
|
||||
static void constifyAllLive(AstNetlist* nodep);
|
||||
|
|
|
|||
|
|
@ -123,9 +123,9 @@ struct V3DfgPeepholeContext final {
|
|||
const std::string m_label; // Label to apply to stats
|
||||
|
||||
// Enable flags for each optimization
|
||||
bool m_enabled[VDfgPeepholePattern::_ENUM_END];
|
||||
std::array<bool, VDfgPeepholePattern::_ENUM_END> m_enabled;
|
||||
// Count of applications for each optimization (for statistics)
|
||||
VDouble0 m_count[VDfgPeepholePattern::_ENUM_END];
|
||||
std::array<VDouble0, VDfgPeepholePattern::_ENUM_END> m_count;
|
||||
|
||||
explicit V3DfgPeepholeContext(const std::string& label);
|
||||
~V3DfgPeepholeContext();
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ public:
|
|||
static string symClassAssign() {
|
||||
return symClassName() + "* const __restrict vlSymsp VL_ATTR_UNUSED = vlSelf->vlSymsp;\n";
|
||||
}
|
||||
static string prefixNameProtect(const AstNode* nodep) VL_MT_STABLE { // C++ name with prefix
|
||||
static string prefixNameProtect(const AstNode* nodep) { // C++ name with prefix
|
||||
return v3Global.opt.modPrefix() + "_" + VIdProtect::protect(nodep->name());
|
||||
}
|
||||
static bool isAnonOk(const AstVar* varp) {
|
||||
|
|
|
|||
|
|
@ -419,7 +419,7 @@ void EmitCFunc::emitCCallArgs(const AstNodeCCall* nodep, const string& selfPoint
|
|||
}
|
||||
if (nodep->funcp()->needProcess()) {
|
||||
if (comma) puts(", ");
|
||||
if (VN_IS(nodep->backp(), CAwait)) {
|
||||
if (VN_IS(nodep->backp(), CAwait) || !nodep->funcp()->isCoroutine()) {
|
||||
puts("vlProcess");
|
||||
} else {
|
||||
puts("std::make_shared<VlProcess>()");
|
||||
|
|
@ -452,6 +452,10 @@ void EmitCFunc::emitCvtPackStr(AstNode* nodep) {
|
|||
putbs("std::string{");
|
||||
putsQuoted(constp->num().toString());
|
||||
puts("}");
|
||||
} else if (VN_IS(nodep->dtypep(), StreamDType)) {
|
||||
putbs("VL_CVT_PACK_STR_ND(");
|
||||
iterateAndNextConstNull(nodep);
|
||||
puts(")");
|
||||
} else {
|
||||
putbs("VL_CVT_PACK_STR_N");
|
||||
emitIQW(nodep);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "V3EmitCConstInit.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3MemberMap.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
|
|
@ -115,6 +116,7 @@ public:
|
|||
|
||||
class EmitCFunc VL_NOT_FINAL : public EmitCConstInit {
|
||||
private:
|
||||
VMemberMap m_memberMap;
|
||||
AstVarRef* m_wideTempRefp = nullptr; // Variable that _WW macros should be setting
|
||||
int m_labelNum = 0; // Next label number
|
||||
int m_splitSize = 0; // # of cfunc nodes placed into output file
|
||||
|
|
@ -148,6 +150,22 @@ protected:
|
|||
bool m_useSelfForThis = false; // Replace "this" with "vlSelf"
|
||||
const AstNodeModule* m_modp = nullptr; // Current module being emitted
|
||||
const AstCFunc* m_cfuncp = nullptr; // Current function being emitted
|
||||
bool m_instantiatesOwnProcess = false;
|
||||
|
||||
bool constructorNeedsProcess(const AstClass* const classp) {
|
||||
const AstNode* const newp = m_memberMap.findMember(classp, "new");
|
||||
if (!newp) return false;
|
||||
const AstCFunc* const ctorp = VN_CAST(newp, CFunc);
|
||||
if (!ctorp) return false;
|
||||
UASSERT_OBJ(ctorp->isConstructor(), ctorp, "`new` is not a constructor!");
|
||||
return ctorp->needProcess();
|
||||
}
|
||||
|
||||
bool constructorNeedsProcess(const AstNodeDType* const dtypep) {
|
||||
if (const AstClassRefDType* const crefdtypep = VN_CAST(dtypep, ClassRefDType))
|
||||
return constructorNeedsProcess(crefdtypep->classp());
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
|
|
@ -212,13 +230,34 @@ public:
|
|||
comma = true;
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
string optionalProcArg(const T* const nodep) {
|
||||
return (nodep && constructorNeedsProcess(nodep)) ? "vlProcess, " : "";
|
||||
}
|
||||
const AstCNew* getSuperNewCallRecursep(AstNode* const nodep) {
|
||||
// Get the super.new call
|
||||
if (!nodep) return nullptr;
|
||||
if (const AstCNew* const cnewp = VN_CAST(nodep, CNew)) return cnewp;
|
||||
if (const AstStmtExpr* const stmtp = VN_CAST(nodep, StmtExpr)) {
|
||||
if (const AstCNew* const cnewp = VN_CAST(stmtp->exprp(), CNew)) return cnewp;
|
||||
}
|
||||
if (const AstJumpBlock* const blockp = VN_CAST(nodep, JumpBlock)) {
|
||||
if (const AstCNew* const cnewp = getSuperNewCallRecursep(blockp->stmtsp())) {
|
||||
return cnewp;
|
||||
}
|
||||
}
|
||||
if (const AstCNew* const cnewp = getSuperNewCallRecursep(nodep->nextp())) return cnewp;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
using EmitCConstInit::visit;
|
||||
void visit(AstCFunc* nodep) override {
|
||||
VL_RESTORER(m_useSelfForThis);
|
||||
VL_RESTORER(m_cfuncp);
|
||||
VL_RESTORER(m_instantiatesOwnProcess)
|
||||
m_cfuncp = nodep;
|
||||
m_instantiatesOwnProcess = false;
|
||||
|
||||
splitSizeInc(nodep);
|
||||
|
||||
|
|
@ -231,20 +270,16 @@ public:
|
|||
if (!nodep->baseCtors().empty()) {
|
||||
puts(": ");
|
||||
puts(nodep->baseCtors());
|
||||
puts("(vlSymsp");
|
||||
const AstClass* const classp = VN_CAST(nodep->scopep()->modp(), Class);
|
||||
// Find call to super.new to get the arguments
|
||||
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
AstNode* exprp;
|
||||
if (VN_IS(stmtp, StmtExpr)) {
|
||||
exprp = VN_CAST(stmtp, StmtExpr)->exprp();
|
||||
} else {
|
||||
exprp = stmtp;
|
||||
}
|
||||
if (AstCNew* const newRefp = VN_CAST(exprp, CNew)) {
|
||||
putCommaIterateNext(newRefp->argsp(), true);
|
||||
break;
|
||||
}
|
||||
if (classp && constructorNeedsProcess(classp)) {
|
||||
puts("(vlProcess, vlSymsp");
|
||||
} else {
|
||||
puts("(vlSymsp");
|
||||
}
|
||||
const AstCNew* const superNewCallp = getSuperNewCallRecursep(nodep->stmtsp());
|
||||
UASSERT_OBJ(superNewCallp, nodep, "super.new call not found");
|
||||
putCommaIterateNext(superNewCallp->argsp(), true);
|
||||
puts(")");
|
||||
}
|
||||
puts(" {\n");
|
||||
|
|
@ -265,6 +300,23 @@ public:
|
|||
puts(nodep->isLoose() ? "__" : "::");
|
||||
puts(nodep->nameProtect() + "\\n\"); );\n");
|
||||
|
||||
// Instantiate a process class if it's going to be needed somewhere later
|
||||
nodep->forall([&](const AstNodeCCall* ccallp) -> bool {
|
||||
if (ccallp->funcp()->needProcess()
|
||||
&& (ccallp->funcp()->isCoroutine() == VN_IS(ccallp->backp(), CAwait))) {
|
||||
if (!nodep->needProcess() && !m_instantiatesOwnProcess) {
|
||||
m_instantiatesOwnProcess = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (m_instantiatesOwnProcess) {
|
||||
AstNode* const vlprocp = new AstCStmt{
|
||||
nodep->fileline(), "VlProcessRef vlProcess = std::make_shared<VlProcess>();\n"};
|
||||
nodep->stmtsp()->addHereThisAsNext(vlprocp);
|
||||
}
|
||||
|
||||
for (AstNode* subnodep = nodep->argsp(); subnodep; subnodep = subnodep->nextp()) {
|
||||
if (AstVar* const varp = VN_CAST(subnodep, Var)) {
|
||||
if (varp->isFuncReturn()) emitVarDecl(varp);
|
||||
|
|
@ -383,19 +435,17 @@ public:
|
|||
void visit(AstAssocSel* nodep) override {
|
||||
iterateAndNextConstNull(nodep->fromp());
|
||||
putbs(".at(");
|
||||
AstAssocArrayDType* const adtypep = VN_AS(nodep->fromp()->dtypep(), AssocArrayDType);
|
||||
AstAssocArrayDType* const adtypep
|
||||
= VN_AS(nodep->fromp()->dtypep()->skipRefp(), AssocArrayDType);
|
||||
UASSERT_OBJ(adtypep, nodep, "Associative select on non-associative type");
|
||||
if (adtypep->keyDTypep()->isWide()) {
|
||||
emitCvtWideArray(nodep->bitp(), nodep->fromp());
|
||||
} else {
|
||||
iterateAndNextConstNull(nodep->bitp());
|
||||
}
|
||||
iterateAndNextConstNull(nodep->bitp());
|
||||
puts(")");
|
||||
}
|
||||
void visit(AstWildcardSel* nodep) override {
|
||||
iterateAndNextConstNull(nodep->fromp());
|
||||
putbs(".at(");
|
||||
AstWildcardArrayDType* const adtypep = VN_AS(nodep->fromp()->dtypep(), WildcardArrayDType);
|
||||
AstWildcardArrayDType* const adtypep
|
||||
= VN_AS(nodep->fromp()->dtypep()->skipRefp(), WildcardArrayDType);
|
||||
UASSERT_OBJ(adtypep, nodep, "Wildcard select on non-wildcard-associative type");
|
||||
iterateAndNextConstNull(nodep->bitp());
|
||||
puts(")");
|
||||
|
|
@ -443,8 +493,9 @@ public:
|
|||
// super.new case
|
||||
return;
|
||||
}
|
||||
// assignment case
|
||||
puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", vlSymsp");
|
||||
// assignment case;
|
||||
puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", "
|
||||
+ optionalProcArg(nodep->dtypep()) + "vlSymsp");
|
||||
putCommaIterateNext(nodep->argsp(), true);
|
||||
puts(")");
|
||||
}
|
||||
|
|
@ -1026,6 +1077,7 @@ public:
|
|||
UASSERT_OBJ(!emitSimpleOk(nodep), nodep, "Triop cannot be described in a simple way");
|
||||
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), nodep->thsp());
|
||||
}
|
||||
void visit(AstCvtPackString* nodep) override { emitCvtPackStr(nodep->lhsp()); }
|
||||
void visit(AstRedXor* nodep) override {
|
||||
if (nodep->lhsp()->isWide()) {
|
||||
visit(static_cast<AstNodeUniop*>(nodep));
|
||||
|
|
@ -1088,7 +1140,8 @@ public:
|
|||
puts(")");
|
||||
}
|
||||
void visit(AstNewCopy* nodep) override {
|
||||
puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", ");
|
||||
puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", "
|
||||
+ optionalProcArg(nodep->dtypep()));
|
||||
puts("*"); // i.e. make into a reference
|
||||
iterateAndNextConstNull(nodep->rhsp());
|
||||
puts(")");
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ class EmitCGatherDependencies final : VNVisitorConst {
|
|||
}
|
||||
|
||||
public:
|
||||
static const std::set<std::string> gather(AstCFunc* cfuncp) VL_MT_STABLE {
|
||||
static const std::set<std::string> gather(AstCFunc* cfuncp) {
|
||||
const EmitCGatherDependencies visitor{cfuncp};
|
||||
return std::move(visitor.m_dependencies);
|
||||
}
|
||||
|
|
@ -566,8 +566,7 @@ class EmitCImp final : EmitCFunc {
|
|||
~EmitCImp() override = default;
|
||||
|
||||
public:
|
||||
static void main(const AstNodeModule* modp, bool slow,
|
||||
std::deque<AstCFile*>& cfilesr) VL_MT_STABLE {
|
||||
static void main(const AstNodeModule* modp, bool slow, std::deque<AstCFile*>& cfilesr) {
|
||||
EmitCImp{modp, slow, cfilesr};
|
||||
}
|
||||
};
|
||||
|
|
@ -932,11 +931,11 @@ void V3EmitC::emitcImp() {
|
|||
const AstNodeModule* const modp = VN_AS(nodep, NodeModule);
|
||||
cfiles.emplace_back();
|
||||
auto& slowCfilesr = cfiles.back();
|
||||
futures.push_back(V3ThreadPool::s().enqueue<void>(
|
||||
futures.push_back(V3ThreadPool::s().enqueue(
|
||||
[modp, &slowCfilesr]() { EmitCImp::main(modp, /* slow: */ true, slowCfilesr); }));
|
||||
cfiles.emplace_back();
|
||||
auto& fastCfilesr = cfiles.back();
|
||||
futures.push_back(V3ThreadPool::s().enqueue<void>(
|
||||
futures.push_back(V3ThreadPool::s().enqueue(
|
||||
[modp, &fastCfilesr]() { EmitCImp::main(modp, /* slow: */ false, fastCfilesr); }));
|
||||
}
|
||||
|
||||
|
|
@ -944,12 +943,12 @@ void V3EmitC::emitcImp() {
|
|||
if (v3Global.opt.trace() && !v3Global.opt.lintOnly()) {
|
||||
cfiles.emplace_back();
|
||||
auto& slowCfilesr = cfiles.back();
|
||||
futures.push_back(V3ThreadPool::s().enqueue<void>([&slowCfilesr]() {
|
||||
futures.push_back(V3ThreadPool::s().enqueue([&slowCfilesr]() {
|
||||
EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ true, slowCfilesr);
|
||||
}));
|
||||
cfiles.emplace_back();
|
||||
auto& fastCfilesr = cfiles.back();
|
||||
futures.push_back(V3ThreadPool::s().enqueue<void>([&fastCfilesr]() {
|
||||
futures.push_back(V3ThreadPool::s().enqueue([&fastCfilesr]() {
|
||||
EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ false, fastCfilesr);
|
||||
}));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -222,20 +222,18 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra)
|
|||
#ifndef V3ERROR_NO_GLOBAL_
|
||||
if (dumpTreeLevel() || debug()) {
|
||||
V3Broken::allowMidvisitorCheck(true);
|
||||
V3ThreadPool::s().requestExclusiveAccess([&]() VL_REQUIRES(m_mutex) {
|
||||
if (dumpTreeLevel()) {
|
||||
v3Global.rootp()->dumpTreeFile(
|
||||
v3Global.debugFilename("final.tree", 990));
|
||||
}
|
||||
if (debug()) {
|
||||
execErrorExitCb();
|
||||
V3Stats::statsFinalAll(v3Global.rootp());
|
||||
V3Stats::statsReport();
|
||||
}
|
||||
// Abort in exclusive access to make sure other threads
|
||||
// don't change error code
|
||||
vlAbortOrExit();
|
||||
});
|
||||
const V3ThreadPool::ScopedExclusiveAccess exclusiveAccess;
|
||||
if (dumpTreeLevel()) {
|
||||
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("final.tree", 990));
|
||||
}
|
||||
if (debug()) {
|
||||
execErrorExitCb();
|
||||
V3Stats::statsFinalAll(v3Global.rootp());
|
||||
V3Stats::statsReport();
|
||||
}
|
||||
// Abort in exclusive access to make sure other threads
|
||||
// don't change error code
|
||||
vlAbortOrExit();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ public:
|
|||
I_TRACING, // Tracing is on/off from /*verilator tracing_on/off*/
|
||||
I_UNUSED, // Unused genvar, parameter or signal message (Backward Compatibility)
|
||||
// Error codes:
|
||||
E_LIFETIME, // Error: Reference to a variable might outlive the variable.
|
||||
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
|
||||
|
|
@ -98,6 +99,7 @@ public:
|
|||
ENUMVALUE, // Error: enum type needs explicit cast
|
||||
EOFNEWLINE, // End-of-file missing newline
|
||||
GENCLK, // Generated Clock. Historical, never issued.
|
||||
GENUNNAMED, // Generate unnamed, without label
|
||||
HIERBLOCK, // Ignored hierarchical block setting
|
||||
IFDEPTH, // If statements too deep
|
||||
IGNOREDRETURN, // Ignoring return value (function as task)
|
||||
|
|
@ -113,6 +115,7 @@ public:
|
|||
LATCH, // Latch detected outside of always_latch block
|
||||
LITENDIAN, // Little endian, renamed to ASCRANGE
|
||||
MINTYPMAXDLY, // Unsupported: min/typ/max delay expressions
|
||||
MISINDENT, // Misleading indentation
|
||||
MODDUP, // Duplicate module
|
||||
MULTIDRIVEN, // Driven from multiple blocks
|
||||
MULTITOP, // Multiple top level modules
|
||||
|
|
@ -183,7 +186,7 @@ public:
|
|||
// Boolean
|
||||
" I_CELLDEFINE", " I_COVERAGE", " I_DEF_NETTYPE_WIRE", " I_LINT", " I_TIMING", " I_TRACING", " I_UNUSED",
|
||||
// Errors
|
||||
"NEEDTIMINGOPT", "NOTIMING", "PORTSHORT", "TASKNSVAR", "UNSUPPORTED",
|
||||
"LIFETIME", "NEEDTIMINGOPT", "NOTIMING", "PORTSHORT", "TASKNSVAR", "UNSUPPORTED",
|
||||
// Warnings
|
||||
" EC_FIRST_WARN",
|
||||
"ALWCOMBORDER", "ASCRANGE", "ASSIGNDLY", "ASSIGNIN", "BADSTDPRAGMA",
|
||||
|
|
@ -191,11 +194,12 @@ public:
|
|||
"CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST", "CDCRSTLOGIC", "CLKDATA",
|
||||
"CMPCONST", "COLONPLUS", "COMBDLY", "CONSTRAINTIGN", "CONTASSREG",
|
||||
"DECLFILENAME", "DEFPARAM", "DEPRECATED",
|
||||
"ENCAPSULATED", "ENDLABEL", "ENUMVALUE", "EOFNEWLINE", "GENCLK", "HIERBLOCK",
|
||||
"ENCAPSULATED", "ENDLABEL", "ENUMVALUE", "EOFNEWLINE", "GENCLK", "GENUNNAMED",
|
||||
"HIERBLOCK",
|
||||
"IFDEPTH", "IGNOREDRETURN",
|
||||
"IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE",
|
||||
"INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE",
|
||||
"LATCH", "LITENDIAN", "MINTYPMAXDLY", "MODDUP",
|
||||
"LATCH", "LITENDIAN", "MINTYPMAXDLY", "MISINDENT", "MODDUP",
|
||||
"MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOLATCH", "NULLPORT", "PINCONNECTEMPTY",
|
||||
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PROCASSWIRE",
|
||||
"PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY",
|
||||
|
|
@ -234,18 +238,18 @@ 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 == 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);
|
||||
|| m_e == LATCH || m_e == MISINDENT || 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 {
|
||||
return (m_e == ASSIGNDLY // More than style, but for backward compatibility
|
||||
|| m_e == BLKSEQ || m_e == DEFPARAM || m_e == DECLFILENAME || m_e == EOFNEWLINE
|
||||
|| m_e == IMPORTSTAR || m_e == INCABSPATH || m_e == PINCONNECTEMPTY
|
||||
|| m_e == PINNOCONNECT || m_e == SYNCASYNCNET || m_e == UNDRIVEN
|
||||
|| m_e == UNUSEDGENVAR || m_e == UNUSEDPARAM || m_e == UNUSEDSIGNAL
|
||||
|| m_e == VARHIDDEN);
|
||||
|| m_e == GENUNNAMED || m_e == IMPORTSTAR || m_e == INCABSPATH
|
||||
|| m_e == PINCONNECTEMPTY || m_e == PINNOCONNECT || m_e == SYNCASYNCNET
|
||||
|| m_e == UNDRIVEN || m_e == UNUSEDGENVAR || m_e == UNUSEDPARAM
|
||||
|| m_e == UNUSEDSIGNAL || m_e == VARHIDDEN);
|
||||
}
|
||||
// Warnings that are unused only
|
||||
bool unusedError() const VL_MT_SAFE {
|
||||
|
|
@ -335,7 +339,7 @@ public:
|
|||
v3errorEnd(
|
||||
(v3errorPrep(V3ErrorCode::EC_FATALEXIT),
|
||||
(v3errorStr() << "Exiting due to too many errors encountered; --error-limit="
|
||||
<< errorCount() << endl),
|
||||
<< errorCount() << std::endl),
|
||||
v3errorStr()));
|
||||
assert(0); // LCOV_EXCL_LINE
|
||||
VL_UNREACHABLE;
|
||||
|
|
@ -588,12 +592,12 @@ inline void v3errorEndFatal(std::ostringstream& sstr)
|
|||
#define UINFO(level, stmsg) \
|
||||
do { \
|
||||
if (VL_UNCOVERABLE(debug() >= (level))) { \
|
||||
cout << "- " << V3Error::lineStr(__FILE__, __LINE__) << stmsg; \
|
||||
std::cout << "- " << V3Error::lineStr(__FILE__, __LINE__) << stmsg; \
|
||||
} \
|
||||
} while (false)
|
||||
#define UINFONL(level, stmsg) \
|
||||
do { \
|
||||
if (VL_UNCOVERABLE(debug() >= (level))) { cout << stmsg; } \
|
||||
if (VL_UNCOVERABLE(debug() >= (level))) { std::cout << stmsg; } \
|
||||
} while (false)
|
||||
|
||||
#ifdef VL_DEBUG
|
||||
|
|
|
|||
|
|
@ -489,8 +489,8 @@ private:
|
|||
void startFilter(const string& command) {
|
||||
if (command == "") {} // Prevent Unused
|
||||
#ifdef INFILTER_PIPE
|
||||
int fd_stdin[2];
|
||||
int fd_stdout[2];
|
||||
int fd_stdin[2]; // Can't use std::array
|
||||
int fd_stdout[2]; // Can't use std::array
|
||||
constexpr int P_RD = 0;
|
||||
constexpr int P_WR = 1;
|
||||
|
||||
|
|
|
|||
|
|
@ -433,6 +433,12 @@ string FileLine::source() const VL_MT_SAFE {
|
|||
} // LCOV_EXCL_STOP
|
||||
return m_contentp->getLine(m_contentLineno);
|
||||
}
|
||||
string FileLine::sourcePrefix(int toColumn) const VL_MT_SAFE {
|
||||
const std::string src = source();
|
||||
if (toColumn > static_cast<int>(src.length())) toColumn = static_cast<int>(src.length());
|
||||
if (toColumn < 1) return "";
|
||||
return src.substr(0, toColumn - 1);
|
||||
}
|
||||
string FileLine::prettySource() const VL_MT_SAFE {
|
||||
string out = source();
|
||||
// Drop ignore trailing newline
|
||||
|
|
|
|||
|
|
@ -241,6 +241,7 @@ public:
|
|||
// as the parser errors etc generally make more sense pointing at the last parse point
|
||||
int lineno() const VL_MT_SAFE { return m_lastLineno; }
|
||||
string source() const VL_MT_SAFE;
|
||||
string sourcePrefix(int toColumn) const VL_MT_SAFE;
|
||||
string prettySource() const VL_MT_SAFE; // Source, w/stripped unprintables and newlines
|
||||
FileLine* parent() const VL_MT_SAFE { return m_parent; }
|
||||
V3LangCode language() const { return singleton().numberToLang(filenameno()); }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,245 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Create separate tasks for forked processes that
|
||||
// can outlive their parents
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2023 by Wilson Snyder. This program is free software; you
|
||||
// can redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
// V3Fork's Transformations:
|
||||
//
|
||||
// Each module:
|
||||
// Look for FORKs [JOIN_NONE]/[JOIN_ANY]
|
||||
// FORK(stmts) -> TASK(stmts), FORK(TASKREF(inits))
|
||||
//
|
||||
// FORKs that spawn tasks which might outlive their parents require those
|
||||
// tasks to carry their own frames and as such they require their own
|
||||
// variable scopes.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Fork.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3AstNodeExpr.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Fork visitor, transforms asynchronous blocks into separate tasks
|
||||
|
||||
class ForkVisitor final : public VNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// AstNode::user1() -> bool, 1 = Node was created as a call to an asynchronous task
|
||||
// AstVarRef::user2() -> bool, 1 = Node is a class handle reference. The handle gets
|
||||
// modified in the context of this reference.
|
||||
const VNUser1InUse m_inuser1;
|
||||
const VNUser2InUse m_inuser2;
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp = nullptr; // Class/module we are currently under
|
||||
int m_forkDepth = 0; // Nesting level of asynchronous forks
|
||||
bool m_newProcess = false; // True if we are directly under an asynchronous fork.
|
||||
AstVar* m_capturedVarsp = nullptr; // Local copies of captured variables
|
||||
std::set<AstVar*> m_forkLocalsp; // Variables local to a given fork
|
||||
AstArg* m_capturedVarRefsp = nullptr; // References to captured variables (as args)
|
||||
int m_createdTasksCount = 0; // Number of tasks created by this visitor
|
||||
|
||||
// METHODS
|
||||
AstVar* captureRef(AstNodeExpr* refp) {
|
||||
AstVar* varp = nullptr;
|
||||
for (varp = m_capturedVarsp; varp; varp = VN_AS(varp->nextp(), Var))
|
||||
if (varp->name() == refp->name()) break;
|
||||
if (!varp) {
|
||||
// Create a local copy for a capture
|
||||
varp = new AstVar{refp->fileline(), VVarType::BLOCKTEMP, refp->name(), refp->dtypep()};
|
||||
varp->direction(VDirection::INPUT);
|
||||
varp->funcLocal(true);
|
||||
varp->lifetime(VLifetime::AUTOMATIC);
|
||||
m_capturedVarsp = AstNode::addNext(m_capturedVarsp, varp);
|
||||
// Use the original ref as an argument for call
|
||||
AstArg* arg = new AstArg{refp->fileline(), refp->name(), refp->cloneTree(false)};
|
||||
m_capturedVarRefsp = AstNode::addNext(m_capturedVarRefsp, arg);
|
||||
}
|
||||
return varp;
|
||||
}
|
||||
|
||||
AstTask* makeTask(FileLine* fl, AstNode* stmtsp, std::string name) {
|
||||
stmtsp = AstNode::addNext(static_cast<AstNode*>(m_capturedVarsp), stmtsp);
|
||||
AstTask* const taskp = new AstTask{fl, name, stmtsp};
|
||||
++m_createdTasksCount;
|
||||
return taskp;
|
||||
}
|
||||
|
||||
std::string generateTaskName(AstNode* fromp, std::string kind) {
|
||||
// TODO: Ensure no collisions occur
|
||||
return "__V" + kind + (!fromp->name().empty() ? (fromp->name() + "__") : "UNNAMED__")
|
||||
+ cvtToHex(fromp);
|
||||
}
|
||||
|
||||
void visitTaskifiable(AstNode* nodep) {
|
||||
if (!m_newProcess || nodep->user1()) {
|
||||
VL_RESTORER(m_forkDepth);
|
||||
if (nodep->user1()) {
|
||||
UASSERT(m_forkDepth > 0, "Wrong fork depth!");
|
||||
--m_forkDepth;
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
return;
|
||||
}
|
||||
|
||||
VL_RESTORER(m_capturedVarsp);
|
||||
VL_RESTORER(m_capturedVarRefsp);
|
||||
VL_RESTORER(m_newProcess);
|
||||
m_capturedVarsp = nullptr;
|
||||
m_capturedVarRefsp = nullptr;
|
||||
m_newProcess = false;
|
||||
|
||||
iterateChildren(nodep);
|
||||
|
||||
// If there are no captures, there's no need to taskify
|
||||
if (m_forkLocalsp.empty() && (m_capturedVarsp == nullptr) && !v3Global.opt.fTaskifyAll())
|
||||
return;
|
||||
|
||||
VNRelinker handle;
|
||||
AstTask* taskp = nullptr;
|
||||
|
||||
if (AstBegin* beginp = VN_CAST(nodep, Begin)) {
|
||||
UASSERT(beginp->stmtsp(), "No stmtsp\n");
|
||||
const std::string taskName = generateTaskName(beginp, "__FORK_BEGIN_");
|
||||
taskp
|
||||
= makeTask(beginp->fileline(), beginp->stmtsp()->unlinkFrBackWithNext(), taskName);
|
||||
beginp->unlinkFrBack(&handle);
|
||||
VL_DO_DANGLING(beginp->deleteTree(), beginp);
|
||||
} else if (AstNodeStmt* stmtp = VN_CAST(nodep, NodeStmt)) {
|
||||
const std::string taskName = generateTaskName(stmtp, "__FORK_STMT_");
|
||||
taskp = makeTask(stmtp->fileline(), stmtp->unlinkFrBack(&handle), taskName);
|
||||
} else if (AstFork* forkp = VN_CAST(nodep, Fork)) {
|
||||
const std::string taskName = generateTaskName(forkp, "__FORK_NESTED_");
|
||||
taskp = makeTask(forkp->fileline(), forkp->unlinkFrBack(&handle), taskName);
|
||||
}
|
||||
|
||||
m_modp->addStmtsp(taskp);
|
||||
|
||||
AstTaskRef* const taskrefp
|
||||
= new AstTaskRef{nodep->fileline(), taskp->name(), m_capturedVarRefsp};
|
||||
AstStmtExpr* const taskcallp = new AstStmtExpr{nodep->fileline(), taskrefp};
|
||||
// Replaced nodes will be revisited, so we don't need to "lift" the arguments
|
||||
// as captures in case of nested forks.
|
||||
handle.relink(taskcallp);
|
||||
taskcallp->user1(true);
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstFork* nodep) override {
|
||||
bool nested = m_newProcess;
|
||||
|
||||
VL_RESTORER(m_forkLocalsp);
|
||||
VL_RESTORER(m_newProcess);
|
||||
VL_RESTORER(m_forkDepth)
|
||||
if (!nodep->joinType().join()) {
|
||||
++m_forkDepth;
|
||||
m_newProcess = true;
|
||||
m_forkLocalsp.clear();
|
||||
// Nested forks get moved into separate tasks
|
||||
if (nested) {
|
||||
visitTaskifiable(nodep);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
m_newProcess = false;
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstBegin* nodep) override { visitTaskifiable(nodep); }
|
||||
void visit(AstNodeStmt* nodep) override { visitTaskifiable(nodep); }
|
||||
void visit(AstVar* nodep) override {
|
||||
if (m_forkDepth) m_forkLocalsp.insert(nodep);
|
||||
}
|
||||
void visit(AstVarRef* nodep) override {
|
||||
|
||||
// VL_KEEP_THIS ensures that we hold a handle to the class
|
||||
if (m_forkDepth && !nodep->varp()->isFuncLocal() && nodep->varp()->isClassMember()) return;
|
||||
|
||||
if (m_forkDepth && (m_forkLocalsp.count(nodep->varp()) == 0)
|
||||
&& !nodep->varp()->lifetime().isStatic()) {
|
||||
if (nodep->access().isWriteOrRW()
|
||||
&& (!nodep->isClassHandleValue() || nodep->user2())) {
|
||||
nodep->v3warn(
|
||||
E_LIFETIME,
|
||||
"Invalid reference: Process might outlive variable `"
|
||||
<< nodep->varp()->name() << "`.\n"
|
||||
<< nodep->varp()->warnMore()
|
||||
<< "... Suggest use it as read-only to initialize a local copy at the "
|
||||
"beginning of the process, or declare it as static. It is also "
|
||||
"possible to refer by reference to objects and their members.");
|
||||
return;
|
||||
}
|
||||
UASSERT_OBJ(
|
||||
!nodep->varp()->lifetime().isNone(), nodep,
|
||||
"Variable's lifetime is unknown. Can't determine if a capture is necessary.");
|
||||
|
||||
AstVar* const varp = captureRef(nodep);
|
||||
nodep->varp(varp);
|
||||
}
|
||||
}
|
||||
void visit(AstAssign* nodep) override {
|
||||
if (VN_IS(nodep->lhsp(), VarRef) && nodep->lhsp()->isClassHandleValue()) {
|
||||
nodep->lhsp()->user2(true);
|
||||
}
|
||||
visit(static_cast<AstNodeStmt*>(nodep));
|
||||
}
|
||||
void visit(AstThisRef* nodep) override { return; }
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
m_modp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNode* nodep) override {
|
||||
VL_RESTORER(m_newProcess)
|
||||
VL_RESTORER(m_forkDepth);
|
||||
if (nodep->user1()) --m_forkDepth;
|
||||
m_newProcess = false;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
ForkVisitor(AstNetlist* nodep) { visit(nodep); }
|
||||
~ForkVisitor() override = default;
|
||||
|
||||
// UTILITY
|
||||
int createdTasksCount() { return m_createdTasksCount; }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Fork class functions
|
||||
|
||||
int V3Fork::makeTasks(AstNetlist* nodep) {
|
||||
int createdTasksCount;
|
||||
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{
|
||||
ForkVisitor fork_visitor(nodep);
|
||||
createdTasksCount = fork_visitor.createdTasksCount();
|
||||
}
|
||||
V3Global::dumpCheckGlobalTree("fork", 0, dumpTreeLevel() >= 3);
|
||||
|
||||
return createdTasksCount;
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Create separate tasks for forked processes that
|
||||
// can outlive their parents
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2023 by Wilson Snyder. This program is free software; you
|
||||
// can redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_V3FORK_H_
|
||||
#define VERILATOR_V3FORK_H_
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
class AstNetlist;
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3Fork final {
|
||||
public:
|
||||
// Create tasks out of begin blocks that can outlive processes in which they were forked.
|
||||
// Return value: number of tasks created
|
||||
static int makeTasks(AstNetlist* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
|
@ -176,9 +176,7 @@ public:
|
|||
, m_slow{slow} {}
|
||||
~GateLogicVertex() override = default;
|
||||
// ACCESSORS
|
||||
string name() const override VL_MT_STABLE {
|
||||
return (cvtToHex(m_nodep) + "@" + scopep()->prettyName());
|
||||
}
|
||||
string name() const override { return (cvtToHex(m_nodep) + "@" + scopep()->prettyName()); }
|
||||
string dotColor() const override { return "purple"; }
|
||||
FileLine* fileline() const override { return nodep()->fileline(); }
|
||||
AstNode* nodep() const { return m_nodep; }
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@
|
|||
|
||||
// clang-format off
|
||||
#include "config_build.h"
|
||||
#ifndef HAVE_CONFIG_BUILD
|
||||
# error "Something failed during ./configure as config_build.h is incomplete. Perhaps you used autoreconf, don't."
|
||||
#ifndef HAVE_CONFIG_PACKAGE
|
||||
# error "Something failed during ./configure as config_package.h is incomplete. Perhaps you used autoreconf, don't."
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
|
|
|
|||
|
|
@ -384,7 +384,6 @@ private:
|
|||
nodep->v3fatalSrc("Null connection?");
|
||||
}
|
||||
}
|
||||
nodep->name(nodep->varp()->name());
|
||||
}
|
||||
void visit(AstVarXRef* nodep) override {
|
||||
// Track what scope it was originally under so V3LinkDot can resolve it
|
||||
|
|
|
|||
|
|
@ -147,12 +147,6 @@ private:
|
|||
iterateAndNextConstNull(nodep->lsbp());
|
||||
iterateAndNextConstNull(nodep->widthp());
|
||||
}
|
||||
void visit(AstSliceSel* nodep) override { // LCOV_EXCL_LINE
|
||||
nodep->v3fatalSrc("AstSliceSel unhandled");
|
||||
}
|
||||
void visit(AstMemberSel* nodep) override { // LCOV_EXCL_LINE
|
||||
nodep->v3fatalSrc("AstMemberSel unhandled");
|
||||
}
|
||||
void visit(AstConcat* nodep) override {
|
||||
if (m_ignoreRemaining) return;
|
||||
// Nop.
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@
|
|||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Graph.h"
|
||||
#include "V3MemberMap.h"
|
||||
#include "V3String.h"
|
||||
#include "V3SymTable.h"
|
||||
|
||||
|
|
@ -215,6 +216,7 @@ public:
|
|||
VSymGraph* symsp() { return &m_syms; }
|
||||
int stepNumber() const { return int(m_step); }
|
||||
bool forPrimary() const { return m_step == LDS_PRIMARY; }
|
||||
bool forParamed() const { return m_step == LDS_PARAMED; }
|
||||
bool forPrearray() const { return m_step == LDS_PARAMED || m_step == LDS_PRIMARY; }
|
||||
bool forScopeCreation() const { return m_step == LDS_SCOPED; }
|
||||
|
||||
|
|
@ -751,6 +753,8 @@ class LinkDotFindVisitor final : public VNVisitor {
|
|||
// METHODS
|
||||
void makeImplicitNew(AstClass* nodep) {
|
||||
AstFunc* const newp = new AstFunc{nodep->fileline(), "new", nullptr, nullptr};
|
||||
// If needed, super.new() call is added the 2nd pass of V3LinkDotResolve,
|
||||
// because base classes are already resolved there.
|
||||
newp->isConstructor(true);
|
||||
nodep->addMembersp(newp);
|
||||
UINFO(8, "Made implicit new for " << nodep->name() << ": " << nodep << endl);
|
||||
|
|
@ -2025,6 +2029,7 @@ private:
|
|||
// (except the default instances)
|
||||
// They are added to the set only in linkDotPrimary.
|
||||
bool m_insideClassExtParam = false; // Inside a class from m_extendsParam
|
||||
bool m_explicitSuperNew = false; // Hit a "super.new" call inside a "new" function
|
||||
|
||||
struct DotStates {
|
||||
DotPosition m_dotPos; // Scope part of dotted resolution
|
||||
|
|
@ -2060,40 +2065,38 @@ private:
|
|||
} m_ds; // State to preserve across recursions
|
||||
|
||||
// METHODS - Variables
|
||||
void createImplicitVar(VSymEnt* /*lookupSymp*/, AstVarRef* nodep, AstNodeModule* modp,
|
||||
VSymEnt* moduleSymp, bool noWarn) {
|
||||
AstVar* createImplicitVar(VSymEnt* /*lookupSymp*/, AstParseRef* nodep, AstNodeModule* modp,
|
||||
VSymEnt* moduleSymp, bool noWarn) {
|
||||
// Create implicit after warning
|
||||
if (!nodep->varp()) {
|
||||
if (!noWarn) {
|
||||
if (nodep->fileline()->warnIsOff(V3ErrorCode::I_DEF_NETTYPE_WIRE)) {
|
||||
const string suggest = m_statep->suggestSymFallback(moduleSymp, nodep->name(),
|
||||
LinkNodeMatcherVar{});
|
||||
nodep->v3error("Signal definition not found, and implicit disabled with "
|
||||
"`default_nettype: "
|
||||
<< nodep->prettyNameQ() << '\n'
|
||||
<< (suggest.empty() ? "" : nodep->warnMore() + suggest));
|
||||
if (!noWarn) {
|
||||
if (nodep->fileline()->warnIsOff(V3ErrorCode::I_DEF_NETTYPE_WIRE)) {
|
||||
const string suggest = m_statep->suggestSymFallback(moduleSymp, nodep->name(),
|
||||
LinkNodeMatcherVar{});
|
||||
nodep->v3error("Signal definition not found, and implicit disabled with "
|
||||
"`default_nettype: "
|
||||
<< nodep->prettyNameQ() << '\n'
|
||||
<< (suggest.empty() ? "" : nodep->warnMore() + suggest));
|
||||
|
||||
}
|
||||
// Bypass looking for suggestions if IMPLICIT is turned off
|
||||
// as there could be thousands of these suppressed in large netlists
|
||||
else if (!nodep->fileline()->warnIsOff(V3ErrorCode::IMPLICIT)) {
|
||||
const string suggest = m_statep->suggestSymFallback(moduleSymp, nodep->name(),
|
||||
LinkNodeMatcherVar{});
|
||||
nodep->v3warn(IMPLICIT,
|
||||
"Signal definition not found, creating implicitly: "
|
||||
<< nodep->prettyNameQ() << '\n'
|
||||
<< (suggest.empty() ? "" : nodep->warnMore() + suggest));
|
||||
}
|
||||
}
|
||||
AstVar* const newp = new AstVar{nodep->fileline(), VVarType::WIRE, nodep->name(),
|
||||
VFlagLogicPacked{}, 1};
|
||||
newp->trace(modp->modTrace());
|
||||
nodep->varp(newp);
|
||||
modp->addStmtsp(newp);
|
||||
// Link it to signal list, must add the variable under the module;
|
||||
// current scope might be lower now
|
||||
m_statep->insertSym(moduleSymp, newp->name(), newp, nullptr /*classOrPackagep*/);
|
||||
// Bypass looking for suggestions if IMPLICIT is turned off
|
||||
// as there could be thousands of these suppressed in large netlists
|
||||
else if (!nodep->fileline()->warnIsOff(V3ErrorCode::IMPLICIT)) {
|
||||
const string suggest = m_statep->suggestSymFallback(moduleSymp, nodep->name(),
|
||||
LinkNodeMatcherVar{});
|
||||
nodep->v3warn(IMPLICIT,
|
||||
"Signal definition not found, creating implicitly: "
|
||||
<< nodep->prettyNameQ() << '\n'
|
||||
<< (suggest.empty() ? "" : nodep->warnMore() + suggest));
|
||||
}
|
||||
}
|
||||
AstVar* const newp
|
||||
= new AstVar{nodep->fileline(), VVarType::WIRE, nodep->name(), VFlagLogicPacked{}, 1};
|
||||
newp->trace(modp->modTrace());
|
||||
modp->addStmtsp(newp);
|
||||
// Link it to signal list, must add the variable under the module;
|
||||
// current scope might be lower now
|
||||
m_statep->insertSym(moduleSymp, newp->name(), newp, nullptr /*classOrPackagep*/);
|
||||
return newp;
|
||||
}
|
||||
AstVar* foundToVarp(const VSymEnt* symp, AstNode* nodep, VAccess access) {
|
||||
// Return a variable if possible, auto converting a modport to variable
|
||||
|
|
@ -2112,6 +2115,25 @@ private:
|
|||
return nullptr;
|
||||
}
|
||||
}
|
||||
AstNodeStmt* addImplicitSuperNewCall(AstFunc* nodep) {
|
||||
// Returns the added node
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstDot* const superNewp
|
||||
= new AstDot{fl, false, new AstParseRef{fl, VParseRefExp::PX_ROOT, "super"},
|
||||
new AstNew{fl, nullptr}};
|
||||
AstNodeStmt* const superNewStmtp = superNewp->makeStmt();
|
||||
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
// super.new shall be the first statement (section 8.15 of IEEE Std 1800-2017)
|
||||
// but some nodes (such as variable declarations and typedefs) should stay before
|
||||
if (VN_IS(stmtp, NodeStmt)) {
|
||||
stmtp->addHereThisAsNext(superNewStmtp);
|
||||
return superNewStmtp;
|
||||
}
|
||||
}
|
||||
// There were no statements
|
||||
nodep->addStmtsp(superNewStmtp);
|
||||
return superNewStmtp;
|
||||
}
|
||||
void taskFuncSwapCheck(AstNodeFTaskRef* nodep) {
|
||||
if (nodep->taskp() && VN_IS(nodep->taskp(), Task) && VN_IS(nodep, FuncRef)) {
|
||||
nodep->v3error("Illegal call of a task as a function: " << nodep->prettyNameQ());
|
||||
|
|
@ -2120,7 +2142,7 @@ private:
|
|||
void checkNoDot(AstNode* nodep) {
|
||||
if (VL_UNLIKELY(m_ds.m_dotPos != DP_NONE)) {
|
||||
// UINFO(9, "ds=" << m_ds.ascii() << endl);
|
||||
nodep->v3error("Syntax Error: Not expecting " << nodep->type() << " under a "
|
||||
nodep->v3error("Syntax error: Not expecting " << nodep->type() << " under a "
|
||||
<< nodep->backp()->type()
|
||||
<< " in dotted expression");
|
||||
m_ds.m_dotErr = true;
|
||||
|
|
@ -2820,11 +2842,11 @@ private:
|
|||
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
|
||||
auto varp = createImplicitVar(m_curSymp, nodep, m_modp, m_modSymp, err);
|
||||
AstVarRef* const newp
|
||||
= new AstVarRef{nodep->fileline(), nodep->name(), VAccess::READ};
|
||||
= new AstVarRef{nodep->fileline(), varp, VAccess::READ};
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
createImplicitVar(m_curSymp, newp, m_modp, m_modSymp, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3026,7 +3048,8 @@ private:
|
|||
}
|
||||
UASSERT_OBJ(cpackagerefp->classOrPackagep(), m_ds.m_dotp->lhsp(), "Bad package link");
|
||||
nodep->classOrPackagep(cpackagerefp->classOrPackagep());
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
// Class/package :: HERE function() . method_called_on_function_return_value()
|
||||
m_ds.m_dotPos = DP_MEMBER;
|
||||
m_ds.m_dotp = nullptr;
|
||||
} else if (m_ds.m_dotp && m_ds.m_dotPos == DP_FINAL) {
|
||||
nodep->dotted(m_ds.m_dotText); // Maybe ""
|
||||
|
|
@ -3129,7 +3152,15 @@ private:
|
|||
} else if (nodep->name() == "randomize" || nodep->name() == "srandom"
|
||||
|| nodep->name() == "get_randstate"
|
||||
|| nodep->name() == "set_randstate") {
|
||||
// Resolved in V3Width
|
||||
if (AstClass* const classp = VN_CAST(m_modp, Class)) {
|
||||
nodep->classOrPackagep(classp);
|
||||
} else {
|
||||
nodep->v3error("Calling implicit class method "
|
||||
<< nodep->prettyNameQ() << " without being under class");
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), 0});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
} else if (nodep->dotted() == "") {
|
||||
if (nodep->pli()) {
|
||||
if (v3Global.opt.bboxSys()) {
|
||||
|
|
@ -3215,7 +3246,7 @@ private:
|
|||
if (nodep->user3SetOnce()) return;
|
||||
if (m_ds.m_dotPos
|
||||
== DP_SCOPE) { // Already under dot, so this is {modulepart} DOT {modulepart}
|
||||
nodep->v3error("Syntax Error: Range ':', '+:' etc are not allowed in the instance "
|
||||
nodep->v3error("Syntax error: Range ':', '+:' etc are not allowed in the instance "
|
||||
"part of a dotted reference");
|
||||
m_ds.m_dotErr = true;
|
||||
return;
|
||||
|
|
@ -3273,7 +3304,16 @@ private:
|
|||
{
|
||||
m_ftaskp = nodep;
|
||||
m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep);
|
||||
const bool isNew = nodep->name() == "new";
|
||||
if (isNew) m_explicitSuperNew = false;
|
||||
iterateChildren(nodep);
|
||||
if (isNew && !m_explicitSuperNew && m_statep->forParamed()) {
|
||||
const AstClassExtends* const classExtendsp = VN_AS(m_modp, Class)->extendsp();
|
||||
if (classExtendsp && classExtendsp->classOrNullp()) {
|
||||
AstNodeStmt* const superNewp = addImplicitSuperNewCall(VN_AS(nodep, Func));
|
||||
iterate(superNewp);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_ds.m_dotSymp = m_curSymp = oldCurSymp;
|
||||
m_ftaskp = nullptr;
|
||||
|
|
@ -3389,6 +3429,7 @@ private:
|
|||
checkNoDot(nodep);
|
||||
VL_RESTORER(m_curSymp);
|
||||
VL_RESTORER(m_modSymp);
|
||||
VL_RESTORER(m_modp);
|
||||
VL_RESTORER(m_ifClassImpNames);
|
||||
VL_RESTORER(m_insideClassExtParam);
|
||||
{
|
||||
|
|
@ -3457,15 +3498,16 @@ private:
|
|||
// V3Width when determines types needs to find enum values and such
|
||||
// so add members pointing to appropriate enum values
|
||||
{
|
||||
nodep->repairCache();
|
||||
VMemberMap memberMap;
|
||||
for (VSymEnt::const_iterator it = m_curSymp->begin(); it != m_curSymp->end(); ++it) {
|
||||
AstNode* const itemp = it->second->nodep();
|
||||
if (!nodep->findMember(it->first)) {
|
||||
if (!memberMap.findMember(nodep, it->first)) {
|
||||
if (AstEnumItem* const aitemp = VN_CAST(itemp, EnumItem)) {
|
||||
AstEnumItemRef* const newp = new AstEnumItemRef{
|
||||
aitemp->fileline(), aitemp, it->second->classOrPackagep()};
|
||||
UINFO(8, "Class import noderef '" << it->first << "' " << newp << endl);
|
||||
nodep->addMembersp(newp);
|
||||
memberMap.insert(nodep, newp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3595,6 +3637,19 @@ private:
|
|||
UINFO(5, " AstCellArrayRef: " << nodep << " " << m_ds.ascii() << endl);
|
||||
// No need to iterate, if we have a UnlinkedVarXRef, we're already done
|
||||
}
|
||||
void visit(AstStmtExpr* nodep) override {
|
||||
checkNoDot(nodep);
|
||||
// Check if nodep represents a super.new call;
|
||||
if (VN_IS(nodep->exprp(), New)) {
|
||||
// in this case it was already linked, so it doesn't have a super reference
|
||||
m_explicitSuperNew = true;
|
||||
} else if (const AstDot* const dotp = VN_CAST(nodep->exprp(), Dot)) {
|
||||
if (dotp->lhsp()->name() == "super" && VN_IS(dotp->rhsp(), New)) {
|
||||
m_explicitSuperNew = true;
|
||||
}
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstNode* nodep) override {
|
||||
checkNoDot(nodep);
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ private:
|
|||
bool m_setContinuously = false; // Set that var has some continuous assignment
|
||||
bool m_setStrengthSpecified = false; // Set that var has assignment with strength specified.
|
||||
VAccess m_setRefLvalue; // Set VarRefs to lvalues for pin assignments
|
||||
AstNodeFTask* m_ftaskp = nullptr; // Function or task we're inside
|
||||
|
||||
// VISITs
|
||||
// Result handing
|
||||
|
|
@ -55,7 +54,8 @@ private:
|
|||
// so it is needed to check only if m_setContinuously is true
|
||||
if (m_setStrengthSpecified) nodep->varp()->hasStrengthAssignment(true);
|
||||
}
|
||||
if (nodep->access().isWriteOrRW() && !m_ftaskp && nodep->varp()->isReadOnly()) {
|
||||
if (nodep->access().isWriteOrRW() && !nodep->varp()->isFuncLocal()
|
||||
&& nodep->varp()->isReadOnly()) {
|
||||
nodep->v3warn(ASSIGNIN,
|
||||
"Assigning to input/const variable: " << nodep->prettyNameQ());
|
||||
}
|
||||
|
|
@ -275,9 +275,14 @@ private:
|
|||
iterateAndNextNull(nodep->thsp());
|
||||
}
|
||||
}
|
||||
void visit(AstNodeFTask* nodep) override {
|
||||
VL_RESTORER(m_ftaskp);
|
||||
m_ftaskp = nodep;
|
||||
void visit(AstMemberSel* nodep) override {
|
||||
if (m_setRefLvalue != VAccess::NOCHANGE) {
|
||||
nodep->access(m_setRefLvalue);
|
||||
} else {
|
||||
// It is the only place where the access is set to member select nodes.
|
||||
// If it doesn't have to be set to WRITE, it means that it is READ.
|
||||
nodep->access(VAccess::READ);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNodeFTaskRef* nodep) override {
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@ private:
|
|||
// without suppressing other token's messages as a side effect.
|
||||
// We could have verilog.l create a new one on every token,
|
||||
// but that's a lot more structures than only doing AST nodes.
|
||||
// TODO: Many places copy the filename when suppressing warnings,
|
||||
// perhaps audit to make consistent and this is no longer needed
|
||||
if (m_filelines.find(nodep->fileline()) != m_filelines.end()) {
|
||||
nodep->fileline(new FileLine{nodep->fileline()});
|
||||
}
|
||||
|
|
@ -125,6 +127,58 @@ private:
|
|||
&& !nodep->stmtsp()->nextp()); // Has only one item
|
||||
}
|
||||
|
||||
void checkIndent(AstNode* nodep, AstNode* childp) {
|
||||
// Try very hard to avoid false positives
|
||||
AstNode* nextp = nodep->nextp();
|
||||
if (!childp) return;
|
||||
if (!nextp && VN_IS(nodep, While) && VN_IS(nodep->backp(), Begin))
|
||||
nextp = nodep->backp()->nextp();
|
||||
if (!nextp) return;
|
||||
if (VN_IS(childp, Begin)) return;
|
||||
FileLine* const nodeFlp = nodep->fileline();
|
||||
FileLine* const childFlp = childp->fileline();
|
||||
FileLine* const nextFlp = nextp->fileline();
|
||||
// UINFO(0, "checkInd " << nodeFlp->firstColumn() << " " << nodep << endl);
|
||||
// UINFO(0, " child " << childFlp->firstColumn() << " " << childp << endl);
|
||||
// UINFO(0, " next " << nextFlp->firstColumn() << " " << nextp << endl);
|
||||
// Same filename, later line numbers (no macro magic going on)
|
||||
if (nodeFlp->filenameno() != childFlp->filenameno()) return;
|
||||
if (nodeFlp->filenameno() != nextFlp->filenameno()) return;
|
||||
if (nodeFlp->lastLineno() >= childFlp->firstLineno()) return;
|
||||
if (childFlp->lastLineno() >= nextFlp->firstLineno()) return;
|
||||
// This block has indent 'a'
|
||||
// Child block has indent 'b' where indent('b') > indent('a')
|
||||
// Next block has indent 'b'
|
||||
// (note similar code below)
|
||||
if (!(nodeFlp->firstColumn() < childFlp->firstColumn()
|
||||
&& nextFlp->firstColumn() >= childFlp->firstColumn()))
|
||||
return;
|
||||
// Might be a tab difference in spaces up to the node prefix, if so
|
||||
// just ignore this warning
|
||||
// Note it's correct we look at nodep's column in all of these
|
||||
const std::string nodePrefix = nodeFlp->sourcePrefix(nodeFlp->firstColumn());
|
||||
const std::string childPrefix = childFlp->sourcePrefix(nodeFlp->firstColumn());
|
||||
const std::string nextPrefix = nextFlp->sourcePrefix(nodeFlp->firstColumn());
|
||||
if (childPrefix != nodePrefix) return;
|
||||
if (nextPrefix != childPrefix) return;
|
||||
// Some file lines start after the indentation, so make another check
|
||||
// using actual file contents
|
||||
const std::string nodeSource = nodeFlp->source();
|
||||
const std::string childSource = childFlp->source();
|
||||
const std::string nextSource = nextFlp->source();
|
||||
if (!(VString::leadingWhitespaceCount(nodeSource)
|
||||
< VString::leadingWhitespaceCount(childSource)
|
||||
&& VString::leadingWhitespaceCount(nextSource)
|
||||
>= VString::leadingWhitespaceCount(childSource)))
|
||||
return;
|
||||
nextp->v3warn(MISINDENT,
|
||||
"Misleading indentation\n"
|
||||
<< nextp->warnContextPrimary() << '\n'
|
||||
<< nodep->warnOther()
|
||||
<< "... Expected indentation matching this earlier statement's line:\n"
|
||||
<< nodep->warnContextSecondary());
|
||||
}
|
||||
|
||||
// VISITs
|
||||
void visit(AstNodeFTask* nodep) override {
|
||||
if (!nodep->user1SetOnce()) { // Process only once.
|
||||
|
|
@ -316,9 +370,11 @@ private:
|
|||
// Making an AstAssign (vs AstAssignW) to a wire is an error, suppress it
|
||||
FileLine* const newfl = new FileLine{fl};
|
||||
newfl->warnOff(V3ErrorCode::PROCASSWIRE, true);
|
||||
auto* const assp
|
||||
= new AstAssign{newfl, new AstVarRef{newfl, nodep->name(), VAccess::WRITE},
|
||||
VN_AS(nodep->valuep()->unlinkFrBack(), NodeExpr)};
|
||||
// Create a ParseRef to the wire. We cannot use the var as it may be deleted if
|
||||
// it's a port (see t_var_set_link.v)
|
||||
auto* const assp = new AstAssign{
|
||||
newfl, new AstParseRef{newfl, VParseRefExp::PX_TEXT, nodep->name()},
|
||||
VN_AS(nodep->valuep()->unlinkFrBack(), NodeExpr)};
|
||||
if (nodep->lifetime().isAutomatic()) {
|
||||
nodep->addNextHere(new AstInitialAutomatic{newfl, assp});
|
||||
} else {
|
||||
|
|
@ -327,7 +383,7 @@ private:
|
|||
} // 4. Under blocks, it's an initial value to be under an assign
|
||||
else {
|
||||
nodep->addNextHere(
|
||||
new AstAssign{fl, new AstVarRef{fl, nodep->name(), VAccess::WRITE},
|
||||
new AstAssign{fl, new AstVarRef{fl, nodep, VAccess::WRITE},
|
||||
VN_AS(nodep->valuep()->unlinkFrBack(), NodeExpr)});
|
||||
}
|
||||
}
|
||||
|
|
@ -476,6 +532,7 @@ private:
|
|||
void visit(AstForeach* nodep) override {
|
||||
// FOREACH(array, loopvars, body)
|
||||
UINFO(9, "FOREACH " << nodep << endl);
|
||||
cleanFileline(nodep);
|
||||
// Separate iteration vars from base from variable
|
||||
// Input:
|
||||
// v--- arrayp
|
||||
|
|
@ -510,13 +567,16 @@ private:
|
|||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstRepeat* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
VL_RESTORER(m_insideLoop);
|
||||
{
|
||||
m_insideLoop = true;
|
||||
checkIndent(nodep, nodep->stmtsp());
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstDoWhile* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
VL_RESTORER(m_insideLoop);
|
||||
{
|
||||
m_insideLoop = true;
|
||||
|
|
@ -524,9 +584,11 @@ private:
|
|||
}
|
||||
}
|
||||
void visit(AstWhile* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
VL_RESTORER(m_insideLoop);
|
||||
{
|
||||
m_insideLoop = true;
|
||||
checkIndent(nodep, nodep->stmtsp());
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
|
|
@ -580,14 +642,25 @@ private:
|
|||
// The genblk name will get attached to the if true/false LOWER begin block(s)
|
||||
const bool nestedIf = nestedIfBegin(nodep);
|
||||
// It's not FOR(BEGIN(...)) but we earlier changed it to BEGIN(FOR(...))
|
||||
int assignGenBlkNum = -1;
|
||||
if (nodep->genforp()) {
|
||||
++m_genblkNum;
|
||||
if (nodep->name() == "") nodep->name("genblk" + cvtToStr(m_genblkNum));
|
||||
if (nodep->name() == "") assignGenBlkNum = m_genblkNum;
|
||||
} else if (nodep->generate() && nodep->name() == "" && assignGenBlkNum == -1
|
||||
&& (VN_IS(backp, CaseItem) || VN_IS(backp, GenIf)) && !nestedIf) {
|
||||
assignGenBlkNum = m_genblkAbove;
|
||||
}
|
||||
if (nodep->generate() && nodep->name() == ""
|
||||
&& (VN_IS(backp, CaseItem) || VN_IS(backp, GenIf)) && !nestedIf) {
|
||||
nodep->name("genblk" + cvtToStr(m_genblkAbove));
|
||||
if (assignGenBlkNum != -1) {
|
||||
nodep->name("genblk" + cvtToStr(assignGenBlkNum));
|
||||
if (nodep->stmtsp()) {
|
||||
nodep->v3warn(GENUNNAMED,
|
||||
"Unnamed generate block "
|
||||
<< nodep->prettyNameQ() << " (IEEE 1800-2017 27.6)"
|
||||
<< nodep->warnMore()
|
||||
<< "... Suggest assign a label with 'begin : gen_<label_name>'");
|
||||
}
|
||||
}
|
||||
|
||||
if (nodep->name() != "") {
|
||||
VL_RESTORER(m_genblkAbove);
|
||||
VL_RESTORER(m_genblkNum);
|
||||
|
|
@ -611,6 +684,7 @@ private:
|
|||
}
|
||||
void visit(AstGenIf* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
checkIndent(nodep, nodep->elsesp() ? nodep->elsesp() : nodep->thensp());
|
||||
const bool nestedIf
|
||||
= (VN_IS(nodep->backp(), Begin) && nestedIfBegin(VN_CAST(nodep->backp(), Begin)));
|
||||
if (nestedIf) {
|
||||
|
|
@ -655,6 +729,11 @@ private:
|
|||
}
|
||||
}
|
||||
}
|
||||
void visit(AstIf* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
checkIndent(nodep, nodep->elsesp() ? nodep->elsesp() : nodep->thensp());
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstPrintTimeScale* nodep) override {
|
||||
// Inlining may change hierarchy, so just save timescale where needed
|
||||
cleanFileline(nodep);
|
||||
|
|
@ -683,6 +762,7 @@ private:
|
|||
nodep->timeunit(m_modp->timeunit());
|
||||
}
|
||||
void visit(AstTimeUnit* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
iterateChildren(nodep);
|
||||
nodep->timeunit(m_modp->timeunit());
|
||||
}
|
||||
|
|
@ -711,6 +791,7 @@ private:
|
|||
}
|
||||
}
|
||||
void visit(AstClocking* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
VL_RESTORER(m_defaultInSkewp);
|
||||
VL_RESTORER(m_defaultOutSkewp);
|
||||
// Find default input and output skews
|
||||
|
|
@ -745,6 +826,7 @@ private:
|
|||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstClockingItem* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
if (nodep->direction() == VDirection::OUTPUT) {
|
||||
if (!nodep->skewp()) {
|
||||
if (m_defaultOutSkewp) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Member names for classes/structs
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2023 by Wilson Snyder. This program is free software; you
|
||||
// can redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// For a class or struct, create a map of names of each member.
|
||||
// Used for faster lookup to avoid O(n^2) processing times.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_V3MEMBERMAP_H_
|
||||
#define VERILATOR_V3MEMBERMAP_H_
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
//######################################################################
|
||||
|
||||
class VMemberMap final {
|
||||
// MEMBERS
|
||||
using MemberMap = std::map<std::string, AstNode*>;
|
||||
using NodeMap = std::map<const AstNode*, MemberMap>;
|
||||
NodeMap m_map; // Map of nodes being tracked
|
||||
public:
|
||||
void clear() { m_map.clear(); }
|
||||
// Find 'name' under 'nodep', caching nodep's children if needed
|
||||
AstNode* findMember(const AstNode* nodep, const std::string& name) {
|
||||
auto nit = m_map.find(nodep);
|
||||
if (VL_UNLIKELY(nit == m_map.end())) {
|
||||
scan(nodep);
|
||||
nit = m_map.find(nodep);
|
||||
}
|
||||
const auto mit = nit->second.find(name);
|
||||
if (mit == nit->second.end()) return nullptr;
|
||||
return mit->second;
|
||||
}
|
||||
// Insert under the given parent node the child's name
|
||||
void insert(const AstNode* nodep, AstNode* childp) {
|
||||
auto nit = m_map.find(nodep);
|
||||
if (nit == m_map.end()) {
|
||||
scan(nodep);
|
||||
nit = m_map.find(nodep);
|
||||
}
|
||||
memberInsert(nit->second, childp, false);
|
||||
}
|
||||
|
||||
private:
|
||||
void scan(const AstNode* nodep) {
|
||||
const auto nitPair = m_map.emplace(nodep, MemberMap{});
|
||||
MemberMap& mmapr = nitPair.first->second;
|
||||
if (const AstClass* const anodep = VN_CAST(nodep, Class)) {
|
||||
for (AstNode* itemp = anodep->membersp(); itemp; itemp = itemp->nextp()) {
|
||||
if (const AstScope* const scopep = VN_CAST(itemp, Scope)) {
|
||||
for (AstNode* blockp = scopep->blocksp(); blockp; blockp = blockp->nextp()) {
|
||||
if (AstClass::isCacheableChild(blockp)) memberInsert(mmapr, blockp);
|
||||
}
|
||||
} else {
|
||||
if (AstClass::isCacheableChild(itemp)) memberInsert(mmapr, itemp);
|
||||
}
|
||||
}
|
||||
} else if (const AstNodeUOrStructDType* const anodep
|
||||
= VN_CAST(nodep, NodeUOrStructDType)) {
|
||||
for (AstNode* itemp = anodep->membersp(); itemp; itemp = itemp->nextp()) {
|
||||
memberInsert(mmapr, itemp);
|
||||
}
|
||||
} else {
|
||||
nodep->v3fatalSrc("Unsupported node type");
|
||||
}
|
||||
}
|
||||
void memberInsert(MemberMap& mmapr, AstNode* childp, bool warn = true) {
|
||||
const auto mitPair = mmapr.emplace(childp->name(), childp);
|
||||
if (VL_UNCOVERABLE(!mitPair.second && warn)) {
|
||||
// Probably an internal error, but we'll make it user friendly if happens
|
||||
childp->v3error("Duplicate declaration of member name: " << childp->prettyNameQ());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // guard
|
||||
|
|
@ -109,6 +109,10 @@ public:
|
|||
bool try_lock() VL_TRY_ACQUIRE(true) VL_MT_SAFE {
|
||||
return V3MutexConfig::s().enable() ? m_mutex.try_lock() : true;
|
||||
}
|
||||
/// Assume that the mutex is already held. Purely for Clang thread safety analyzer.
|
||||
void assumeLocked() VL_ASSERT_CAPABILITY(this) VL_MT_SAFE {}
|
||||
/// Pretend that the mutex is being unlocked. Purely for Clang thread safety analyzer.
|
||||
void pretendUnlock() VL_RELEASE() VL_MT_SAFE {}
|
||||
/// 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.
|
||||
|
|
|
|||
|
|
@ -89,12 +89,7 @@ private:
|
|||
rename(nodep, false);
|
||||
}
|
||||
}
|
||||
void visit(AstVarRef* nodep) override {
|
||||
if (nodep->varp()) {
|
||||
iterate(nodep->varp());
|
||||
nodep->name(nodep->varp()->name());
|
||||
}
|
||||
}
|
||||
void visit(AstVarRef* nodep) override { iterate(nodep->varp()); }
|
||||
void visit(AstCell* nodep) override {
|
||||
if (!nodep->user1()) {
|
||||
rename(nodep, (!nodep->modp()->modPublic() && !VN_IS(nodep->modp(), ClassPackage)));
|
||||
|
|
|
|||
|
|
@ -1866,12 +1866,15 @@ V3Number& V3Number::opAdd(const V3Number& lhs, const V3Number& rhs) {
|
|||
if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX();
|
||||
setZero();
|
||||
// Addem
|
||||
int carry = 0;
|
||||
for (int bit = 0; bit < width(); bit++) {
|
||||
const int sum = ((lhs.bitIs1(bit) ? 1 : 0) + (rhs.bitIs1(bit) ? 1 : 0) + carry);
|
||||
if (sum & 1) setBit(bit, 1);
|
||||
carry = (sum >= 2);
|
||||
uint64_t carry = 0;
|
||||
for (int word = 0; word < words(); word++) {
|
||||
const uint64_t lwordval = lhs.m_data.num()[word].m_value;
|
||||
const uint64_t rwordval = rhs.m_data.num()[word].m_value;
|
||||
const uint64_t sum = lwordval + rwordval + carry;
|
||||
m_data.num()[word].m_value = sum & 0xffffffffULL;
|
||||
carry = sum > 0xffffffffULL;
|
||||
}
|
||||
opCleanThis(); // Just in case it produced extra bits in result
|
||||
return *this;
|
||||
}
|
||||
V3Number& V3Number::opSub(const V3Number& lhs, const V3Number& rhs) {
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ class VOptionBool;
|
|||
// if (int consumed = parser.parse(i, argc, argv)) {
|
||||
// i += consumed;
|
||||
// } else { // error
|
||||
// cerr << parser.getSuggestion(argv[i]) << endl;
|
||||
// std::cerr << parser.getSuggestion(argv[i]) << std::endl;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
|
|
|
|||
|
|
@ -896,7 +896,7 @@ void V3Options::notify() {
|
|||
// V3 Options accessors
|
||||
|
||||
string V3Options::version() VL_PURE {
|
||||
string ver = DTVERSION;
|
||||
string ver = PACKAGE_STRING;
|
||||
ver += " rev " + cvtToStr(DTVERSION_rev);
|
||||
return ver;
|
||||
}
|
||||
|
|
@ -1224,6 +1224,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
DECL_OPTION("-fsubst", FOnOff, &m_fSubst);
|
||||
DECL_OPTION("-fsubst-const", FOnOff, &m_fSubstConst);
|
||||
DECL_OPTION("-ftable", FOnOff, &m_fTable);
|
||||
DECL_OPTION("-ftaskify-all-forked", FOnOff, &m_fTaskifyAll).undocumented(); // Debug
|
||||
|
||||
DECL_OPTION("-G", CbPartialMatch, [this](const char* optp) { addParameter(optp, false); });
|
||||
DECL_OPTION("-gate-stmts", Set, &m_gateStmts);
|
||||
|
|
|
|||
|
|
@ -374,6 +374,7 @@ private:
|
|||
bool m_fSubst; // main switch: -fno-subst: substitute expression temp values
|
||||
bool m_fSubstConst; // main switch: -fno-subst-const: final constant substitution
|
||||
bool m_fTable; // main switch: -fno-table: lookup table creation
|
||||
bool m_fTaskifyAll = false; // main switch: --ftaskify-all-forked
|
||||
// clang-format on
|
||||
|
||||
bool m_available = false; // Set to true at the end of option parsing
|
||||
|
|
@ -633,6 +634,7 @@ public:
|
|||
bool fSubst() const { return m_fSubst; }
|
||||
bool fSubstConst() const { return m_fSubstConst; }
|
||||
bool fTable() const { return m_fTable; }
|
||||
bool fTaskifyAll() const { return m_fTaskifyAll; }
|
||||
|
||||
string traceClassBase() const { return m_traceFormat.classBase(); }
|
||||
string traceClassLang() const { return m_traceFormat.classBase() + (systemC() ? "Sc" : "C"); }
|
||||
|
|
|
|||
|
|
@ -394,6 +394,11 @@ class ParamProcessor final : public VNDeleter {
|
|||
}
|
||||
return nullptr;
|
||||
}
|
||||
bool isString(AstNodeDType* nodep) {
|
||||
if (AstBasicDType* const basicp = VN_CAST(nodep->skipRefToEnump(), BasicDType))
|
||||
return basicp->isString();
|
||||
return false;
|
||||
}
|
||||
void collectPins(CloneMap* clonemapp, AstNodeModule* modp, bool originalIsCopy) {
|
||||
// Grab all I/O so we can remap our pins later
|
||||
for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
|
|
@ -670,6 +675,19 @@ class ParamProcessor final : public VNDeleter {
|
|||
return modInfop;
|
||||
}
|
||||
|
||||
void convertToStringp(AstNode* nodep) {
|
||||
// Should be called on values of parameters of type string to convert them
|
||||
// to properly typed string constants.
|
||||
// Has no effect if the value is not a string constant.
|
||||
AstConst* const constp = VN_CAST(nodep, Const);
|
||||
// Check if it wasn't already converted
|
||||
if (constp && !constp->num().isString()) {
|
||||
constp->replaceWith(
|
||||
new AstConst{constp->fileline(), AstConst::String{}, constp->num().toString()});
|
||||
constp->deleteTree();
|
||||
}
|
||||
}
|
||||
|
||||
void cellPinCleanup(AstNode* nodep, AstPin* pinp, AstNodeModule* srcModp, string& longnamer,
|
||||
bool& any_overridesr) {
|
||||
if (!pinp->exprp()) return; // No-connect
|
||||
|
|
@ -684,8 +702,16 @@ class ParamProcessor final : public VNDeleter {
|
|||
any_overridesr = true;
|
||||
} else {
|
||||
V3Const::constifyParamsEdit(pinp->exprp());
|
||||
// String constants are parsed as logic arrays and converted to strings in V3Const.
|
||||
// At this moment, some constants may have been already converted.
|
||||
// To correctly compare constants, both should be of the same type,
|
||||
// so they need to be converted.
|
||||
if (isString(modvarp->subDTypep())) {
|
||||
convertToStringp(pinp->exprp());
|
||||
convertToStringp(modvarp->valuep());
|
||||
}
|
||||
AstConst* const exprp = VN_CAST(pinp->exprp(), Const);
|
||||
const AstConst* const origp = VN_CAST(modvarp->valuep(), Const);
|
||||
AstConst* const origp = VN_CAST(modvarp->valuep(), Const);
|
||||
if (!exprp) {
|
||||
if (debug()) pinp->dumpTree("- ");
|
||||
pinp->v3error("Can't convert defparam value to constant: Param "
|
||||
|
|
@ -709,7 +735,7 @@ class ParamProcessor final : public VNDeleter {
|
|||
}
|
||||
} else if (AstParamTypeDType* const modvarp = pinp->modPTypep()) {
|
||||
AstNodeDType* const exprp = VN_CAST(pinp->exprp(), NodeDType);
|
||||
const AstNodeDType* const origp = modvarp->subDTypep();
|
||||
const AstNodeDType* const origp = modvarp->skipRefToEnump();
|
||||
if (!exprp) {
|
||||
pinp->v3error("Parameter type pin value isn't a type: Param "
|
||||
<< pinp->prettyNameQ() << " of " << nodep->prettyNameQ());
|
||||
|
|
|
|||
|
|
@ -85,9 +85,10 @@ AstArg* V3ParseGrammar::argWrapList(AstNodeExpr* nodep) {
|
|||
}
|
||||
|
||||
AstNode* V3ParseGrammar::createSupplyExpr(FileLine* fileline, const string& name, int value) {
|
||||
AstAssignW* assignp = new AstAssignW{fileline, new AstVarRef{fileline, name, VAccess::WRITE},
|
||||
value ? new AstConst{fileline, AstConst::All1{}}
|
||||
: new AstConst{fileline, AstConst::All0{}}};
|
||||
AstAssignW* assignp
|
||||
= new AstAssignW{fileline, new AstParseRef{fileline, VParseRefExp::PX_TEXT, name},
|
||||
value ? new AstConst{fileline, AstConst::All1{}}
|
||||
: new AstConst{fileline, AstConst::All0{}}};
|
||||
AstStrengthSpec* strengthSpecp
|
||||
= new AstStrengthSpec{fileline, VStrength::SUPPLY, VStrength::SUPPLY};
|
||||
assignp->strengthSpecp(strengthSpecp);
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ public:
|
|||
if (VL_UNCOVERABLE(symCurrentp()->nodep() != nodep)) { // LCOV_EXCL_START
|
||||
if (debug()) {
|
||||
showUpward();
|
||||
dumpSelf(cout, "-mism: ");
|
||||
dumpSelf(std::cout, "-mism: ");
|
||||
}
|
||||
nodep->v3fatalSrc("Symbols suggest ending " << symCurrentp()->nodep()->prettyTypeName()
|
||||
<< " but parser thinks ending "
|
||||
|
|
|
|||
|
|
@ -518,7 +518,7 @@ class MTaskEdge final : public V3GraphEdge, public MergeCandidate {
|
|||
// MEMBERS
|
||||
// This edge can be in 2 EdgeHeaps, one forward and one reverse. We allocate the heap nodes
|
||||
// directly within the edge as they are always required and this makes association cheap.
|
||||
EdgeHeap::Node m_edgeHeapNode[GraphWay::NUM_WAYS];
|
||||
std::array<EdgeHeap::Node, GraphWay::NUM_WAYS> m_edgeHeapNode;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
|
|
@ -1060,7 +1060,7 @@ class PartPropagateCpSelfTest final {
|
|||
private:
|
||||
// MEMBERS
|
||||
V3Graph m_graph; // A graph
|
||||
LogicMTask* m_vx[50]; // All vertices within the graph
|
||||
std::array<LogicMTask*, 50> m_vx; // All vertices within the graph
|
||||
|
||||
// CONSTRUCTORS
|
||||
PartPropagateCpSelfTest() = default;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include "V3Randomize.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3MemberMap.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
|
|
@ -138,6 +139,7 @@ private:
|
|||
const VNUser2InUse m_inuser2;
|
||||
|
||||
// STATE
|
||||
VMemberMap memberMap; // Member names cached for fast lookup
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
const AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
||||
size_t m_enumValueTabCount = 0; // Number of tables with enum values created
|
||||
|
|
@ -213,7 +215,7 @@ private:
|
|||
}
|
||||
}
|
||||
void addPrePostCall(AstClass* classp, AstFunc* funcp, const string& name) {
|
||||
if (AstTask* userFuncp = VN_CAST(classp->findMember(name), Task)) {
|
||||
if (AstTask* userFuncp = VN_CAST(memberMap.findMember(classp, name), Task)) {
|
||||
AstTaskRef* const callp
|
||||
= new AstTaskRef{userFuncp->fileline(), userFuncp->name(), nullptr};
|
||||
callp->taskp(userFuncp);
|
||||
|
|
@ -380,7 +382,8 @@ void V3Randomize::randomizeNetlist(AstNetlist* nodep) {
|
|||
}
|
||||
|
||||
AstFunc* V3Randomize::newRandomizeFunc(AstClass* nodep) {
|
||||
AstFunc* funcp = VN_AS(nodep->findMember("randomize"), Func);
|
||||
VMemberMap memberMap;
|
||||
AstFunc* funcp = VN_AS(memberMap.findMember(nodep, "randomize"), Func);
|
||||
if (!funcp) {
|
||||
v3Global.useRandomizeMethods(true);
|
||||
AstNodeDType* const dtypep
|
||||
|
|
@ -395,7 +398,6 @@ AstFunc* V3Randomize::newRandomizeFunc(AstClass* nodep) {
|
|||
funcp->classMethod(true);
|
||||
funcp->isVirtual(nodep->isExtended());
|
||||
nodep->addMembersp(funcp);
|
||||
nodep->repairCache();
|
||||
AstClass* const basep = nodep->baseMostClassp();
|
||||
basep->needRNG(true);
|
||||
}
|
||||
|
|
@ -403,8 +405,9 @@ AstFunc* V3Randomize::newRandomizeFunc(AstClass* nodep) {
|
|||
}
|
||||
|
||||
AstFunc* V3Randomize::newSRandomFunc(AstClass* nodep) {
|
||||
VMemberMap memberMap;
|
||||
AstClass* const basep = nodep->baseMostClassp();
|
||||
AstFunc* funcp = VN_AS(basep->findMember("srandom"), Func);
|
||||
AstFunc* funcp = VN_AS(memberMap.findMember(basep, "srandom"), Func);
|
||||
if (!funcp) {
|
||||
v3Global.useRandomizeMethods(true);
|
||||
AstNodeDType* const dtypep
|
||||
|
|
@ -418,7 +421,6 @@ AstFunc* V3Randomize::newSRandomFunc(AstClass* nodep) {
|
|||
funcp->classMethod(true);
|
||||
funcp->isVirtual(false);
|
||||
basep->addMembersp(funcp);
|
||||
basep->repairCache();
|
||||
funcp->addStmtsp(new AstCStmt{basep->fileline(), "__Vm_rng.srandom(seed);\n"});
|
||||
basep->needRNG(true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,8 +125,43 @@ void splitCheck(AstCFunc* ofuncp) {
|
|||
|
||||
int funcnum = 0;
|
||||
int func_stmts = 0;
|
||||
const bool is_ofuncp_coroutine = ofuncp->isCoroutine();
|
||||
AstCFunc* funcp = nullptr;
|
||||
|
||||
const auto createNewSubFuncp = [&]() {
|
||||
AstCFunc* const subFuncp = new AstCFunc{
|
||||
ofuncp->fileline(), ofuncp->name() + "__" + cvtToStr(funcnum++), ofuncp->scopep()};
|
||||
subFuncp->dontCombine(true);
|
||||
subFuncp->isStatic(false);
|
||||
subFuncp->isLoose(true);
|
||||
subFuncp->slow(ofuncp->slow());
|
||||
subFuncp->declPrivate(ofuncp->declPrivate());
|
||||
|
||||
func_stmts = 0;
|
||||
|
||||
return subFuncp;
|
||||
};
|
||||
|
||||
const auto finishSubFuncp = [&](AstCFunc* subFuncp) {
|
||||
ofuncp->scopep()->addBlocksp(subFuncp);
|
||||
AstCCall* const callp = new AstCCall{subFuncp->fileline(), subFuncp};
|
||||
callp->dtypeSetVoid();
|
||||
|
||||
if (is_ofuncp_coroutine && subFuncp->exists([](const AstCAwait*) {
|
||||
return true;
|
||||
})) { // Wrap call with co_await
|
||||
subFuncp->rtnType("VlCoroutine");
|
||||
|
||||
AstCAwait* const awaitp = new AstCAwait{subFuncp->fileline(), callp};
|
||||
awaitp->dtypeSetVoid();
|
||||
ofuncp->addStmtsp(awaitp->makeStmt());
|
||||
} else {
|
||||
ofuncp->addStmtsp(callp->makeStmt());
|
||||
}
|
||||
};
|
||||
|
||||
funcp = createNewSubFuncp();
|
||||
|
||||
// Unlink all statements, then add item by item to new sub-functions
|
||||
AstBegin* const tempp = new AstBegin{ofuncp->fileline(), "[EditWrapper]",
|
||||
ofuncp->stmtsp()->unlinkFrBackWithNext()};
|
||||
|
|
@ -135,24 +170,16 @@ void splitCheck(AstCFunc* ofuncp) {
|
|||
while (tempp->stmtsp()) {
|
||||
AstNode* const itemp = tempp->stmtsp()->unlinkFrBack();
|
||||
const int stmts = itemp->nodeCount();
|
||||
if (!funcp || (func_stmts + stmts) > v3Global.opt.outputSplitCFuncs()) {
|
||||
// Make a new function
|
||||
funcp = new AstCFunc{ofuncp->fileline(), ofuncp->name() + "__" + cvtToStr(funcnum++),
|
||||
ofuncp->scopep()};
|
||||
funcp->dontCombine(true);
|
||||
funcp->isStatic(false);
|
||||
funcp->isLoose(true);
|
||||
funcp->slow(ofuncp->slow());
|
||||
ofuncp->scopep()->addBlocksp(funcp);
|
||||
//
|
||||
AstCCall* const callp = new AstCCall{funcp->fileline(), funcp};
|
||||
callp->dtypeSetVoid();
|
||||
ofuncp->addStmtsp(callp->makeStmt());
|
||||
func_stmts = 0;
|
||||
|
||||
if ((func_stmts + stmts) > v3Global.opt.outputSplitCFuncs()) {
|
||||
finishSubFuncp(funcp);
|
||||
funcp = createNewSubFuncp();
|
||||
}
|
||||
|
||||
funcp->addStmtsp(itemp);
|
||||
func_stmts += stmts;
|
||||
}
|
||||
finishSubFuncp(funcp);
|
||||
VL_DO_DANGLING(tempp->deleteTree(), tempp);
|
||||
}
|
||||
|
||||
|
|
@ -247,7 +274,6 @@ void orderSequentially(AstCFunc* funcp, const LogicByScope& lbs) {
|
|||
subFuncp = createNewSubFuncp(scopep);
|
||||
subFuncp->name(subFuncp->name() + "__" + cvtToStr(scopep->user2Inc()));
|
||||
subFuncp->rtnType("VlCoroutine");
|
||||
if (procp->needProcess()) subFuncp->setNeedProcess();
|
||||
if (VN_IS(procp, Always)) {
|
||||
subFuncp->slow(false);
|
||||
FileLine* const flp = procp->fileline();
|
||||
|
|
@ -256,6 +282,8 @@ void orderSequentially(AstCFunc* funcp, const LogicByScope& lbs) {
|
|||
}
|
||||
}
|
||||
subFuncp->addStmtsp(bodyp);
|
||||
if (procp->needProcess()) subFuncp->setNeedProcess();
|
||||
splitCheck(subFuncp);
|
||||
}
|
||||
} else {
|
||||
logicp->unlinkFrBack();
|
||||
|
|
|
|||
|
|
@ -290,26 +290,18 @@ void transformForks(AstNetlist* const netlistp) {
|
|||
funcp->foreach([&](AstNodeVarRef* refp) {
|
||||
AstVar* const varp = refp->varp();
|
||||
AstBasicDType* const dtypep = varp->dtypep()->basicp();
|
||||
// If it a fork sync or an intra-assignment variable, pass it by value
|
||||
const bool passByValue = (dtypep && dtypep->isForkSync())
|
||||
|| VString::startsWith(varp->name(), "__Vintra");
|
||||
if (passByValue) {
|
||||
// We can just pass it to the new function
|
||||
bool passByValue = false;
|
||||
if (VString::startsWith(varp->name(), "__Vintra")) {
|
||||
// Pass it by value to the new function, as otherwise there are issues with
|
||||
// -flocalize (see t_timing_intra_assign)
|
||||
passByValue = true;
|
||||
} else if (!varp->user1() || !varp->isFuncLocal()) {
|
||||
// Not func local, or not declared before the fork. Their lifetime is longer
|
||||
// than the forked process. Skip
|
||||
return;
|
||||
} else if (m_forkp->joinType().join()) {
|
||||
// If it's fork..join, we can refer to variables from the parent process
|
||||
} else {
|
||||
// TODO: It is possible to relax this by allowing the use of such variables up
|
||||
// until the first await. Also, variables defined within a forked process
|
||||
// (inside a begin) are extracted out by V3Begin, so they also trigger this
|
||||
// error. Preventing this (or detecting such cases and moving the vars back)
|
||||
// would also allow for using them freely.
|
||||
refp->v3warn(E_UNSUPPORTED, "Unsupported: variable local to a forking process "
|
||||
"accessed in a fork..join_any or fork..join_none");
|
||||
return;
|
||||
} else if (dtypep && dtypep->isForkSync()) {
|
||||
// We can just pass it by value to the new function
|
||||
passByValue = true;
|
||||
}
|
||||
// Remap the reference
|
||||
AstVarScope* const vscp = refp->varScopep();
|
||||
|
|
|
|||
|
|
@ -232,7 +232,13 @@ public:
|
|||
}
|
||||
|
||||
std::vector<AstNodeStmt*> getAndClearInits() { return std::move(m_inits); }
|
||||
std::vector<AstVar*> getAndClearLocals() { return std::move(m_locals); }
|
||||
|
||||
std::vector<AstVar*> getAndClearLocals() {
|
||||
// With m_locals empty, m_prev and m_curr are no longer valid
|
||||
m_prev.clear();
|
||||
m_curr.clear();
|
||||
return std::move(m_locals);
|
||||
}
|
||||
|
||||
std::vector<AstNodeStmt*> getAndClearPreUpdates() {
|
||||
m_hasPreUpdate.clear();
|
||||
|
|
|
|||
|
|
@ -178,8 +178,8 @@ public:
|
|||
m_whyNotNodep = nodep;
|
||||
if (debug() >= 5) {
|
||||
UINFO(0, "Clear optimizable: " << why);
|
||||
if (nodep) cout << ": " << nodep;
|
||||
cout << endl;
|
||||
if (nodep) std::cout << ": " << nodep;
|
||||
std::cout << std::endl;
|
||||
}
|
||||
m_whyNotOptimizable = why;
|
||||
std::ostringstream stack;
|
||||
|
|
|
|||
|
|
@ -109,9 +109,7 @@ protected:
|
|||
// ACCESSORS
|
||||
// Do not make accessor for nodep(), It may change due to
|
||||
// reordering a lower block, but we don't repair it
|
||||
string name() const override VL_MT_STABLE {
|
||||
return cvtToHex(m_nodep) + ' ' + m_nodep->prettyTypeName();
|
||||
}
|
||||
string name() const override { return cvtToHex(m_nodep) + ' ' + m_nodep->prettyTypeName(); }
|
||||
FileLine* fileline() const override { return nodep()->fileline(); }
|
||||
|
||||
public:
|
||||
|
|
@ -148,7 +146,7 @@ public:
|
|||
SplitVarPostVertex(V3Graph* graphp, AstNode* nodep)
|
||||
: SplitNodeVertex{graphp, nodep} {}
|
||||
~SplitVarPostVertex() override = default;
|
||||
string name() const override VL_MT_STABLE { return string{"POST "} + SplitNodeVertex::name(); }
|
||||
string name() const override { return string{"POST "} + SplitNodeVertex::name(); }
|
||||
string dotColor() const override { return "CadetBlue"; }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -158,6 +158,15 @@ bool VString::isWhitespace(const string& str) {
|
|||
return true;
|
||||
}
|
||||
|
||||
string::size_type VString::leadingWhitespaceCount(const string& str) {
|
||||
string::size_type result = 0;
|
||||
for (const char c : str) {
|
||||
++result;
|
||||
if (!std::isspace(c)) break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
double VString::parseDouble(const string& str, bool* successp) {
|
||||
char* const strgp = new char[str.size() + 1];
|
||||
char* dp = strgp;
|
||||
|
|
|
|||
|
|
@ -113,6 +113,8 @@ public:
|
|||
static string removeWhitespace(const string& str);
|
||||
// Return true if only whitespace or ""
|
||||
static bool isWhitespace(const string& str);
|
||||
// Return number of spaces/tabs leading in string
|
||||
static string::size_type leadingWhitespaceCount(const string& str);
|
||||
// Return double by parsing string
|
||||
static double parseDouble(const string& str, bool* successp);
|
||||
// Replace all occurrences of the word 'from' in 'str' with 'to'. A word is considered
|
||||
|
|
|
|||
|
|
@ -123,7 +123,8 @@ public:
|
|||
<< " " << entp->nodep() << endl);
|
||||
if (name != "" && m_idNameMap.find(name) != m_idNameMap.end()) {
|
||||
if (!V3Error::errorCount()) { // Else may have just reported warning
|
||||
if (debug() >= 9 || V3Error::debugDefault()) dumpSelf(cout, "- err-dump: ", 1);
|
||||
if (debug() >= 9 || V3Error::debugDefault())
|
||||
dumpSelf(std::cout, "- err-dump: ", 1);
|
||||
entp->nodep()->v3fatalSrc("Inserting two symbols with same name: " << name);
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -350,7 +350,6 @@ private:
|
|||
// TYPES
|
||||
enum InsertMode : uint8_t {
|
||||
IM_BEFORE, // Pointing at statement ref is in, insert before this
|
||||
IM_AFTER, // Pointing at last inserted stmt, insert after
|
||||
IM_WHILE_PRECOND // Pointing to for loop, add to body end
|
||||
};
|
||||
using DpiCFuncs = std::map<const string, std::tuple<AstNodeFTask*, std::string, AstCFunc*>>;
|
||||
|
|
@ -413,7 +412,6 @@ private:
|
|||
AstVarScope* const newvscp = VN_AS(refp->varp()->user2p(), VarScope);
|
||||
refp->varScopep(newvscp);
|
||||
refp->varp(refp->varScopep()->varp());
|
||||
refp->name(refp->varp()->name());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -657,8 +655,15 @@ private:
|
|||
args += ", ";
|
||||
dpiproto += ", ";
|
||||
}
|
||||
// Include both the Verilator and C type names, as if either
|
||||
// differ we may get C compilation problems later
|
||||
const std::string dpiType = portp->dpiArgType(false, false);
|
||||
dpiproto += dpiType;
|
||||
const std::string vType = portp->dtypep()->prettyDTypeName();
|
||||
if (!portp->isDpiOpenArray() && dpiType != vType) {
|
||||
dpiproto += " /* " + vType + " */ ";
|
||||
}
|
||||
args += portp->name(); // Leftover so ,'s look nice
|
||||
if (nodep->dpiImport()) dpiproto += portp->dpiArgType(false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -929,15 +934,15 @@ private:
|
|||
m_dpiNames.emplace(nodep->cname(), std::make_tuple(nodep, signature, funcp));
|
||||
return funcp;
|
||||
} else {
|
||||
// Seen this cname before. Check if it's the same prototype.
|
||||
// Seen this cname import before. Check if it's the same prototype.
|
||||
const AstNodeFTask* firstNodep;
|
||||
string firstSignature;
|
||||
AstCFunc* firstFuncp;
|
||||
std::tie(firstNodep, firstSignature, firstFuncp) = it->second;
|
||||
if (signature != firstSignature) {
|
||||
// Different signature, so error.
|
||||
nodep->v3error("Duplicate declaration of DPI function with different signature: "
|
||||
<< nodep->prettyNameQ() << '\n'
|
||||
nodep->v3error("Duplicate declaration of DPI function with different signature: '"
|
||||
<< nodep->cname() << "'\n"
|
||||
<< nodep->warnContextPrimary() << '\n'
|
||||
<< nodep->warnMore() //
|
||||
<< "... New signature: " << signature << '\n' //
|
||||
|
|
@ -1093,7 +1098,7 @@ private:
|
|||
if (nodep->isFunction()) {
|
||||
AstVar* const portp = VN_AS(nodep->fvarp(), Var);
|
||||
UASSERT_OBJ(portp, nodep, "function without function output variable");
|
||||
if (!portp->isFuncReturn()) nodep->v3error("Not marked as function return var");
|
||||
UASSERT_OBJ(portp->isFuncReturn(), nodep, "Not marked as function return var");
|
||||
if (nodep->dpiImport() || nodep->dpiExport()) {
|
||||
AstBasicDType* const bdtypep = portp->dtypep()->basicp();
|
||||
if (!bdtypep->isDpiPrimitive()) {
|
||||
|
|
@ -1108,7 +1113,7 @@ private:
|
|||
"other than a single 'logic' (IEEE 1800-2017 35.5.5)");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if (nodep->taskPublic()) {
|
||||
if (portp->isWide()) {
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Public functions with return > 64 bits wide.\n"
|
||||
|
|
@ -1317,7 +1322,7 @@ private:
|
|||
}
|
||||
|
||||
// Mark the fact that this function allocates std::process
|
||||
if (nodep->isFromStd() && nodep->name() == "self") cfuncp->setNeedProcess();
|
||||
if (nodep->needProcess()) cfuncp->setNeedProcess();
|
||||
|
||||
// Delete rest of cloned task and return new func
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
|
|
@ -1339,16 +1344,13 @@ private:
|
|||
AstNode* insertBeforeStmt(AstNode* nodep, AstNode* newp) {
|
||||
// Return node that must be visited, if any
|
||||
if (debug() >= 9) nodep->dumpTree("- newstmt: ");
|
||||
UASSERT_OBJ(m_insStmtp, nodep, "Function not underneath a statement");
|
||||
UASSERT_OBJ(m_insStmtp, nodep, "Function call not underneath a statement");
|
||||
AstNode* visitp = nullptr;
|
||||
if (m_insMode == IM_BEFORE) {
|
||||
// Add the whole thing before insertAt
|
||||
UINFO(5, " IM_Before " << m_insStmtp << endl);
|
||||
if (debug() >= 9) newp->dumpTree("- newfunc: ");
|
||||
m_insStmtp->addHereThisAsNext(newp);
|
||||
} else if (m_insMode == IM_AFTER) {
|
||||
UINFO(5, " IM_After " << m_insStmtp << endl);
|
||||
m_insStmtp->addNextHere(newp);
|
||||
} else if (m_insMode == IM_WHILE_PRECOND) {
|
||||
UINFO(5, " IM_While_Precond " << m_insStmtp << endl);
|
||||
AstWhile* const whilep = VN_AS(m_insStmtp, While);
|
||||
|
|
@ -1358,8 +1360,6 @@ private:
|
|||
} else {
|
||||
nodep->v3fatalSrc("Unknown InsertMode");
|
||||
}
|
||||
m_insMode = IM_AFTER;
|
||||
m_insStmtp = newp;
|
||||
return visitp;
|
||||
}
|
||||
|
||||
|
|
@ -1539,6 +1539,13 @@ private:
|
|||
iterateChildren(nodep);
|
||||
m_insStmtp = nullptr; // Next thing should be new statement
|
||||
}
|
||||
void visit(AstVar* nodep) override {
|
||||
if (nodep->isFuncLocal() && nodep->direction() == VDirection::INPUT && nodep->valuep()) {
|
||||
// It's the default value of optional argument.
|
||||
// Such values are added to function calls on this stage and aren't needed here.
|
||||
pushDeletep(nodep->valuep()->unlinkFrBack());
|
||||
}
|
||||
}
|
||||
void visit(AstStmtExpr* nodep) override {
|
||||
m_insMode = IM_BEFORE;
|
||||
m_insStmtp = nodep;
|
||||
|
|
@ -1651,7 +1658,14 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp)
|
|||
<< portp->prettyNameQ() << " in function call to "
|
||||
<< nodep->taskp()->prettyTypeName());
|
||||
newvaluep = new AstConst{nodep->fileline(), AstConst::Unsized32{}, 0};
|
||||
} else if (!VN_IS(portp->valuep(), Const)) {
|
||||
} else if (AstFuncRef* const funcRefp = VN_CAST(portp->valuep(), FuncRef)) {
|
||||
const AstNodeFTask* const funcp = funcRefp->taskp();
|
||||
if (funcp->classMethod() && funcp->lifetime().isStatic()) newvaluep = funcRefp;
|
||||
} else if (AstConst* const constp = VN_CAST(portp->valuep(), Const)) {
|
||||
newvaluep = constp;
|
||||
}
|
||||
|
||||
if (!newvaluep) {
|
||||
// The default value for this port might be a constant
|
||||
// expression that hasn't been folded yet. Try folding it
|
||||
// now; we don't have much to lose if it fails.
|
||||
|
|
@ -1665,12 +1679,9 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp)
|
|||
<< portp->prettyNameQ() << " in function call to "
|
||||
<< nodep->taskp()->prettyTypeName());
|
||||
newvaluep = new AstConst{nodep->fileline(), AstConst::Unsized32{}, 0};
|
||||
} else {
|
||||
newvaluep = newvaluep->cloneTree(true);
|
||||
}
|
||||
} else {
|
||||
newvaluep = VN_AS(portp->valuep(), NodeExpr)->cloneTree(true);
|
||||
}
|
||||
newvaluep = newvaluep->cloneTree(true);
|
||||
// To avoid problems with callee needing to know to deleteTree
|
||||
// or not, we make this into a pin
|
||||
UINFO(9, "Default pin for " << portp << endl);
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ void V3ThreadPool::workerJobLoop(int id) VL_MT_SAFE {
|
|||
while (true) {
|
||||
// Wait for a notification
|
||||
waitIfStopRequested();
|
||||
job_t job;
|
||||
VAnyPackagedTask job;
|
||||
{
|
||||
V3LockGuard lock(m_mutex);
|
||||
m_cv.wait(m_mutex, [&]() VL_REQUIRES(m_mutex) {
|
||||
|
|
@ -72,7 +72,7 @@ void V3ThreadPool::workerJobLoop(int id) VL_MT_SAFE {
|
|||
// Get the job
|
||||
UASSERT(!m_queue.empty(), "Job should be available");
|
||||
|
||||
job = m_queue.front();
|
||||
job = std::move(m_queue.front());
|
||||
m_queue.pop();
|
||||
}
|
||||
|
||||
|
|
@ -81,40 +81,6 @@ void V3ThreadPool::workerJobLoop(int id) VL_MT_SAFE {
|
|||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void V3ThreadPool::pushJob<void>(std::shared_ptr<std::promise<void>>& prom,
|
||||
std::function<void()>&& f) VL_MT_SAFE {
|
||||
if (willExecuteSynchronously()) {
|
||||
f();
|
||||
prom->set_value();
|
||||
} else {
|
||||
const V3LockGuard lock{m_mutex};
|
||||
m_queue.push([prom, f] {
|
||||
f();
|
||||
prom->set_value();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void V3ThreadPool::requestExclusiveAccess(const V3ThreadPool::job_t&& exclusiveAccessJob)
|
||||
VL_MT_SAFE {
|
||||
if (willExecuteSynchronously()) {
|
||||
exclusiveAccessJob();
|
||||
} else {
|
||||
V3LockGuard stoppedJobLock{m_stoppedJobsMutex};
|
||||
// if some other job already requested exclusive access
|
||||
// wait until it stops
|
||||
if (stopRequested()) { waitStopRequested(); }
|
||||
m_stopRequested = true;
|
||||
waitOtherThreads();
|
||||
m_exclusiveAccess = true;
|
||||
exclusiveAccessJob();
|
||||
m_exclusiveAccess = false;
|
||||
m_stopRequested = false;
|
||||
m_stoppedJobsCV.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
bool V3ThreadPool::waitIfStopRequested() VL_MT_SAFE VL_EXCLUDES(m_stoppedJobsMutex) {
|
||||
if (!stopRequested()) return false;
|
||||
V3LockGuard stoppedJobLock(m_stoppedJobsMutex);
|
||||
|
|
@ -150,11 +116,10 @@ void V3ThreadPool::selfTest() {
|
|||
|
||||
auto firstJob = [&](int sleep) -> void {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{sleep});
|
||||
s().requestExclusiveAccess([&]() {
|
||||
commonValue = 10;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{sleep + 10});
|
||||
UASSERT(commonValue == 10, "unexpected commonValue = " << commonValue);
|
||||
});
|
||||
const V3ThreadPool::ScopedExclusiveAccess exclusiveAccess;
|
||||
commonValue = 10;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{sleep + 10});
|
||||
UASSERT(commonValue == 10, "unexpected commonValue = " << commonValue);
|
||||
};
|
||||
auto secondJob = [&](int sleep) -> void {
|
||||
commonMutex.lock();
|
||||
|
|
@ -175,19 +140,19 @@ void V3ThreadPool::selfTest() {
|
|||
};
|
||||
std::list<std::future<void>> futures;
|
||||
|
||||
futures.push_back(s().enqueue<void>(std::bind(firstJob, 100)));
|
||||
futures.push_back(s().enqueue<void>(std::bind(secondJob, 100)));
|
||||
futures.push_back(s().enqueue<void>(std::bind(firstJob, 100)));
|
||||
futures.push_back(s().enqueue<void>(std::bind(secondJob, 100)));
|
||||
futures.push_back(s().enqueue<void>(std::bind(secondJob, 200)));
|
||||
futures.push_back(s().enqueue<void>(std::bind(firstJob, 200)));
|
||||
futures.push_back(s().enqueue<void>(std::bind(firstJob, 300)));
|
||||
futures.push_back(s().enqueue(std::bind(firstJob, 100)));
|
||||
futures.push_back(s().enqueue(std::bind(secondJob, 100)));
|
||||
futures.push_back(s().enqueue(std::bind(firstJob, 100)));
|
||||
futures.push_back(s().enqueue(std::bind(secondJob, 100)));
|
||||
futures.push_back(s().enqueue(std::bind(secondJob, 200)));
|
||||
futures.push_back(s().enqueue(std::bind(firstJob, 200)));
|
||||
futures.push_back(s().enqueue(std::bind(firstJob, 300)));
|
||||
while (!futures.empty()) {
|
||||
s().waitForFuture(futures.front());
|
||||
futures.pop_front();
|
||||
}
|
||||
futures.push_back(s().enqueue<void>(std::bind(thirdJob, 100)));
|
||||
futures.push_back(s().enqueue<void>(std::bind(thirdJob, 100)));
|
||||
futures.push_back(s().enqueue(std::bind(thirdJob, 100)));
|
||||
futures.push_back(s().enqueue(std::bind(thirdJob, 100)));
|
||||
V3ThreadPool::waitForFutures(futures);
|
||||
|
||||
s().waitIfStopRequested();
|
||||
|
|
@ -195,7 +160,7 @@ void V3ThreadPool::selfTest() {
|
|||
|
||||
auto forthJob = [&]() -> int { return 1234; };
|
||||
std::list<std::future<int>> futuresInt;
|
||||
futuresInt.push_back(s().enqueue<int>(forthJob));
|
||||
futuresInt.push_back(s().enqueue(forthJob));
|
||||
auto result = V3ThreadPool::waitForFutures(futuresInt);
|
||||
UASSERT(result.back() == 1234, "unexpected future result = " << result.back());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,13 +29,53 @@
|
|||
|
||||
//============================================================================
|
||||
|
||||
// Callable, type-erased wrapper for std::packaged_task<Signature> with any Signature.
|
||||
class VAnyPackagedTask final {
|
||||
// TYPES
|
||||
struct PTWrapperBase {
|
||||
virtual ~PTWrapperBase() {}
|
||||
virtual void operator()() = 0;
|
||||
};
|
||||
|
||||
template <typename Signature>
|
||||
struct PTWrapper final : PTWrapperBase {
|
||||
std::packaged_task<Signature> m_pt;
|
||||
|
||||
PTWrapper(std::packaged_task<Signature>&& pt)
|
||||
: m_pt(std::move(pt)) {}
|
||||
|
||||
void operator()() final override { m_pt(); }
|
||||
};
|
||||
|
||||
// MEMBERS
|
||||
std::unique_ptr<PTWrapperBase> m_ptWrapperp = nullptr; // Wrapper to call
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
template <typename Signature>
|
||||
VAnyPackagedTask(std::packaged_task<Signature>&& pt)
|
||||
: m_ptWrapperp{std::make_unique<PTWrapper<Signature>>(std::move(pt))} {}
|
||||
|
||||
VAnyPackagedTask() = default;
|
||||
~VAnyPackagedTask() = default;
|
||||
|
||||
VAnyPackagedTask(const VAnyPackagedTask&) = delete;
|
||||
VAnyPackagedTask& operator=(const VAnyPackagedTask&) = delete;
|
||||
|
||||
VAnyPackagedTask(VAnyPackagedTask&&) = default;
|
||||
VAnyPackagedTask& operator=(VAnyPackagedTask&&) = default;
|
||||
|
||||
// METHODS
|
||||
// Call the wrapped function
|
||||
void operator()() { (*m_ptWrapperp)(); }
|
||||
};
|
||||
|
||||
class V3ThreadPool final {
|
||||
// MEMBERS
|
||||
static constexpr unsigned int FUTUREWAITFOR_MS = 100;
|
||||
using job_t = std::function<void()>;
|
||||
|
||||
mutable V3Mutex m_mutex; // Mutex for use by m_queue
|
||||
std::queue<job_t> m_queue VL_GUARDED_BY(m_mutex); // Queue of jobs
|
||||
V3Mutex m_mutex; // Mutex for use by m_queue
|
||||
std::queue<VAnyPackagedTask> m_queue VL_GUARDED_BY(m_mutex); // Queue of jobs
|
||||
// We don't need to guard this condition_variable as
|
||||
// both `notify_one` and `notify_all` functions are atomic,
|
||||
// `wait` function is not atomic, but we are guarding `m_queue` that is
|
||||
|
|
@ -62,6 +102,9 @@ class V3ThreadPool final {
|
|||
}
|
||||
|
||||
public:
|
||||
// Request exclusive access to processing for the object lifetime.
|
||||
class ScopedExclusiveAccess;
|
||||
|
||||
// METHODS
|
||||
// Singleton
|
||||
static V3ThreadPool& s() VL_MT_SAFE {
|
||||
|
|
@ -79,8 +122,8 @@ public:
|
|||
// will call it. `VL_MT_START` here indicates that
|
||||
// every function call inside this `std::function` requires
|
||||
// annotations.
|
||||
template <typename T>
|
||||
std::future<T> enqueue(std::function<T()>&& f) VL_MT_START;
|
||||
template <typename Callable>
|
||||
auto enqueue(Callable&& f) VL_MT_START;
|
||||
|
||||
// Request exclusive access to processing.
|
||||
// It sends request to stop all other threads and waits for them to stop.
|
||||
|
|
@ -88,7 +131,9 @@ public:
|
|||
// they can be stopped.
|
||||
// When all other threads are stopped, this function executes the job
|
||||
// and resumes execution of other jobs.
|
||||
void requestExclusiveAccess(const job_t&& exclusiveAccessJob) VL_MT_SAFE;
|
||||
template <typename Callable>
|
||||
void requestExclusiveAccess(Callable&& exclusiveAccessJob) VL_MT_SAFE
|
||||
VL_EXCLUDES(m_stoppedJobsMutex);
|
||||
|
||||
// Check if other thread requested exclusive access to processing,
|
||||
// if so, it waits for it to complete. Afterwards it is resumed.
|
||||
|
|
@ -151,14 +196,39 @@ private:
|
|||
// Waits until all other jobs are stopped
|
||||
void waitOtherThreads() VL_MT_SAFE_EXCLUDES(m_mutex) VL_REQUIRES(m_stoppedJobsMutex);
|
||||
|
||||
template <typename T>
|
||||
void pushJob(std::shared_ptr<std::promise<T>>& prom, std::function<T()>&& f) VL_MT_SAFE;
|
||||
|
||||
void workerJobLoop(int id) VL_MT_SAFE;
|
||||
|
||||
static void startWorker(V3ThreadPool* selfThreadp, int id) VL_MT_SAFE;
|
||||
};
|
||||
|
||||
class VL_SCOPED_CAPABILITY V3ThreadPool::ScopedExclusiveAccess final {
|
||||
public:
|
||||
ScopedExclusiveAccess() VL_ACQUIRE(V3ThreadPool::s().m_stoppedJobsMutex) VL_MT_SAFE {
|
||||
if (!V3ThreadPool::s().willExecuteSynchronously()) {
|
||||
V3ThreadPool::s().m_stoppedJobsMutex.lock();
|
||||
|
||||
if (V3ThreadPool::s().stopRequested()) { V3ThreadPool::s().waitStopRequested(); }
|
||||
V3ThreadPool::s().m_stopRequested = true;
|
||||
V3ThreadPool::s().waitOtherThreads();
|
||||
V3ThreadPool::s().m_exclusiveAccess = true;
|
||||
} else {
|
||||
V3ThreadPool::s().m_stoppedJobsMutex.assumeLocked();
|
||||
}
|
||||
}
|
||||
~ScopedExclusiveAccess() VL_RELEASE(V3ThreadPool::s().m_stoppedJobsMutex) VL_MT_SAFE {
|
||||
// Can't use `willExecuteSynchronously`, we're still in exclusive execution state.
|
||||
if (V3ThreadPool::s().m_exclusiveAccess) {
|
||||
V3ThreadPool::s().m_exclusiveAccess = false;
|
||||
V3ThreadPool::s().m_stopRequested = false;
|
||||
V3ThreadPool::s().m_stoppedJobsCV.notify_all();
|
||||
|
||||
V3ThreadPool::s().m_stoppedJobsMutex.unlock();
|
||||
} else {
|
||||
V3ThreadPool::s().m_stoppedJobsMutex.pretendUnlock();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
T V3ThreadPool::waitForFuture(std::future<T>& future) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
while (true) {
|
||||
|
|
@ -175,29 +245,28 @@ T V3ThreadPool::waitForFuture(std::future<T>& future) VL_MT_SAFE_EXCLUDES(m_mute
|
|||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::future<T> V3ThreadPool::enqueue(std::function<T()>&& f) VL_MT_START {
|
||||
std::shared_ptr<std::promise<T>> prom = std::make_shared<std::promise<T>>();
|
||||
std::future<T> result = prom->get_future();
|
||||
pushJob(prom, std::move(f));
|
||||
const V3LockGuard guard{m_mutex};
|
||||
m_cv.notify_one();
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void V3ThreadPool::pushJob(std::shared_ptr<std::promise<T>>& prom,
|
||||
std::function<T()>&& f) VL_MT_SAFE {
|
||||
template <typename Callable>
|
||||
auto V3ThreadPool::enqueue(Callable&& f) VL_MT_START {
|
||||
using result_t = decltype(f());
|
||||
auto&& job = std::packaged_task<result_t()>{std::forward<Callable>(f)};
|
||||
auto future = job.get_future();
|
||||
if (willExecuteSynchronously()) {
|
||||
prom->set_value(f());
|
||||
job();
|
||||
} else {
|
||||
const V3LockGuard guard{m_mutex};
|
||||
m_queue.push([prom, f] { prom->set_value(f()); });
|
||||
{
|
||||
const V3LockGuard guard{m_mutex};
|
||||
m_queue.push(std::move(job));
|
||||
}
|
||||
m_cv.notify_one();
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
template <>
|
||||
void V3ThreadPool::pushJob<void>(std::shared_ptr<std::promise<void>>& prom,
|
||||
std::function<void()>&& f) VL_MT_SAFE;
|
||||
template <typename Callable>
|
||||
void V3ThreadPool::requestExclusiveAccess(Callable&& exclusiveAccessJob) VL_MT_SAFE
|
||||
VL_EXCLUDES(m_stoppedJobsMutex) {
|
||||
ScopedExclusiveAccess exclusive_access;
|
||||
exclusiveAccessJob();
|
||||
}
|
||||
|
||||
#endif // Guard
|
||||
|
|
|
|||
304
src/V3Timing.cpp
304
src/V3Timing.cpp
|
|
@ -53,6 +53,7 @@
|
|||
#include "V3Const.h"
|
||||
#include "V3EmitV.h"
|
||||
#include "V3Graph.h"
|
||||
#include "V3MemberMap.h"
|
||||
#include "V3SenExprBuilder.h"
|
||||
#include "V3SenTree.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
|
@ -63,41 +64,85 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
|||
|
||||
// ######################################################################
|
||||
|
||||
enum TimingFlag : uint8_t {
|
||||
// Properties of flags with higher numbers include properties of flags with
|
||||
// lower numbers
|
||||
T_NORM = 0, // Normal non-suspendable process
|
||||
T_SUSP = 1, // Suspendable
|
||||
T_PROC = 2 // Suspendable with process metadata
|
||||
enum NodeFlag : uint8_t {
|
||||
T_SUSPENDEE = 1 << 0, // Suspendable (due to dependence on another suspendable)
|
||||
T_SUSPENDER = 1 << 1, // Suspendable (has timing control)
|
||||
T_HAS_PROC = 1 << 2, // Has an associated std::process
|
||||
T_CALLS_PROC_SELF = 1 << 3, // Calls std::process::self
|
||||
};
|
||||
|
||||
enum ForkType : uint8_t {
|
||||
F_NONE = 0, // Not under a fork
|
||||
F_MIGHT_SUSPEND = 1 << 0, // Fork might suspend the execution of current process
|
||||
F_MIGHT_NEED_PROC = 1 << 1, // Fork might need a process (any fork really)
|
||||
};
|
||||
|
||||
enum PropagationType : uint8_t {
|
||||
P_CALL = 1, // Propagation through call to a function/task/method
|
||||
P_FORK = 2, // Propagation due to fork's behaviour
|
||||
P_SIGNATURE = 3, // Propagation required to maintain C++ function's signature requirements
|
||||
};
|
||||
|
||||
// ######################################################################
|
||||
// Detect nodes affected by timing
|
||||
// Detect nodes affected by timing and/or requiring a process
|
||||
|
||||
class TimingSuspendableVisitor final : public VNVisitor {
|
||||
private:
|
||||
// TYPES
|
||||
// Vertex of a dependency graph of suspendable nodes, e.g. if a node (process or task) is
|
||||
// suspendable, all its dependents should also be suspendable
|
||||
class TimingDependencyVertex final : public V3GraphVertex {
|
||||
class DepVtx VL_NOT_FINAL : public V3GraphVertex {
|
||||
AstClass* const m_classp; // Class associated with a method
|
||||
AstNode* const m_nodep; // AST node represented by this graph vertex
|
||||
|
||||
// ACCESSORS
|
||||
string name() const override VL_MT_STABLE {
|
||||
string name() const override {
|
||||
if (m_classp) {
|
||||
if (VN_IS(nodep(), CFunc)) {
|
||||
return cvtToHex(nodep()) + ' ' + classp()->name() + "::" + nodep()->name();
|
||||
}
|
||||
}
|
||||
return cvtToHex(nodep()) + ' ' + nodep()->prettyTypeName();
|
||||
}
|
||||
FileLine* fileline() const override { return nodep()->fileline(); }
|
||||
string dotColor() const override { return nodep()->user2() ? "red" : "black"; }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
TimingDependencyVertex(V3Graph* graphp, AstNode* nodep)
|
||||
DepVtx(V3Graph* graphp, AstNode* nodep, AstClass* classp)
|
||||
: V3GraphVertex{graphp}
|
||||
, m_classp{classp}
|
||||
, m_nodep{nodep} {}
|
||||
~TimingDependencyVertex() override = default;
|
||||
~DepVtx() override = default;
|
||||
|
||||
// ACCESSORS
|
||||
virtual AstNode* nodep() const VL_MT_STABLE { return m_nodep; }
|
||||
virtual AstNode* classp() const VL_MT_STABLE { return m_classp; }
|
||||
};
|
||||
|
||||
class SuspendDepVtx final : public DepVtx {
|
||||
string dotColor() const override {
|
||||
if (nodep()->user2() & T_SUSPENDER) return "red";
|
||||
if (nodep()->user2() & T_SUSPENDEE) return "blue";
|
||||
return "black";
|
||||
}
|
||||
|
||||
public:
|
||||
SuspendDepVtx(V3Graph* graphp, AstNode* nodep, AstClass* classp)
|
||||
: DepVtx{graphp, nodep, classp} {}
|
||||
~SuspendDepVtx() override = default;
|
||||
};
|
||||
|
||||
class NeedsProcDepVtx final : public DepVtx {
|
||||
string dotColor() const override {
|
||||
if (nodep()->user2() & T_CALLS_PROC_SELF) return "red";
|
||||
if (nodep()->user2() & T_HAS_PROC) return "blue";
|
||||
return "black";
|
||||
}
|
||||
|
||||
public:
|
||||
NeedsProcDepVtx(V3Graph* graphp, AstNode* nodep, AstClass* classp)
|
||||
: DepVtx{graphp, nodep, classp} {}
|
||||
~NeedsProcDepVtx() override = default;
|
||||
};
|
||||
|
||||
// NODE STATE
|
||||
|
|
@ -108,40 +153,70 @@ private:
|
|||
// process/task suspendable
|
||||
// and to T_PROC if it
|
||||
// needs process metadata.
|
||||
// Ast{NodeProcedure,CFunc,Begin}::user3() -> DependencyVertex*. Vertex in m_depGraph
|
||||
// Ast{NodeProcedure,CFunc,Begin}::user3() -> DependencyVertex*. Vertex in m_suspGraph
|
||||
// Ast{NodeProcedure,CFunc,Begin}::user3() -> DependencyVertex*. Vertex in m_procGraph
|
||||
const VNUser1InUse m_user1InUse;
|
||||
const VNUser2InUse m_user2InUse;
|
||||
const VNUser3InUse m_user3InUse;
|
||||
const VNUser5InUse m_user5InUse;
|
||||
|
||||
// STATE
|
||||
VMemberMap memberMap; // Member names cached for fast lookup
|
||||
AstClass* m_classp = nullptr; // Current class
|
||||
AstNode* m_procp = nullptr; // NodeProcedure/CFunc/Begin we're under
|
||||
V3Graph m_depGraph; // Dependency graph where a node is a dependency of another if it being
|
||||
// suspendable makes the other node suspendable
|
||||
uint8_t m_underFork = F_NONE; // F_NONE or flags of a fork we are under
|
||||
V3Graph m_suspGraph; // Dependency graph where a node is a dependency of another if it being
|
||||
// suspendable makes the other node suspendable
|
||||
V3Graph m_procGraph; // Dependency graph where a node is a dependency of another if it being
|
||||
// suspendable makes the other node suspendable
|
||||
|
||||
// METHODS
|
||||
// Get or create the dependency vertex for the given node
|
||||
TimingDependencyVertex* getDependencyVertex(AstNode* const nodep) {
|
||||
if (!nodep->user3p()) nodep->user3p(new TimingDependencyVertex{&m_depGraph, nodep});
|
||||
return nodep->user3u().to<TimingDependencyVertex*>();
|
||||
DepVtx* getSuspendDepVtx(AstNode* const nodep) {
|
||||
AstClass* classp = nullptr;
|
||||
if (AstCFunc* funcp = VN_CAST(nodep, CFunc)) {
|
||||
if (funcp->scopep() && funcp->scopep()->modp()) {
|
||||
classp = VN_CAST(funcp->scopep()->modp(), Class);
|
||||
}
|
||||
}
|
||||
if (!nodep->user3p()) nodep->user3p(new SuspendDepVtx{&m_suspGraph, nodep, classp});
|
||||
return nodep->user3u().to<SuspendDepVtx*>();
|
||||
}
|
||||
DepVtx* getNeedsProcDepVtx(AstNode* const nodep) {
|
||||
AstClass* classp = nullptr;
|
||||
if (AstCFunc* funcp = VN_CAST(nodep, CFunc)) {
|
||||
if (funcp->scopep() && funcp->scopep()->modp()) {
|
||||
classp = VN_CAST(funcp->scopep()->modp(), Class);
|
||||
}
|
||||
}
|
||||
if (!nodep->user5p()) nodep->user5p(new NeedsProcDepVtx{&m_procGraph, nodep, classp});
|
||||
return nodep->user5u().to<NeedsProcDepVtx*>();
|
||||
}
|
||||
// Set timing flag of a node
|
||||
bool setTimingFlag(AstNode* nodep, int flag) {
|
||||
// Properties of flags with higher numbers include properties of flags with lower
|
||||
// numbers, so modify nodep->user2() only if it will increase.
|
||||
if (nodep->user2() < flag) {
|
||||
nodep->user2(flag);
|
||||
bool passFlag(const AstNode* from, AstNode* to, NodeFlag flag) {
|
||||
if ((from->user2() & flag) && !(to->user2() & flag)) {
|
||||
to->user2(to->user2() | flag);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Propagate suspendable/needProcess flag to all nodes that depend on the given one
|
||||
void propagateTimingFlags(TimingDependencyVertex* const vxp) {
|
||||
// Propagate flag to all nodes that depend on the given one
|
||||
void propagateFlags(DepVtx* const vxp, NodeFlag flag) {
|
||||
auto* const parentp = vxp->nodep();
|
||||
for (V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
auto* const depVxp = static_cast<DepVtx*>(edgep->top());
|
||||
AstNode* const depp = depVxp->nodep();
|
||||
if (passFlag(parentp, depp, flag)) propagateFlags(depVxp, flag);
|
||||
}
|
||||
}
|
||||
template <typename Predicate>
|
||||
void propagateFlagsReversedIf(DepVtx* const vxp, NodeFlag flag, Predicate p) {
|
||||
auto* const parentp = vxp->nodep();
|
||||
for (V3GraphEdge* edgep = vxp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
auto* const depVxp = static_cast<TimingDependencyVertex*>(edgep->fromp());
|
||||
auto* const depVxp = static_cast<DepVtx*>(edgep->fromp());
|
||||
AstNode* const depp = depVxp->nodep();
|
||||
if (setTimingFlag(depp, parentp->user2())) propagateTimingFlags(depVxp);
|
||||
if (p(edgep) && passFlag(parentp, depp, flag))
|
||||
propagateFlagsReversedIf(depVxp, flag, p);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -155,17 +230,20 @@ private:
|
|||
void visit(AstNodeProcedure* nodep) override {
|
||||
VL_RESTORER(m_procp);
|
||||
m_procp = nodep;
|
||||
if (nodep->needProcess()) nodep->user2(T_HAS_PROC | T_CALLS_PROC_SELF);
|
||||
if (VN_IS(nodep, Always)) {
|
||||
UINFO(1, "Always does " << (nodep->needProcess() ? "" : "NOT ") << "need process\n");
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstCFunc* nodep) override {
|
||||
VL_RESTORER(m_procp);
|
||||
m_procp = nodep;
|
||||
iterateChildren(nodep);
|
||||
TimingDependencyVertex* const vxp = getDependencyVertex(nodep);
|
||||
if (nodep->needProcess()) nodep->user2(T_PROC);
|
||||
if (nodep->needProcess()) nodep->user2(T_HAS_PROC | T_CALLS_PROC_SELF);
|
||||
DepVtx* const sVxp = getSuspendDepVtx(nodep);
|
||||
DepVtx* const pVxp = getNeedsProcDepVtx(nodep);
|
||||
if (!m_classp) return;
|
||||
// If class method (possibly overrides another method)
|
||||
if (!m_classp->user1SetOnce()) m_classp->repairCache();
|
||||
|
||||
// Go over overridden functions
|
||||
|
||||
|
|
@ -182,19 +260,15 @@ private:
|
|||
// actually overridden by our method. If this causes a problem, traverse to
|
||||
// the root of the inheritance hierarchy and check if the original method is
|
||||
// virtual or not.
|
||||
if (!cextp->classp()->user1SetOnce()) cextp->classp()->repairCache();
|
||||
if (auto* const overriddenp
|
||||
= VN_CAST(cextp->classp()->findMember(nodep->name()), CFunc)) {
|
||||
setTimingFlag(nodep, overriddenp->user2());
|
||||
if (nodep->user2()
|
||||
< T_PROC) { // Add a vertex only if the flag can still change
|
||||
// Make a dependency cycle, as being suspendable should propagate both up
|
||||
// and down the inheritance tree
|
||||
TimingDependencyVertex* const overriddenVxp
|
||||
= getDependencyVertex(overriddenp);
|
||||
new V3GraphEdge{&m_depGraph, vxp, overriddenVxp, 1};
|
||||
new V3GraphEdge{&m_depGraph, overriddenVxp, vxp, 1};
|
||||
}
|
||||
= VN_CAST(memberMap.findMember(cextp->classp(), nodep->name()), CFunc)) {
|
||||
// Suspendability and process affects typing, so they propagate both ways
|
||||
DepVtx* const overriddenSVxp = getSuspendDepVtx(overriddenp);
|
||||
DepVtx* const overriddenPVxp = getNeedsProcDepVtx(overriddenp);
|
||||
new V3GraphEdge{&m_suspGraph, sVxp, overriddenSVxp, P_SIGNATURE};
|
||||
new V3GraphEdge{&m_suspGraph, overriddenSVxp, sVxp, P_SIGNATURE};
|
||||
new V3GraphEdge{&m_procGraph, pVxp, overriddenPVxp, P_SIGNATURE};
|
||||
new V3GraphEdge{&m_procGraph, overriddenPVxp, pVxp, P_SIGNATURE};
|
||||
} else {
|
||||
AstClassExtends* more_extends = cextp->classp()->extendsp();
|
||||
if (more_extends) extends.push(more_extends);
|
||||
|
|
@ -203,30 +277,49 @@ private:
|
|||
}
|
||||
}
|
||||
void visit(AstNodeCCall* nodep) override {
|
||||
setTimingFlag(m_procp, nodep->funcp()->user2());
|
||||
if (m_procp->user2() < T_PROC) { // Add a vertex only if the flag can still change
|
||||
TimingDependencyVertex* const procVxp = getDependencyVertex(m_procp);
|
||||
TimingDependencyVertex* const funcVxp = getDependencyVertex(nodep->funcp());
|
||||
new V3GraphEdge{&m_depGraph, procVxp, funcVxp, 1};
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
if (!m_underFork || (m_underFork & F_MIGHT_SUSPEND))
|
||||
new V3GraphEdge{&m_suspGraph, getSuspendDepVtx(nodep->funcp()),
|
||||
getSuspendDepVtx(m_procp), m_underFork ? P_FORK : P_CALL};
|
||||
|
||||
if (!m_underFork)
|
||||
new V3GraphEdge{&m_procGraph, getNeedsProcDepVtx(nodep->funcp()),
|
||||
getNeedsProcDepVtx(m_procp), P_CALL};
|
||||
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstBegin* nodep) override {
|
||||
VL_RESTORER(m_procp);
|
||||
VL_RESTORER(m_underFork);
|
||||
|
||||
if (!m_underFork || (m_underFork & F_MIGHT_SUSPEND))
|
||||
new V3GraphEdge{&m_suspGraph, getSuspendDepVtx(nodep), getSuspendDepVtx(m_procp),
|
||||
m_underFork ? P_FORK : P_CALL};
|
||||
|
||||
if (!m_underFork)
|
||||
new V3GraphEdge{&m_procGraph, getNeedsProcDepVtx(nodep), getNeedsProcDepVtx(m_procp),
|
||||
P_CALL};
|
||||
|
||||
m_procp = nodep;
|
||||
m_underFork = 0;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstFork* nodep) override {
|
||||
VL_RESTORER(m_underFork);
|
||||
|
||||
v3Global.setUsesTiming(); // Even if there are no event controls, we have to set this flag
|
||||
// so that transformForks() in V3SchedTiming gets called and
|
||||
// removes all forks and begins
|
||||
if (nodep->isTimingControl() && m_procp) m_procp->user2(T_SUSP);
|
||||
if (nodep->isTimingControl() && m_procp) {
|
||||
m_procp->user2(T_SUSPENDEE | T_SUSPENDER);
|
||||
m_underFork |= F_MIGHT_SUSPEND;
|
||||
}
|
||||
m_underFork |= F_MIGHT_NEED_PROC;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNode* nodep) override {
|
||||
if (nodep->isTimingControl()) {
|
||||
v3Global.setUsesTiming();
|
||||
if (m_procp) m_procp->user2(T_SUSP);
|
||||
if (m_procp) m_procp->user2(T_SUSPENDEE | T_SUSPENDER);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
|
@ -238,12 +331,29 @@ public:
|
|||
// CONSTRUCTORS
|
||||
explicit TimingSuspendableVisitor(AstNetlist* nodep) {
|
||||
iterate(nodep);
|
||||
m_depGraph.removeTransitiveEdges();
|
||||
for (V3GraphVertex* vxp = m_depGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||
TimingDependencyVertex* const depVxp = static_cast<TimingDependencyVertex*>(vxp);
|
||||
if (depVxp->nodep()->user2()) propagateTimingFlags(depVxp);
|
||||
m_suspGraph.removeTransitiveEdges();
|
||||
m_procGraph.removeTransitiveEdges();
|
||||
// Propagate suspendability
|
||||
for (V3GraphVertex* vxp = m_suspGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||
DepVtx* const depVxp = static_cast<DepVtx*>(vxp);
|
||||
if (depVxp->nodep()->user2() & T_SUSPENDEE) propagateFlags(depVxp, T_SUSPENDEE);
|
||||
}
|
||||
if (dumpGraphLevel() >= 6) m_depGraph.dumpDotFilePrefixed("timing_deps");
|
||||
if (dumpGraphLevel() >= 6) m_suspGraph.dumpDotFilePrefixed("timing_deps");
|
||||
// Propagate process
|
||||
for (V3GraphVertex* vxp = m_procGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||
DepVtx* const depVxp = static_cast<DepVtx*>(vxp);
|
||||
if (depVxp->nodep()->user2() & T_HAS_PROC) propagateFlags(depVxp, T_HAS_PROC);
|
||||
}
|
||||
// Propagate process downwards (from caller to callee) for suspendable calls
|
||||
for (V3GraphVertex* vxp = m_suspGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||
DepVtx* const depVxp = static_cast<DepVtx*>(vxp);
|
||||
if (depVxp->nodep()->user2() & T_HAS_PROC)
|
||||
propagateFlagsReversedIf(depVxp, T_HAS_PROC, [&](const V3GraphEdge* e) -> bool {
|
||||
return (e->weight() != P_FORK)
|
||||
&& (static_cast<DepVtx*>(e->top())->nodep()->user2() & T_SUSPENDEE);
|
||||
});
|
||||
}
|
||||
if (dumpGraphLevel() >= 6) m_procGraph.dumpDotFilePrefixed("proc_deps");
|
||||
}
|
||||
~TimingSuspendableVisitor() override = default;
|
||||
};
|
||||
|
|
@ -277,13 +387,13 @@ private:
|
|||
AstActive* m_activep = nullptr; // Current active
|
||||
AstNode* m_procp = nullptr; // NodeProcedure/CFunc/Begin we're under
|
||||
double m_timescaleFactor = 1.0; // Factor to scale delays by
|
||||
int m_forkCnt = 0; // Number of forks inside a module
|
||||
|
||||
// Unique names
|
||||
V3UniqueNames m_contAssignVarNames{"__VassignWtmp"}; // Names for temp AssignW vars
|
||||
V3UniqueNames m_intraValueNames{"__Vintraval"}; // Intra assign delay value var names
|
||||
V3UniqueNames m_intraIndexNames{"__Vintraidx"}; // Intra assign delay index var names
|
||||
V3UniqueNames m_intraLsbNames{"__Vintralsb"}; // Intra assign delay LSB var names
|
||||
V3UniqueNames m_forkNames{"__Vfork"}; // Fork name generator
|
||||
V3UniqueNames m_trigSchedNames{"__VtrigSched"}; // Trigger scheduler name generator
|
||||
V3UniqueNames m_dynTrigNames{"__VdynTrigger"}; // Dynamic trigger name generator
|
||||
|
||||
|
|
@ -299,6 +409,7 @@ private:
|
|||
|
||||
// Other
|
||||
SenTreeFinder m_finder{m_netlistp}; // Sentree finder and uniquifier
|
||||
SenExprBuilder* m_senExprBuilderp = nullptr; // Sens expression builder for current m_scope
|
||||
|
||||
// METHODS
|
||||
// Find net delay on the LHS of an assignment
|
||||
|
|
@ -358,23 +469,6 @@ private:
|
|||
int scalePowerOfTen = timeunit.powerOfTen() - m_netlistp->timeprecision().powerOfTen();
|
||||
return std::pow(10.0, scalePowerOfTen);
|
||||
}
|
||||
// Construct SenItems from VarRefs in an expression
|
||||
AstSenItem* varRefpsToSenItemsp(AstNode* const nodep) const {
|
||||
AstNode* senItemsp = nullptr;
|
||||
const VNUser4InUse user4InUse;
|
||||
nodep->foreach([&](AstNodeVarRef* refp) {
|
||||
if (refp->access().isWriteOnly()) return;
|
||||
AstVarScope* const vscp = refp->varScopep();
|
||||
if (vscp->user4SetOnce()) return;
|
||||
const bool isEvent = vscp->dtypep() && vscp->dtypep()->basicp()
|
||||
&& vscp->dtypep()->basicp()->isEvent();
|
||||
const auto edgeType = isEvent ? VEdgeType::ET_EVENT : VEdgeType::ET_CHANGED;
|
||||
senItemsp = AstNode::addNext(
|
||||
senItemsp, new AstSenItem{refp->fileline(), edgeType,
|
||||
new AstVarRef{refp->fileline(), vscp, VAccess::READ}});
|
||||
});
|
||||
return VN_AS(senItemsp, SenItem);
|
||||
}
|
||||
// Creates the global delay scheduler variable
|
||||
AstVarScope* getCreateDelayScheduler() {
|
||||
if (m_delaySchedp) return m_delaySchedp;
|
||||
|
|
@ -488,7 +582,7 @@ private:
|
|||
void addProcessInfo(AstCMethodHard* const methodp) const {
|
||||
FileLine* const flp = methodp->fileline();
|
||||
AstCExpr* const ap = new AstCExpr{
|
||||
flp, m_procp && m_procp->user2() == T_PROC ? "vlProcess" : "nullptr", 0};
|
||||
flp, m_procp && (m_procp->user2() & T_HAS_PROC) ? "vlProcess" : "nullptr", 0};
|
||||
ap->dtypeSetVoid();
|
||||
methodp->addPinsp(ap);
|
||||
}
|
||||
|
|
@ -564,12 +658,19 @@ private:
|
|||
m_classp = VN_CAST(nodep, Class);
|
||||
VL_RESTORER(m_timescaleFactor);
|
||||
m_timescaleFactor = calculateTimescaleFactor(nodep->timeunit());
|
||||
VL_RESTORER(m_forkCnt);
|
||||
m_forkCnt = 0;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstScope* nodep) override {
|
||||
VL_RESTORER(m_scopep);
|
||||
m_scopep = nodep;
|
||||
iterateChildren(nodep);
|
||||
SenExprBuilder senExprBuilder{m_scopep};
|
||||
{ // Restore m_senExprBuilderp before destroying senExprBuilder
|
||||
VL_RESTORER(m_senExprBuilderp);
|
||||
m_senExprBuilderp = &senExprBuilder;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstActive* nodep) override {
|
||||
m_activep = nodep;
|
||||
|
|
@ -580,8 +681,8 @@ private:
|
|||
VL_RESTORER(m_procp);
|
||||
m_procp = nodep;
|
||||
iterateChildren(nodep);
|
||||
if (nodep->user2() >= T_SUSP) nodep->setSuspendable();
|
||||
if (nodep->user2() >= T_PROC) nodep->setNeedProcess();
|
||||
if (nodep->user2() & T_SUSPENDEE) nodep->setSuspendable();
|
||||
if (nodep->user2() & T_HAS_PROC) nodep->setNeedProcess();
|
||||
}
|
||||
void visit(AstInitial* nodep) override {
|
||||
visit(static_cast<AstNodeProcedure*>(nodep));
|
||||
|
|
@ -592,9 +693,16 @@ private:
|
|||
}
|
||||
void visit(AstAlways* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
VL_RESTORER(m_procp);
|
||||
m_procp = nodep;
|
||||
|
||||
// Workaround for killing `always` processes (doing that is pretty much UB)
|
||||
// TODO: Disallow killing `always` at runtime (throw an error)
|
||||
if (nodep->user2() & T_HAS_PROC) nodep->user2(nodep->user2() | T_SUSPENDEE);
|
||||
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->user2()) return;
|
||||
if (nodep->user2() == T_PROC) nodep->setNeedProcess();
|
||||
if (nodep->user2() & T_HAS_PROC) nodep->setNeedProcess();
|
||||
if (!(nodep->user2() & T_SUSPENDEE)) return;
|
||||
nodep->setSuspendable();
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstSenTree* const sensesp = m_activep->sensesp();
|
||||
|
|
@ -615,7 +723,9 @@ private:
|
|||
VL_RESTORER(m_procp);
|
||||
m_procp = nodep;
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->user2()) return;
|
||||
if (nodep->user2() & T_HAS_PROC) nodep->setNeedProcess();
|
||||
if (!(nodep->user2() & T_SUSPENDEE)) return;
|
||||
|
||||
nodep->rtnType("VlCoroutine");
|
||||
// If in a class, create a shared pointer to 'this'
|
||||
if (m_classp) nodep->addInitsp(new AstCStmt{nodep->fileline(), "VL_KEEP_THIS;\n"});
|
||||
|
|
@ -635,10 +745,9 @@ private:
|
|||
firstCoStmtp->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Timing controls inside DPI-exported tasks");
|
||||
}
|
||||
if (nodep->user2() == T_PROC) nodep->setNeedProcess();
|
||||
}
|
||||
void visit(AstNodeCCall* nodep) override {
|
||||
if (nodep->funcp()->user2() && !nodep->user1SetOnce()) { // If suspendable
|
||||
if ((nodep->funcp()->user2() & T_SUSPENDEE) && !nodep->user1SetOnce()) { // If suspendable
|
||||
VNRelinker relinker;
|
||||
nodep->unlinkFrBack(&relinker);
|
||||
AstCAwait* const awaitp = new AstCAwait{nodep->fileline(), nodep};
|
||||
|
|
@ -723,14 +832,14 @@ private:
|
|||
= new AstCAwait{flp, evalMethodp, getCreateDynamicTriggerSenTree()};
|
||||
awaitEvalp->dtypeSetVoid();
|
||||
// Construct the sen expression for this sentree
|
||||
SenExprBuilder senExprBuilder{m_scopep};
|
||||
UASSERT_OBJ(m_senExprBuilderp, nodep, "No SenExprBuilder for this scope");
|
||||
auto* const assignp = new AstAssign{flp, new AstVarRef{flp, trigvscp, VAccess::WRITE},
|
||||
senExprBuilder.build(sensesp).first};
|
||||
m_senExprBuilderp->build(sensesp).first};
|
||||
// Put all the locals and inits before the trigger eval loop
|
||||
for (AstVar* const varp : senExprBuilder.getAndClearLocals()) {
|
||||
for (AstVar* const varp : m_senExprBuilderp->getAndClearLocals()) {
|
||||
nodep->addHereThisAsNext(varp);
|
||||
}
|
||||
for (AstNodeStmt* const stmtp : senExprBuilder.getAndClearInits()) {
|
||||
for (AstNodeStmt* const stmtp : m_senExprBuilderp->getAndClearInits()) {
|
||||
nodep->addHereThisAsNext(stmtp);
|
||||
}
|
||||
// Create the trigger eval loop, which will await the evaluation step and check the
|
||||
|
|
@ -739,7 +848,7 @@ private:
|
|||
flp, new AstLogNot{flp, new AstVarRef{flp, trigvscp, VAccess::READ}},
|
||||
awaitEvalp->makeStmt()};
|
||||
// Put pre updates before the trigger check and assignment
|
||||
for (AstNodeStmt* const stmtp : senExprBuilder.getAndClearPreUpdates()) {
|
||||
for (AstNodeStmt* const stmtp : m_senExprBuilderp->getAndClearPreUpdates()) {
|
||||
loopp->addStmtsp(stmtp);
|
||||
}
|
||||
// Then the trigger check and assignment
|
||||
|
|
@ -752,7 +861,7 @@ private:
|
|||
loopp->addStmtsp(awaitPostUpdatep->makeStmt());
|
||||
}
|
||||
// Put the post updates at the end of the loop
|
||||
for (AstNodeStmt* const stmtp : senExprBuilder.getAndClearPostUpdates()) {
|
||||
for (AstNodeStmt* const stmtp : m_senExprBuilderp->getAndClearPostUpdates()) {
|
||||
loopp->addStmtsp(stmtp);
|
||||
}
|
||||
// Finally, await the resumption step in 'act'
|
||||
|
|
@ -897,11 +1006,14 @@ private:
|
|||
if (stmtsp) AstNode::addNext<AstNode, AstNode>(ifp, stmtsp);
|
||||
nodep->replaceWith(ifp);
|
||||
} else {
|
||||
AstSenItem* const senItemsp = varRefpsToSenItemsp(condp);
|
||||
UASSERT_OBJ(senItemsp, nodep, "No varrefs in wait statement condition");
|
||||
// Put the event control in a while loop with the wait expression as condition
|
||||
AstEventControl* const controlp
|
||||
= new AstEventControl{flp, new AstSenTree{flp, senItemsp}, nullptr};
|
||||
// We are using a global sentree, so we cannot use ET_TRUE, as that could lead to the
|
||||
// active region never converging. Because of this, we need to use ET_CHANGED in a
|
||||
// loop.
|
||||
AstEventControl* const controlp = new AstEventControl{
|
||||
flp,
|
||||
new AstSenTree{
|
||||
flp, new AstSenItem{flp, VEdgeType::ET_CHANGED, condp->cloneTree(false)}},
|
||||
nullptr};
|
||||
controlp->user2(true); // Commit immediately
|
||||
AstWhile* const loopp = new AstWhile{flp, new AstLogNot{flp, condp}, controlp};
|
||||
if (stmtsp) AstNode::addNext<AstNode, AstNode>(loopp, stmtsp);
|
||||
|
|
@ -918,7 +1030,7 @@ private:
|
|||
if (nodep->user1SetOnce()) return;
|
||||
v3Global.setUsesTiming();
|
||||
// Create a unique name for this fork
|
||||
nodep->name(m_forkNames.get(nodep));
|
||||
nodep->name("__Vfork_" + cvtToStr(++m_forkCnt));
|
||||
unsigned idx = 0; // Index for naming begins
|
||||
AstNode* stmtp = nodep->stmtsp();
|
||||
// Put each statement in a begin
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ public:
|
|||
// ACCESSORS
|
||||
AstNode* nodep() const VL_MT_STABLE { return m_nodep; }
|
||||
const AstVar* varp() const { return VN_CAST(nodep(), Var); }
|
||||
string name() const override VL_MT_STABLE {
|
||||
string name() const override {
|
||||
return ((isTristate() ? "tri\\n"
|
||||
: feedsTri() ? "feed\\n"
|
||||
: "-\\n")
|
||||
|
|
@ -672,8 +672,7 @@ class TristateVisitor final : public TristateBaseVisitor {
|
|||
VFlagBitPacked{}, w}; // 2-state ok; sep enable
|
||||
UINFO(9, " newout " << newLhsp << endl);
|
||||
nodep->addStmtsp(newLhsp);
|
||||
refp->varp(newLhsp); // assign the new var to the varref
|
||||
refp->name(newLhsp->name());
|
||||
refp->varp(newLhsp);
|
||||
|
||||
// create a new var for this drivers enable signal
|
||||
AstVar* const newEnLhsp = new AstVar{varp->fileline(), VVarType::MODULETEMP,
|
||||
|
|
|
|||
219
src/V3Width.cpp
219
src/V3Width.cpp
|
|
@ -70,6 +70,7 @@
|
|||
|
||||
#include "V3Const.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3MemberMap.h"
|
||||
#include "V3Number.h"
|
||||
#include "V3Randomize.h"
|
||||
#include "V3String.h"
|
||||
|
|
@ -223,8 +224,8 @@ private:
|
|||
using DTypeMap = std::map<const std::string, AstPatMember*>;
|
||||
|
||||
// STATE
|
||||
VMemberMap memberMap; // Member names cached for fast lookup
|
||||
WidthVP* m_vup = nullptr; // Current node state
|
||||
AstClass* m_classp = nullptr; // Current class
|
||||
const AstCell* m_cellp = nullptr; // Current cell for arrayed instantiations
|
||||
const AstEnumItem* m_enumItemp = nullptr; // Current enum item
|
||||
const AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
||||
|
|
@ -232,7 +233,6 @@ private:
|
|||
const AstWith* m_withp = nullptr; // Current 'with' statement
|
||||
const AstFunc* m_funcp = nullptr; // Current function
|
||||
const AstAttrOf* m_attrp = nullptr; // Current attribute
|
||||
AstPackage* m_pkgp = nullptr; // Current package
|
||||
const bool m_paramsOnly; // Computing parameter value; limit operation
|
||||
const bool m_doGenerate; // Do errors later inside generate statement
|
||||
int m_dtTables = 0; // Number of created data type tables
|
||||
|
|
@ -615,9 +615,11 @@ private:
|
|||
}
|
||||
if (m_vup->final()) {
|
||||
if (nodep->lhsp()->isString() || nodep->rhsp()->isString()) {
|
||||
AstNode* const newp
|
||||
= new AstConcatN{nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
|
||||
nodep->rhsp()->unlinkFrBack()};
|
||||
AstNodeExpr* lhsp = nodep->lhsp()->unlinkFrBack();
|
||||
AstNodeExpr* rhsp = nodep->rhsp()->unlinkFrBack();
|
||||
if (!lhsp->isString()) lhsp = new AstCvtPackString{lhsp->fileline(), lhsp};
|
||||
if (!rhsp->isString()) rhsp = new AstCvtPackString{rhsp->fileline(), rhsp};
|
||||
AstNode* const newp = new AstConcatN{nodep->fileline(), lhsp, rhsp};
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
|
|
@ -729,19 +731,12 @@ private:
|
|||
// width: value(LHS) * width(RHS)
|
||||
if (m_vup->prelim()) {
|
||||
iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
|
||||
V3Const::constifyParamsEdit(nodep->rhsp()); // rhsp may change
|
||||
V3Const::constifyParamsNoWarnEdit(nodep->rhsp()); // rhsp may change
|
||||
|
||||
uint32_t times = 1;
|
||||
|
||||
const AstConst* const constp = VN_CAST(nodep->rhsp(), Const);
|
||||
if (!constp) {
|
||||
nodep->v3error("Replication value isn't a constant.");
|
||||
return;
|
||||
}
|
||||
uint32_t times = constp->toUInt();
|
||||
if (times == 0
|
||||
&& !VN_IS(nodep->backp(), Concat)) { // Concat Visitor will clean it up.
|
||||
nodep->v3error("Replication value of 0 is only legal under a concatenation"
|
||||
" (IEEE 1800-2017 11.4.12.1)");
|
||||
times = 1;
|
||||
}
|
||||
if (constp) times = constp->toUInt();
|
||||
|
||||
AstNodeDType* const vdtypep = m_vup->dtypeNullSkipRefp();
|
||||
if (VN_IS(vdtypep, QueueDType) || VN_IS(vdtypep, DynArrayDType)
|
||||
|
|
@ -775,7 +770,7 @@ private:
|
|||
<< vdtypep->prettyDTypeNameQ() << " data type");
|
||||
}
|
||||
iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
|
||||
if (nodep->lhsp()->isString()) {
|
||||
if ((vdtypep && vdtypep->isString()) || nodep->lhsp()->isString()) {
|
||||
AstNode* const newp
|
||||
= new AstReplicateN{nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
|
||||
nodep->rhsp()->unlinkFrBack()};
|
||||
|
|
@ -783,6 +778,13 @@ private:
|
|||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
} else {
|
||||
if (!constp) nodep->v3error("Replication value isn't a constant.");
|
||||
if (times == 0
|
||||
&& !VN_IS(nodep->backp(), Concat)) { // Concat Visitor will clean it up.
|
||||
nodep->v3error("Replication value of 0 is only legal under a concatenation"
|
||||
" (IEEE 1800-2017 11.4.12.1)");
|
||||
times = 1; // Set to 1, so we can continue looking for errors
|
||||
}
|
||||
nodep->dtypeSetLogicUnsized((nodep->lhsp()->width() * times),
|
||||
(nodep->lhsp()->widthMin() * times),
|
||||
VSigning::UNSIGNED);
|
||||
|
|
@ -801,18 +803,7 @@ private:
|
|||
if (m_vup->prelim()) {
|
||||
iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
|
||||
iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
|
||||
V3Const::constifyParamsEdit(nodep->rhsp()); // rhsp may change
|
||||
const AstConst* const constp = VN_CAST(nodep->rhsp(), Const);
|
||||
if (!constp) {
|
||||
nodep->v3error("Replication value isn't a constant.");
|
||||
return;
|
||||
}
|
||||
const uint32_t times = constp->toUInt();
|
||||
if (times == 0
|
||||
&& !VN_IS(nodep->backp(), Concat)) { // Concat Visitor will clean it up.
|
||||
nodep->v3error("Replication value of 0 is only legal under a concatenation"
|
||||
" (IEEE 1800-2017 11.4.12.1)");
|
||||
}
|
||||
V3Const::constifyParamsNoWarnEdit(nodep->rhsp()); // rhsp may change
|
||||
nodep->dtypeSetString();
|
||||
}
|
||||
if (m_vup->final()) {
|
||||
|
|
@ -848,8 +839,14 @@ private:
|
|||
} else {
|
||||
nodep->v3error("Slice size isn't a constant or basic data type.");
|
||||
}
|
||||
nodep->dtypeSetLogicUnsized(nodep->lhsp()->width(), nodep->lhsp()->widthMin(),
|
||||
VSigning::UNSIGNED);
|
||||
if (VN_IS(nodep->lhsp()->dtypep(), DynArrayDType)
|
||||
|| VN_IS(nodep->lhsp()->dtypep(), QueueDType)
|
||||
|| VN_IS(nodep->lhsp()->dtypep(), UnpackArrayDType)) {
|
||||
nodep->dtypeSetStream();
|
||||
} else {
|
||||
nodep->dtypeSetLogicUnsized(nodep->lhsp()->width(), nodep->lhsp()->widthMin(),
|
||||
VSigning::UNSIGNED);
|
||||
}
|
||||
}
|
||||
if (m_vup->final()) {
|
||||
if (!nodep->dtypep()->widthSized()) {
|
||||
|
|
@ -1550,7 +1547,7 @@ private:
|
|||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: $bits for queue");
|
||||
break;
|
||||
}
|
||||
default: nodep->v3error("Unhandled attribute type");
|
||||
default: nodep->v3fatalSrc("Unhandled attribute type");
|
||||
}
|
||||
} else {
|
||||
const std::pair<uint32_t, uint32_t> dimpair = dtypep->skipRefp()->dimensions(true);
|
||||
|
|
@ -2115,6 +2112,10 @@ private:
|
|||
v3Global.rootp()->typeTablep()->addTypesp(newp);
|
||||
}
|
||||
}
|
||||
if (AstWildcardArrayDType* const wildp
|
||||
= VN_CAST(nodep->dtypeSkipRefp(), WildcardArrayDType)) {
|
||||
nodep->dtypep(wildp); // Skip RefDType like for other dynamic array types
|
||||
}
|
||||
if (VN_IS(nodep->dtypep()->skipRefToConstp(), ConstDType)) nodep->isConst(true);
|
||||
// Parameters if implicit untyped inherit from what they are assigned to
|
||||
const AstBasicDType* const bdtypep = VN_CAST(nodep->dtypep(), BasicDType);
|
||||
|
|
@ -2577,7 +2578,6 @@ private:
|
|||
// if (debug() >= 9) nodep->dumpTree("- class-in: ");
|
||||
if (!nodep->packed() && v3Global.opt.structsPacked()) nodep->packed(true);
|
||||
userIterateChildren(nodep, nullptr); // First size all members
|
||||
nodep->repairMemberCache();
|
||||
nodep->dtypep(nodep);
|
||||
nodep->isFourstate(false);
|
||||
// Error checks
|
||||
|
|
@ -2628,28 +2628,34 @@ private:
|
|||
}
|
||||
void visit(AstClass* nodep) override {
|
||||
if (nodep->didWidthAndSet()) return;
|
||||
// If the package is std::process, set m_process type to VlProcessRef
|
||||
if (m_pkgp && m_pkgp->name() == "std" && nodep->name() == "process") {
|
||||
if (AstVar* const varp = VN_CAST(nodep->findMember("m_process"), Var)) {
|
||||
AstBasicDType* const dtypep = new AstBasicDType{
|
||||
nodep->fileline(), VBasicDTypeKwd::PROCESS_REFERENCE, VSigning::UNSIGNED};
|
||||
v3Global.rootp()->typeTablep()->addTypesp(dtypep);
|
||||
varp->getChildDTypep()->unlinkFrBack();
|
||||
varp->dtypep(dtypep);
|
||||
|
||||
// If the class is std::process
|
||||
if (nodep->name() == "process") {
|
||||
AstPackage* const packagep = getItemPackage(nodep);
|
||||
if (packagep && packagep->name() == "std") {
|
||||
// Change type of m_process to VlProcessRef
|
||||
if (AstVar* const varp = VN_CAST(memberMap.findMember(nodep, "m_process"), Var)) {
|
||||
AstNodeDType* const dtypep = varp->getChildDTypep();
|
||||
if (!varp->dtypep()) {
|
||||
VL_DO_DANGLING(pushDeletep(dtypep->unlinkFrBack()), dtypep);
|
||||
}
|
||||
AstBasicDType* const newdtypep = new AstBasicDType{
|
||||
nodep->fileline(), VBasicDTypeKwd::PROCESS_REFERENCE, VSigning::UNSIGNED};
|
||||
v3Global.rootp()->typeTablep()->addTypesp(newdtypep);
|
||||
varp->dtypep(newdtypep);
|
||||
}
|
||||
// Mark that self requires process instance
|
||||
if (AstNodeFTask* const ftaskp
|
||||
= VN_CAST(memberMap.findMember(nodep, "self"), NodeFTask)) {
|
||||
ftaskp->setNeedProcess();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Must do extends first, as we may in functions under this class
|
||||
// start following a tree of extends that takes us to other classes
|
||||
VL_RESTORER(m_classp);
|
||||
m_classp = nodep;
|
||||
userIterateAndNext(nodep->extendsp(), nullptr);
|
||||
userIterateChildren(nodep, nullptr); // First size all members
|
||||
nodep->repairCache();
|
||||
}
|
||||
void visit(AstPackage* nodep) override {
|
||||
VL_RESTORER(m_pkgp);
|
||||
m_pkgp = nodep;
|
||||
userIterateChildren(nodep, nullptr);
|
||||
}
|
||||
void visit(AstThisRef* nodep) override {
|
||||
if (nodep->didWidthAndSet()) return;
|
||||
|
|
@ -2704,31 +2710,7 @@ private:
|
|||
if (AstNodeUOrStructDType* const adtypep = VN_CAST(fromDtp, NodeUOrStructDType)) {
|
||||
if (memberSelStruct(nodep, adtypep)) return;
|
||||
} else if (AstClassRefDType* const adtypep = VN_CAST(fromDtp, ClassRefDType)) {
|
||||
if (AstNode* const foundp = memberSelClass(nodep, adtypep)) {
|
||||
if (AstVar* const varp = VN_CAST(foundp, Var)) {
|
||||
if (!varp->didWidth()) userIterate(varp, nullptr);
|
||||
nodep->dtypep(foundp->dtypep());
|
||||
nodep->varp(varp);
|
||||
return;
|
||||
}
|
||||
if (AstEnumItemRef* const adfoundp = VN_CAST(foundp, EnumItemRef)) {
|
||||
nodep->replaceWith(adfoundp->cloneTree(false));
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
if (AstNodeFTask* const methodp = VN_CAST(foundp, NodeFTask)) {
|
||||
nodep->replaceWith(new AstMethodCall{nodep->fileline(),
|
||||
nodep->fromp()->unlinkFrBack(),
|
||||
nodep->name(), nullptr});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
UINFO(1, "found object " << foundp << endl);
|
||||
nodep->v3fatalSrc("MemberSel of non-variable\n"
|
||||
<< nodep->warnContextPrimary() << '\n'
|
||||
<< foundp->warnOther() << "... Location of found object\n"
|
||||
<< foundp->warnContextSecondary());
|
||||
}
|
||||
if (memberSelClass(nodep, adtypep)) return;
|
||||
} else if (AstIfaceRefDType* const adtypep = VN_CAST(fromDtp, IfaceRefDType)) {
|
||||
if (AstNode* const foundp = memberSelIface(nodep, adtypep)) {
|
||||
if (AstVar* const varp = VN_CAST(foundp, Var)) {
|
||||
|
|
@ -2767,15 +2749,50 @@ private:
|
|||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
AstNode* memberSelClass(AstMemberSel* nodep, AstClassRefDType* adtypep) {
|
||||
// Returns node if ok
|
||||
bool memberSelClass(AstMemberSel* nodep, AstClassRefDType* adtypep) {
|
||||
// Returns true if ok
|
||||
// No need to width-resolve the class, as it was done when we did the child
|
||||
AstClass* const first_classp = adtypep->classp();
|
||||
UASSERT_OBJ(first_classp, nodep, "Unlinked");
|
||||
for (AstClass* classp = first_classp; classp;) {
|
||||
if (AstNode* const foundp = classp->findMember(nodep->name())) return foundp;
|
||||
if (AstNode* const foundp = memberMap.findMember(classp, nodep->name())) {
|
||||
if (AstVar* const varp = VN_CAST(foundp, Var)) {
|
||||
if (!varp->didWidth()) userIterate(varp, nullptr);
|
||||
if (varp->lifetime().isStatic()) {
|
||||
// Static fiels are moved outside the class, so they shouldn't be accessed
|
||||
// by member select on a class object
|
||||
AstVarRef* const varRefp
|
||||
= new AstVarRef{nodep->fileline(), varp, nodep->access()};
|
||||
varRefp->classOrPackagep(classp);
|
||||
nodep->replaceWith(varRefp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return true;
|
||||
}
|
||||
nodep->dtypep(foundp->dtypep());
|
||||
nodep->varp(varp);
|
||||
return true;
|
||||
}
|
||||
if (AstEnumItemRef* const adfoundp = VN_CAST(foundp, EnumItemRef)) {
|
||||
nodep->replaceWith(adfoundp->cloneTree(false));
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return true;
|
||||
}
|
||||
if (AstNodeFTask* const methodp = VN_CAST(foundp, NodeFTask)) {
|
||||
nodep->replaceWith(new AstMethodCall{nodep->fileline(),
|
||||
nodep->fromp()->unlinkFrBack(),
|
||||
nodep->name(), nullptr});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return true;
|
||||
}
|
||||
UINFO(1, "found object " << foundp << endl);
|
||||
nodep->v3fatalSrc("MemberSel of non-variable\n"
|
||||
<< nodep->warnContextPrimary() << '\n'
|
||||
<< foundp->warnOther() << "... Location of found object\n"
|
||||
<< foundp->warnContextSecondary());
|
||||
}
|
||||
classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr;
|
||||
}
|
||||
|
||||
VSpellCheck speller;
|
||||
for (AstClass* classp = first_classp; classp;) {
|
||||
for (AstNode* itemp = classp->membersp(); itemp; itemp = itemp->nextp()) {
|
||||
|
|
@ -2790,14 +2807,13 @@ private:
|
|||
"Member " << nodep->prettyNameQ() << " not found in class "
|
||||
<< first_classp->prettyNameQ() << "\n"
|
||||
<< (suggest.empty() ? "" : nodep->fileline()->warnMore() + suggest));
|
||||
return nullptr; // Caller handles error
|
||||
return false; // Caller handles error
|
||||
}
|
||||
AstNode* memberSelIface(AstMemberSel* nodep, AstIfaceRefDType* adtypep) {
|
||||
// Returns node if ok
|
||||
// No need to width-resolve the interface, as it was done when we did the child
|
||||
AstNodeModule* const ifacep = adtypep->ifacep();
|
||||
UASSERT_OBJ(ifacep, nodep, "Unlinked");
|
||||
// if (AstNode* const foundp = ifacep->findMember(nodep->name())) return foundp;
|
||||
VSpellCheck speller;
|
||||
for (AstNode* itemp = ifacep->stmtsp(); itemp; itemp = itemp->nextp()) {
|
||||
if (itemp->name() == nodep->name()) return itemp;
|
||||
|
|
@ -2814,7 +2830,8 @@ private:
|
|||
}
|
||||
bool memberSelStruct(AstMemberSel* nodep, AstNodeUOrStructDType* adtypep) {
|
||||
// Returns true if ok
|
||||
if (AstMemberDType* const memberp = adtypep->findMember(nodep->name())) {
|
||||
if (AstMemberDType* const memberp
|
||||
= VN_CAST(memberMap.findMember(adtypep, nodep->name()), MemberDType)) {
|
||||
if (m_attrp) { // Looking for the base of the attribute
|
||||
nodep->dtypep(memberp);
|
||||
UINFO(9, " MEMBERSEL(attr) -> " << nodep << endl);
|
||||
|
|
@ -3202,10 +3219,12 @@ private:
|
|||
if (!nodep->firstAbovep()) newp->dtypeSetVoid();
|
||||
} else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique"
|
||||
|| nodep->name() == "unique_index") {
|
||||
AstWith* const withp = methodWithArgument(
|
||||
nodep, false, true, nullptr, nodep->findUInt32DType(), adtypep->subDTypep());
|
||||
methodOkArguments(nodep, 0, 0);
|
||||
methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
|
||||
newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
|
||||
nodep->name()};
|
||||
nodep->name(), withp};
|
||||
if (nodep->name() == "unique_index") {
|
||||
newp->dtypep(queueDTypeIndexedBy(adtypep->keyDTypep()));
|
||||
} else {
|
||||
|
|
@ -3477,8 +3496,10 @@ private:
|
|||
AstClass* const first_classp = adtypep->classp();
|
||||
if (nodep->name() == "randomize") {
|
||||
V3Randomize::newRandomizeFunc(first_classp);
|
||||
memberMap.clear();
|
||||
} else if (nodep->name() == "srandom") {
|
||||
V3Randomize::newSRandomFunc(first_classp);
|
||||
memberMap.clear();
|
||||
}
|
||||
UASSERT_OBJ(first_classp, nodep, "Unlinked");
|
||||
for (AstClass* classp = first_classp; classp;) {
|
||||
|
|
@ -3501,7 +3522,7 @@ private:
|
|||
}
|
||||
}
|
||||
if (AstNodeFTask* const ftaskp
|
||||
= VN_CAST(classp->findMember(nodep->name()), NodeFTask)) {
|
||||
= VN_CAST(memberMap.findMember(classp, nodep->name()), NodeFTask)) {
|
||||
userIterate(ftaskp, nullptr);
|
||||
if (ftaskp->lifetime().isStatic()) {
|
||||
AstNodeExpr* argsp = nullptr;
|
||||
|
|
@ -3771,7 +3792,7 @@ private:
|
|||
|
||||
classp = refp->classp();
|
||||
UASSERT_OBJ(classp, nodep, "Unlinked");
|
||||
if (AstNodeFTask* const ftaskp = VN_CAST(classp->findMember("new"), Func)) {
|
||||
if (AstNodeFTask* const ftaskp = VN_CAST(memberMap.findMember(classp, "new"), Func)) {
|
||||
nodep->taskp(ftaskp);
|
||||
nodep->classOrPackagep(classp);
|
||||
} else {
|
||||
|
|
@ -3939,7 +3960,8 @@ private:
|
|||
// '{member:value} or '{data_type: default_value}
|
||||
if (const AstText* textp = VN_CAST(patp->keyp(), Text)) {
|
||||
// member: value
|
||||
memp = vdtypep->findMember(textp->text());
|
||||
memp = VN_CAST(memberMap.findMember(vdtypep, textp->text()),
|
||||
MemberDType);
|
||||
if (!memp) {
|
||||
patp->keyp()->v3error("Assignment pattern key '"
|
||||
<< textp->text() << "' not found as member");
|
||||
|
|
@ -5298,7 +5320,6 @@ private:
|
|||
nodep->didWidth(true);
|
||||
return;
|
||||
}
|
||||
if (m_pkgp && m_pkgp->name() == "std") nodep->isFromStd(true);
|
||||
if (nodep->classMethod() && nodep->name() == "rand_mode") {
|
||||
nodep->v3error("The 'rand_mode' method is built-in and cannot be overridden"
|
||||
" (IEEE 1800-2017 18.8)");
|
||||
|
|
@ -5575,20 +5596,17 @@ private:
|
|||
|| (!nodep->taskp()
|
||||
&& (nodep->name() == "get_randstate" || nodep->name() == "set_randstate"))) {
|
||||
// TODO perhaps this should move to V3LinkDot
|
||||
if (!m_classp) {
|
||||
nodep->v3error("Calling implicit class method " << nodep->prettyNameQ()
|
||||
<< " without being under class");
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), 0});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
AstClass* const classp = VN_CAST(nodep->classOrPackagep(), Class);
|
||||
UASSERT_OBJ(classp, nodep, "Should have failed in V3LinkDot");
|
||||
if (nodep->name() == "randomize") {
|
||||
nodep->taskp(V3Randomize::newRandomizeFunc(m_classp));
|
||||
nodep->taskp(V3Randomize::newRandomizeFunc(classp));
|
||||
memberMap.clear();
|
||||
} else if (nodep->name() == "srandom") {
|
||||
nodep->taskp(V3Randomize::newSRandomFunc(m_classp));
|
||||
nodep->taskp(V3Randomize::newSRandomFunc(classp));
|
||||
memberMap.clear();
|
||||
} else if (nodep->name() == "get_randstate") {
|
||||
methodOkArguments(nodep, 0, 0);
|
||||
m_classp->baseMostClassp()->needRNG(true);
|
||||
classp->baseMostClassp()->needRNG(true);
|
||||
v3Global.useRandomizeMethods(true);
|
||||
AstCExpr* const newp
|
||||
= new AstCExpr{nodep->fileline(), "__Vm_rng.get_randstate()", 1, true};
|
||||
|
|
@ -5601,7 +5619,7 @@ private:
|
|||
AstNodeExpr* const expr1p = VN_AS(nodep->pinsp(), Arg)->exprp(); // May edit
|
||||
iterateCheckString(nodep, "LHS", expr1p, BOTH);
|
||||
AstNodeExpr* const exprp = VN_AS(nodep->pinsp(), Arg)->exprp();
|
||||
m_classp->baseMostClassp()->needRNG(true);
|
||||
classp->baseMostClassp()->needRNG(true);
|
||||
v3Global.useRandomizeMethods(true);
|
||||
AstCExpr* const newp
|
||||
= new AstCExpr{nodep->fileline(), "__Vm_rng.set_randstate(", 1, true};
|
||||
|
|
@ -5671,7 +5689,8 @@ private:
|
|||
}
|
||||
if (nodep->fileline()->timingOn()) {
|
||||
if (v3Global.opt.timing().isSetTrue()) {
|
||||
userIterate(nodep->condp(), WidthVP{SELF, PRELIM}.p());
|
||||
iterateCheckBool(nodep, "Wait", nodep->condp(),
|
||||
BOTH); // it's like an if() condition.
|
||||
iterateNull(nodep->stmtsp());
|
||||
return;
|
||||
} else if (v3Global.opt.timing().isSetFalse()) {
|
||||
|
|
@ -6371,7 +6390,7 @@ private:
|
|||
}
|
||||
void checkClassAssign(AstNode* nodep, const char* side, AstNode* rhsp,
|
||||
AstNodeDType* lhsDTypep) {
|
||||
if (AstClassRefDType* const lhsClassRefp = VN_CAST(lhsDTypep, ClassRefDType)) {
|
||||
if (AstClassRefDType* const lhsClassRefp = VN_CAST(lhsDTypep->skipRefp(), ClassRefDType)) {
|
||||
UASSERT_OBJ(rhsp->dtypep(), rhsp, "Node has no type");
|
||||
AstNodeDType* const rhsDtypep = rhsp->dtypep()->skipRefp();
|
||||
if (AstClassRefDType* const rhsClassRefp = VN_CAST(rhsDtypep, ClassRefDType)) {
|
||||
|
|
@ -6379,7 +6398,7 @@ private:
|
|||
} else if (auto* const constp = VN_CAST(rhsp, Const)) {
|
||||
if (constp->num().isNull()) return;
|
||||
}
|
||||
nodep->v3error(side << " expects a " << lhsDTypep->prettyTypeName() << ", got "
|
||||
nodep->v3error(side << " expects a " << lhsClassRefp->prettyTypeName() << ", got "
|
||||
<< rhsDtypep->prettyTypeName());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,10 +110,10 @@ private:
|
|||
AstBasicDType* const newp = nodep->findInsertSameDType(bdtypep);
|
||||
if (newp != bdtypep && debug() >= 9) {
|
||||
UINFO(9, "dtype replacement ");
|
||||
nodep->dumpSmall(cout);
|
||||
cout << " ----> ";
|
||||
newp->dumpSmall(cout);
|
||||
cout << endl;
|
||||
nodep->dumpSmall(std::cout);
|
||||
std::cout << " ----> ";
|
||||
newp->dumpSmall(std::cout);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
return newp;
|
||||
}
|
||||
|
|
@ -190,7 +190,6 @@ private:
|
|||
void visit(AstNodeUOrStructDType* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
visitIterateNodeDType(nodep);
|
||||
nodep->clearCache();
|
||||
}
|
||||
void visit(AstParamTypeDType* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
|
|
@ -253,8 +252,9 @@ public:
|
|||
// Were changing widthMin's, so the table is now somewhat trashed
|
||||
nodep->typeTablep()->clearCache();
|
||||
iterate(nodep);
|
||||
// Don't want to repairCache, as all needed nodes have been added back in
|
||||
// a repair would prevent dead nodes from being detected
|
||||
// Don't want to AstTypeTable::repairCache, as all needed nodes
|
||||
// have been added back in; a repair would prevent dead nodes from
|
||||
// being detected
|
||||
}
|
||||
~WidthCommitVisitor() override = default;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -348,8 +348,8 @@ private:
|
|||
UINFO(6, "SELEXTRACT " << nodep << endl);
|
||||
// if (debug() >= 9) nodep->dumpTree("- SELEX0: ");
|
||||
// Below 2 lines may change nodep->widthp()
|
||||
V3Const::constifyParamsEdit(nodep->leftp()); // May relink pointed to node
|
||||
V3Const::constifyParamsEdit(nodep->rightp()); // May relink pointed to node
|
||||
V3Const::constifyParamsNoWarnEdit(nodep->leftp()); // May relink pointed to node
|
||||
V3Const::constifyParamsNoWarnEdit(nodep->rightp()); // May relink pointed to node
|
||||
// if (debug() >= 9) nodep->dumpTree("- SELEX3: ");
|
||||
AstNodeExpr* const fromp = nodep->fromp()->unlinkFrBack();
|
||||
const FromData fromdata = fromDataForArray(nodep, fromp);
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
#include "V3Expand.h"
|
||||
#include "V3File.h"
|
||||
#include "V3Force.h"
|
||||
#include "V3Fork.h"
|
||||
#include "V3Gate.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Graph.h"
|
||||
|
|
@ -221,6 +222,10 @@ static void process() {
|
|||
V3Inst::dearrayAll(v3Global.rootp());
|
||||
V3LinkDot::linkDotArrayed(v3Global.rootp());
|
||||
|
||||
// Create dedicated tasks for fork..join_any / fork_join_none processes
|
||||
if (V3Fork::makeTasks(v3Global.rootp()))
|
||||
V3LinkDot::linkDotPrimary(v3Global.rootp()); // Link newly created tasks
|
||||
|
||||
// Task inlining & pushing BEGINs names to variables/cells
|
||||
// Begin processing must be after Param, before module inlining
|
||||
V3Begin::debeginAll(v3Global.rootp()); // Flatten cell names, before inliner
|
||||
|
|
|
|||
|
|
@ -115,11 +115,11 @@ public:
|
|||
}
|
||||
|
||||
void dump() const {
|
||||
cout << "# ";
|
||||
for (uint64_t i = 0; i < m_dataSize; i++) {
|
||||
if (hits(i)) cout << "," << i;
|
||||
std::cout << "# ";
|
||||
for (uint64_t i = 0; i < m_dataSize; ++i) {
|
||||
if (hits(i)) std::cout << "," << i;
|
||||
}
|
||||
cout << endl;
|
||||
std::cout << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@
|
|||
|
||||
// clang-format off
|
||||
#include "config_build.h"
|
||||
#ifndef HAVE_CONFIG_BUILD
|
||||
# error "Something failed during ./configure as config_build.h is incomplete. Perhaps you used autoreconf, don't."
|
||||
#ifndef HAVE_CONFIG_PACKAGE
|
||||
# error "Something failed during ./configure as config_package.h is incomplete. Perhaps you used autoreconf, don't."
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ static int debug() { return V3Error::debugDefault(); }
|
|||
void VlcOptions::addReadFile(const string& filename) { m_readFiles.insert(filename); }
|
||||
|
||||
string VlcOptions::version() {
|
||||
string ver = DTVERSION;
|
||||
string ver = PACKAGE_STRING;
|
||||
ver += " rev " + cvtToStr(DTVERSION_rev);
|
||||
return ver;
|
||||
}
|
||||
|
|
@ -98,18 +98,18 @@ void VlcOptions::parseOptsList(int argc, char** argv) {
|
|||
}
|
||||
|
||||
void VlcOptions::showVersion(bool verbose) {
|
||||
cout << version();
|
||||
cout << endl;
|
||||
std::cout << version();
|
||||
std::cout << endl;
|
||||
if (!verbose) return;
|
||||
|
||||
cout << endl;
|
||||
cout << "Copyright 2003-2023 by Wilson Snyder. Verilator is free software; you can\n";
|
||||
cout << "redistribute it and/or modify the Verilator internals under the terms of\n";
|
||||
cout << "either the GNU Lesser General Public License Version 3 or the Perl Artistic\n";
|
||||
cout << "License Version 2.0.\n";
|
||||
std::cout << endl;
|
||||
std::cout << "Copyright 2003-2023 by Wilson Snyder. Verilator is free software; you can\n";
|
||||
std::cout << "redistribute it and/or modify the Verilator internals under the terms of\n";
|
||||
std::cout << "either the GNU Lesser General Public License Version 3 or the Perl Artistic\n";
|
||||
std::cout << "License Version 2.0.\n";
|
||||
|
||||
cout << endl;
|
||||
cout << "See https://verilator.org for documentation\n";
|
||||
std::cout << endl;
|
||||
std::cout << "See https://verilator.org for documentation\n";
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
|
|
|
|||
|
|
@ -129,10 +129,10 @@ public:
|
|||
// METHODS
|
||||
void dump() {
|
||||
UINFO(2, "dumpPoints...\n");
|
||||
VlcPoint::dumpHeader(cout);
|
||||
VlcPoint::dumpHeader(std::cout);
|
||||
for (const auto& i : *this) {
|
||||
const VlcPoint& point = pointNumber(i.second);
|
||||
point.dump(cout);
|
||||
point.dump(std::cout);
|
||||
}
|
||||
}
|
||||
VlcPoint& pointNumber(uint64_t num) { return m_points[num]; }
|
||||
|
|
|
|||
|
|
@ -63,20 +63,21 @@ public:
|
|||
|
||||
// METHODS
|
||||
static void dumpHeader() {
|
||||
cout << "Tests:\n";
|
||||
// cout<<" Testrun, Computrons,"; // Currently not loaded
|
||||
cout << " Covered, Rank, RankPts, Filename\n";
|
||||
std::cout << "Tests:\n";
|
||||
// std::cout<<" Testrun, Computrons,"; // Currently not loaded
|
||||
std::cout << " Covered, Rank, RankPts, Filename\n";
|
||||
}
|
||||
void dump(bool bucketsToo) {
|
||||
if (testrun() || computrons() != 0.0) { // currently unused // LCOV_EXCL_LINE
|
||||
cout << " " << std::setw(8) << std::setfill('0') << testrun() // LCOV_EXCL_LINE
|
||||
<< ", " << std::setw(7) << std::setfill(' ') << computrons() // LCOV_EXCL_LINE
|
||||
<< ","; // LCOV_EXCL_LINE
|
||||
std::cout << " " << std::setw(8) << std::setfill('0') << testrun() // LCOV_EXCL_LINE
|
||||
<< ", " << std::setw(7) << std::setfill(' ')
|
||||
<< computrons() // LCOV_EXCL_LINE
|
||||
<< ","; // LCOV_EXCL_LINE
|
||||
}
|
||||
cout << " " << std::setw(7) << std::setfill(' ') << bucketsCovered();
|
||||
cout << ", " << std::setw(7) << std::setfill(' ') << rank();
|
||||
cout << ", " << std::setw(7) << std::setfill(' ') << rankPoints();
|
||||
cout << ", \"" << name() << "\"\n";
|
||||
std::cout << " " << std::setw(7) << std::setfill(' ') << bucketsCovered();
|
||||
std::cout << ", " << std::setw(7) << std::setfill(' ') << rank();
|
||||
std::cout << ", " << std::setw(7) << std::setfill(' ') << rankPoints();
|
||||
std::cout << ", \"" << name() << "\"\n";
|
||||
if (bucketsToo) m_buckets.dump();
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -249,8 +249,8 @@ void VlcTop::annotateCalcNeeded() {
|
|||
}
|
||||
}
|
||||
const float pct = totCases ? (100 * totOk / totCases) : 0;
|
||||
cout << "Total coverage (" << totOk << "/" << totCases << ") ";
|
||||
cout << std::fixed << std::setw(3) << std::setprecision(2) << pct << "%\n";
|
||||
std::cout << "Total coverage (" << totOk << "/" << totCases << ") ";
|
||||
std::cout << std::fixed << std::setw(3) << std::setprecision(2) << pct << "%\n";
|
||||
if (totOk != totCases) cout << "See lines with '%00' in " << opt.annotateOut() << '\n';
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -948,8 +948,8 @@ def write_ast_macros(filename):
|
|||
if not op:
|
||||
continue
|
||||
name, monad, kind = op
|
||||
retrieve = ("VN_AS(op{n}p(), {kind})" if kind != "Node" else
|
||||
"op{n}p()").format(n=n, kind=kind)
|
||||
retrieve = ("VN_DBG_AS(op{n}p(), {kind})" if kind != "Node"
|
||||
else "op{n}p()").format(n=n, kind=kind)
|
||||
if monad == "List":
|
||||
emitBlock('''\
|
||||
Ast{kind}* {name}() const VL_MT_STABLE {{ return {retrieve}; }}
|
||||
|
|
|
|||
|
|
@ -19,12 +19,7 @@
|
|||
//**********************************************************************
|
||||
//**** Version and host name
|
||||
|
||||
// Autoconf substitutes this with the strings from AC_INIT.
|
||||
#define PACKAGE_STRING ""
|
||||
|
||||
#define PACKAGE_VERSION_NUMBER_STRING "0.000"
|
||||
|
||||
#define DTVERSION PACKAGE_STRING
|
||||
#include "config_package.h"
|
||||
|
||||
//**********************************************************************
|
||||
//**** Functions
|
||||
|
|
@ -60,35 +55,20 @@
|
|||
//**********************************************************************
|
||||
//**** Compile options
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
// Avoid needing std:: prefixes on some very common items
|
||||
using string = std::string;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
//**********************************************************************
|
||||
//**** Configure-discovered library options
|
||||
|
||||
// Define if struct stat has st_mtim.tv_nsec (from configure)
|
||||
#undef HAVE_STAT_NSEC
|
||||
// Define if SystemC found
|
||||
// - If defined, the default search path has it, so support is always enabled.
|
||||
// - If undef, not system-wide, user can set SYSTEMC_INCLUDE.
|
||||
#undef HAVE_SYSTEMC
|
||||
#undef HAVE_COROUTINES
|
||||
|
||||
//**********************************************************************
|
||||
//**** OS and compiler specifics
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
//**********************************************************************
|
||||
//**** This file sometimes gets truncated, so check in consumers
|
||||
#define HAVE_CONFIG_BUILD
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
|
||||
// DESCRIPTION: Verilator: Configure source; system configuration
|
||||
//
|
||||
// This file is part of Verilator.
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
// Copyright 2003-2023 by Wilson Snyder. This program is free software;
|
||||
// you can redistribute it and/or modify it under the terms of either
|
||||
// the GNU Lesser General Public License Version 3 or
|
||||
// the Perl Artistic License Version 2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
|
||||
// Autoconf substitutes this with the strings from AC_INIT.
|
||||
#define PACKAGE_STRING ""
|
||||
#define PACKAGE_VERSION_NUMBER_STRING "0.000"
|
||||
// Otherwise Autoheader generates it (with all the same macros and more)
|
||||
|
||||
//**********************************************************************
|
||||
//**** Configure-discovered library options
|
||||
|
||||
// Define if struct stat has st_mtim.tv_nsec (from configure)
|
||||
#undef HAVE_STAT_NSEC
|
||||
|
||||
// Define if SystemC found
|
||||
// - If defined, the default search path has it, so support is always enabled.
|
||||
// - If undef, not system-wide, user can set SYSTEMC_INCLUDE.
|
||||
#undef HAVE_SYSTEMC
|
||||
|
||||
// Define if coroutines are supported on this platform
|
||||
#undef HAVE_COROUTINES
|
||||
|
||||
//**********************************************************************
|
||||
//**** This file sometimes gets truncated, so check in consumers
|
||||
#define HAVE_CONFIG_PACKAGE
|
||||
|
|
@ -2251,7 +2251,7 @@ tf_variable_identifier<varp>: // IEEE: part of list_of_tf_variable_ide
|
|||
id variable_dimensionListE sigAttrListE exprEqE
|
||||
{ $$ = VARDONEA($<fl>1, *$1, $2, $3);
|
||||
if ($4) AstNode::addNext<AstNode, AstNode>(
|
||||
$$, new AstAssign{$4->fileline(), new AstVarRef{$<fl>1, *$1, VAccess::WRITE}, $4}); }
|
||||
$$, new AstAssign{$4->fileline(), new AstParseRef{$<fl>1, VParseRefExp::PX_TEXT, *$1}, $4}); }
|
||||
;
|
||||
|
||||
variable_declExpr<nodep>: // IEEE: part of variable_decl_assignment - rhs of expr
|
||||
|
|
@ -2753,12 +2753,14 @@ genItemBegin<nodep>: // IEEE: part of generate_block
|
|||
{ $$ = new AstBegin{$<fl>1, *$1, $4, true, false};
|
||||
GRAMMARP->endLabel($<fl>6, *$1, $6); }
|
||||
| id yP_COLON__BEGIN yBEGIN yEND endLabelE
|
||||
{ $$ = nullptr; GRAMMARP->endLabel($<fl>5, *$1, $5); }
|
||||
{ $$ = new AstBegin{$<fl>1, *$1, nullptr, true, false};
|
||||
GRAMMARP->endLabel($<fl>5, *$1, $5); }
|
||||
| yBEGIN ':' idAny ~c~genItemList yEND endLabelE
|
||||
{ $$ = new AstBegin{$<fl>3, *$3, $4, true, false};
|
||||
GRAMMARP->endLabel($<fl>6, *$3, $6); }
|
||||
| yBEGIN ':' idAny yEND endLabelE
|
||||
{ $$ = nullptr; GRAMMARP->endLabel($<fl>5, *$3, $5); }
|
||||
{ $$ = new AstBegin{$<fl>3, *$3, nullptr, true, false};
|
||||
GRAMMARP->endLabel($<fl>5, *$3, $5); }
|
||||
;
|
||||
|
||||
c_genItemBegin<nodep>: // IEEE: part of generate_block (for checkers)
|
||||
|
|
@ -2993,7 +2995,7 @@ netSig<varp>: // IEEE: net_decl_assignment - one element from
|
|||
{ $$ = VARDONEA($<fl>1, *$1, nullptr, $2); }
|
||||
| netId sigAttrListE '=' expr
|
||||
{ $$ = VARDONEA($<fl>1, *$1, nullptr, $2);
|
||||
auto* const assignp = new AstAssignW{$3, new AstVarRef{$<fl>1, *$1, VAccess::WRITE}, $4};
|
||||
auto* const assignp = new AstAssignW{$3, new AstParseRef{$<fl>1, VParseRefExp::PX_TEXT, *$1}, $4};
|
||||
if (GRAMMARP->m_netStrengthp) assignp->strengthSpecp(GRAMMARP->m_netStrengthp->cloneTree(false));
|
||||
AstNode::addNext<AstNode, AstNode>($$, assignp); }
|
||||
| netId variable_dimensionList sigAttrListE
|
||||
|
|
@ -3910,14 +3912,14 @@ for_initializationItem<nodep>: // IEEE: variable_assignment + for_varia
|
|||
AstVar* const varp = VARDONEA($<fl>2, *$2, nullptr, nullptr);
|
||||
varp->lifetime(VLifetime::AUTOMATIC);
|
||||
$$ = varp;
|
||||
$$->addNext(new AstAssign{$3, new AstVarRef{$<fl>2, *$2, VAccess::WRITE}, $4}); }
|
||||
$$->addNext(new AstAssign{$3, new AstParseRef{$<fl>2, VParseRefExp::PX_TEXT, *$2}, $4}); }
|
||||
// // IEEE-2012:
|
||||
| yVAR data_type idAny/*new*/ '=' expr
|
||||
{ VARRESET_NONLIST(VAR); VARDTYPE($2);
|
||||
AstVar* const varp = VARDONEA($<fl>3, *$3, nullptr, nullptr);
|
||||
varp->lifetime(VLifetime::AUTOMATIC);
|
||||
$$ = varp;
|
||||
$$->addNext(new AstAssign{$4, new AstVarRef{$<fl>3, *$3, VAccess::WRITE}, $5}); }
|
||||
$$->addNext(new AstAssign{$4, new AstParseRef{$<fl>3, VParseRefExp::PX_TEXT, *$3}, $5}); }
|
||||
// // IEEE: variable_assignment
|
||||
// // UNSUP variable_lvalue below
|
||||
| varRefBase '=' expr { $$ = new AstAssign{$2, $1, $3}; }
|
||||
|
|
@ -5186,7 +5188,7 @@ let_port_list<nodep>: // IEEE: let_port_list
|
|||
;
|
||||
|
||||
let_port_item<nodep>: // IEEE: let_port_Item
|
||||
// // IEEE: Expanded let_formal_type
|
||||
// // IEEE: Expanded let_formal_type
|
||||
yUNTYPED idAny/*formal_port_identifier*/ variable_dimensionListE exprEqE
|
||||
{ $$ = nullptr; BBUNSUP($<fl>1, "Unsupported: let untyped ports"); }
|
||||
| data_type id/*formal_port_identifier*/ variable_dimensionListE exprEqE
|
||||
|
|
@ -5612,8 +5614,8 @@ idArrayedForeach<nodeExprp>: // IEEE: id + select (under foreach expression)
|
|||
;
|
||||
|
||||
// VarRef without any dots or vectorizaion
|
||||
varRefBase<varRefp>:
|
||||
id { $$ = new AstVarRef{$<fl>1, *$1, VAccess::READ}; }
|
||||
varRefBase<parseRefp>:
|
||||
id { $$ = new AstParseRef{$<fl>1, VParseRefExp::PX_TEXT, *$1}; }
|
||||
;
|
||||
|
||||
// ParseRef
|
||||
|
|
@ -5861,6 +5863,7 @@ property_declarationFront<nodeFTaskp>: // IEEE: part of property_declaration
|
|||
|
||||
property_port_listE<nodep>: // IEEE: [ ( [ property_port_list ] ) ]
|
||||
/* empty */ { $$ = nullptr; }
|
||||
| '(' ')' { $$ = nullptr; }
|
||||
| '(' property_port_list ')' { $$ = $2; }
|
||||
;
|
||||
|
||||
|
|
@ -7227,7 +7230,7 @@ vltOffFront<errcodeen>:
|
|||
{ const char *codemsg = (*$3).c_str();
|
||||
if (V3ErrorCode::unusedMsg(codemsg)) {$$ = V3ErrorCode::I_UNUSED; }
|
||||
else {$$ = V3ErrorCode{codemsg}; }
|
||||
if ($$ == V3ErrorCode::EC_ERROR) { $1->v3error("Unknown Error Code: " << *$3); } }
|
||||
if ($$ == V3ErrorCode::EC_ERROR) { $1->v3error("Unknown error code: " << *$3); } }
|
||||
;
|
||||
|
||||
vltOnFront<errcodeen>:
|
||||
|
|
@ -7239,7 +7242,7 @@ vltOnFront<errcodeen>:
|
|||
{ const char *codemsg = (*$3).c_str();
|
||||
if (V3ErrorCode::unusedMsg(codemsg)) {$$ = V3ErrorCode::I_UNUSED; }
|
||||
else {$$ = V3ErrorCode{codemsg}; }
|
||||
if ($$ == V3ErrorCode::EC_ERROR) { $1->v3error("Unknown Error Code: " << *$3); } }
|
||||
if ($$ == V3ErrorCode::EC_ERROR) { $1->v3error("Unknown error code: " << *$3); } }
|
||||
;
|
||||
|
||||
vltDModuleE<strp>:
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
source ~/SandBox/homecvs/v4/verilator/src/.gdbinit
|
||||
source ../src/.gdbinit
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue