Merge from master for release.

This commit is contained in:
Wilson Snyder 2023-08-06 14:17:13 -04:00
commit e6b0bdd4df
238 changed files with 5412 additions and 1437 deletions

View File

@ -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}

View File

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

49
Changes
View File

@ -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
==========================

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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
أحمد المحمودي

View File

@ -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:

View File

@ -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':

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -500,7 +500,7 @@ feedback-directed optimization. See the appropriate compiler
documentation.
.. _Runtime Debugging
.. _Runtime Debugging:
Runtime Debugging
=================

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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) {

View File

@ -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

2
src/.gitignore vendored
View File

@ -1,6 +1,6 @@
*~
*.old
config_build.h
config_package.h
Makefile
Makefile_obj
.objcache*

View File

@ -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()

View File

@ -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

View File

@ -205,6 +205,7 @@ RAW_OBJS = \
V3File.o \
V3FileLine.o \
V3Force.o \
V3Fork.o \
V3Gate.o \
V3Global.o \
V3Graph.o \

View File

@ -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)");
}
}

View File

@ -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

View File

@ -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'.

View File

@ -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);
}

View File

@ -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

View File

@ -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; }

View File

@ -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)

View File

@ -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));
}

View File

@ -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);
}

View File

@ -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)

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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();

View File

@ -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) {

View File

@ -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);

View File

@ -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(")");

View File

@ -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);
}));
}

View File

@ -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
}

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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()); }

245
src/V3Fork.cpp Normal file
View File

@ -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;
}

35
src/V3Fork.h Normal file
View File

@ -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

View File

@ -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; }

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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);

View File

@ -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 {

View File

@ -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) {

95
src/V3MemberMap.h Normal file
View File

@ -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

View File

@ -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.

View File

@ -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)));

View File

@ -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) {

View File

@ -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;
// }
// }
//

View File

@ -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);

View File

@ -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"); }

View File

@ -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());

View File

@ -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);

View File

@ -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 "

View File

@ -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;

View File

@ -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);
}

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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;

View File

@ -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"; }
};

View File

@ -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;

View File

@ -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

View File

@ -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 {

View File

@ -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);

View File

@ -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());
}

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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());
}
}

View File

@ -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;
};

View File

@ -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);

View File

@ -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

View File

@ -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;
}
};

View File

@ -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";
}
//######################################################################

View File

@ -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]; }

View File

@ -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();
}
};

View File

@ -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';
}

View File

@ -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}; }}

View File

@ -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

38
src/config_package.h.in Normal file
View File

@ -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

View File

@ -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>:

View File

@ -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