Merge from master for release.
This commit is contained in:
commit
aaaf8e75af
|
|
@ -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.003
|
||||
VERSION 5.008
|
||||
HOMEPAGE_URL https://verilator.org
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
|
@ -118,6 +118,7 @@ install(DIRECTORY include TYPE DATA FILES_MATCHING
|
|||
PATTERN "include/verilated.mk"
|
||||
PATTERN "include/*.[chv]"
|
||||
PATTERN "include/*.cpp"
|
||||
PATTERN "include/*.sv"
|
||||
PATTERN "include/gtkwave/*.[chv]*"
|
||||
PATTERN "include/vltstd/*.[chv]*"
|
||||
)
|
||||
|
|
|
|||
64
Changes
64
Changes
|
|
@ -8,7 +8,66 @@ The changes in each Verilator version are described below. The
|
|||
contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
|
||||
Verilator 5.006 2022-01-22
|
||||
Verilator 5.008 2023-03-04
|
||||
==========================
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Add --annotate-points option, change multipoint on line reporting (#3876). [Nassim Corteggiani]
|
||||
* Add --verilate-jobs option (#3889). [Kamil Rakoczy, Antmicro Ltd]
|
||||
* Add WIDTHEXPAND and WIDTHTRUNC warnings to replace WIDTH (#3900). [Andrew Nolte]
|
||||
* Add SOURCE_DATE_EPOCH for docs/guide/conf.py (#3918). [Larry Doolittle]
|
||||
* Add /*verilator public[flat|flat_rd|flat_rw| ]*/ metacomments (#3894). [Joseph Nwabueze]
|
||||
* Add lint warning on always_comb multidriven (#3888) (#3939). [Adam Bagley]
|
||||
* Add warning on ++/-- over expressions with potential side effects (#3976). [Krzysztof Boroński]
|
||||
* Add error on mixing .name and by-port instantiations.
|
||||
* Removed deprecated --cdc option.
|
||||
* Support unpacked unions.
|
||||
* Support interface classes and class implements.
|
||||
* Support global clocking and $global_clock.
|
||||
* Support class parameters without initial values.
|
||||
* Support cast to numbers from strings.
|
||||
* Support struct I/O in --lib-create (#3378) (#3892). [Varun Koyyalagunta]
|
||||
* Support function calls without parenthesis (#3903) (#3902). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support class extending its parameter (#3904). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support static function variables (#3830). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support vpiDefName (#3906) (#3931). [Andrew Nolte]
|
||||
* Support recursive methods (#3987). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix real parameters of infinity and NaN.
|
||||
* Fix pattern assignment to unpacked structs (#3510). [Mostafa Garnal]
|
||||
* Fix single-element replication to dynarray/unpacked/queue (#3548). [Gustav Svensk]
|
||||
* Fix constant enum methods (#3621). [Todd Strader]
|
||||
* Fix inconsistent naming of generate scope arrays (#3840). [Andrew Nolte]
|
||||
* Fix namespace fallback resolution (#3863) (#3942). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix std:: to be parsed first (#3864) (#3928). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix cmake warning if multiple SOURCES w/o PREFIX (#3916) (#3927). [Yoda Lee]
|
||||
* Fix paramaterized class function linkage (#3917). [Ryszard Rozak]
|
||||
* Fix static members of type aliases of a parametrized class (#3922). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix class extend parameter dot case (#3926). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix MsWin missing directory exception, and ::std (#3928) (#3933) (#3935). [Kritik Bhimani]
|
||||
* Fix very long VPI signal names (#3929). [Marlon James]
|
||||
* Fix VPI upper interface scopes not found (#3937). [David Stanford]
|
||||
* Fix virus detection false positive (#3944). [Stuart Morris]
|
||||
* Fix constant string function assignment (#3945). [Todd Strader]
|
||||
* Fix constant format field widths (#3946). [Todd Strader]
|
||||
* Fix class field linking when a super classes is a param (#3949). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix CMake bad C identifiers (#3948) (#3951). [Zixi Li]
|
||||
* Fix build on HP PA architecture (#3954). [John David Anglin]
|
||||
* Fix date on the front page of verilator.pdf (#3956) (#3957). [Larry Doolittle]
|
||||
* Fix dicts declared with ref type (#3960). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix missing error on negative replicate (#3963). [Benjamin Menküc]
|
||||
* Fix self references to parameterized classes (#3962). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix LITENDIAN warning is backwards (#3966) (#3967). [Cameron Kirk]
|
||||
* Fix subsequent parameter declarations (#3969). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix timing delays to not truncate below 64 bits (#3973) (#3982). [Felix Neumärker]
|
||||
* Fix cmake on macOS to mark weak symbols with -U linker flag (#3978) (#3979). [Peter Debacker]
|
||||
* Fix UNDRIVEN warning seg fault (#3989). [Felix Neumärker]
|
||||
* Fix coverage of class methods (#3998). [Tim Paine]
|
||||
* Fix packed array structure replication.
|
||||
* Fix enum.next(0) and enum.prev(0).
|
||||
|
||||
|
||||
Verilator 5.006 2023-01-22
|
||||
==========================
|
||||
|
||||
**Minor:**
|
||||
|
|
@ -20,6 +79,7 @@ Verilator 5.006 2022-01-22
|
|||
* Support property calls without parenthesis (#3879) (#3893). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support import/export lists in modport (#3886). [Gökçe Aydos]
|
||||
* Support class queue equality (#3895). [Ilya Barkov]
|
||||
* Support type case and type equality comparisons.
|
||||
* Add IMPLICITSTATIC warning when a ftask/function is implicitly static (#3839). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Add VL_VALUE_STRING_MAX_WORDS override (#3869). [Andrew Nolte]
|
||||
* Optimize expansion of extend operators.
|
||||
|
|
@ -44,6 +104,8 @@ Verilator 5.006 2022-01-22
|
|||
* Fix foreach unnamedblk duplicate error (#3885). [Ilya Barkov]
|
||||
* Fix elaboration of member selected classes (#3890). [Ilya Barkov]
|
||||
* Fix mismatched widths in DFG (#3872). [Geza Lore, Yike Zhou]
|
||||
* Fix lint for non-integral types in packed structs.
|
||||
* Fix generate case with empty body statements.
|
||||
|
||||
|
||||
Verilator 5.004 2022-12-14
|
||||
|
|
|
|||
14
Makefile.in
14
Makefile.in
|
|
@ -15,13 +15,13 @@
|
|||
#
|
||||
#****************************************************************************/
|
||||
#
|
||||
# make all to compile and build Verilator.
|
||||
# make install to install it.
|
||||
# make TAGS to update tags tables.
|
||||
# make all to compile and build Verilator.
|
||||
# make install to install it.
|
||||
# make TAGS to update tags tables.
|
||||
#
|
||||
# make clean or make mostlyclean
|
||||
# Delete all files from the current directory that are normally
|
||||
# created by building the program. Don't delete the files that
|
||||
# created by building the program. Don't delete the files that
|
||||
# record the configuration. Also preserve files that could be made
|
||||
# by building, but normally aren't because the distribution comes
|
||||
# with them.
|
||||
|
|
@ -153,7 +153,7 @@ msg_test: all_nomsg
|
|||
@echo
|
||||
|
||||
.PHONY: test
|
||||
ifeq ($(CFG_WITH_LONGTESTS),yes) # Local... Else don't burden users
|
||||
ifeq ($(CFG_WITH_LONGTESTS),yes) # Local... Else don't burden users
|
||||
test: smoke-test test_regress
|
||||
# examples is part of test_regress's test_regress/t/t_a2_examples.pl
|
||||
# (because that allows it to run in parallel with other test_regress's)
|
||||
|
|
@ -462,7 +462,7 @@ config.status: configure
|
|||
./config.status --recheck
|
||||
|
||||
configure: configure.ac
|
||||
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
|
||||
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
|
||||
autoconf --warnings=all
|
||||
else
|
||||
autoconf
|
||||
|
|
@ -501,7 +501,7 @@ distclean maintainer-clean::
|
|||
TAGFILES=${srcdir}/*/*.cpp ${srcdir}/*/*.h ${srcdir}/*/*.in \
|
||||
${srcdir}/*.in ${srcdir}/*.pod
|
||||
|
||||
TAGS: $(TAGFILES)
|
||||
TAGS: $(TAGFILES)
|
||||
etags $(TAGFILES)
|
||||
|
||||
.PHONY: doxygen
|
||||
|
|
|
|||
|
|
@ -362,7 +362,7 @@ detailed descriptions of these arguments.
|
|||
+incdir+<dir> Directory to search for includes
|
||||
--inline-mult <value> Tune module inlining
|
||||
--instr-count-dpi <value> Assumed dynamic instruction count of DPI imports
|
||||
-j <jobs> Parallelism for --build (alias to --build-jobs)
|
||||
-j <jobs> Parallelism for --build-jobs/--verilate-jobs
|
||||
--l2-name <value> Verilog scope name of the top module
|
||||
--language <lang> Default language standard to parse
|
||||
-LDFLAGS <flags> Linker pre-object arguments for makefile
|
||||
|
|
@ -416,6 +416,7 @@ detailed descriptions of these arguments.
|
|||
--no-skip-identical Disable skipping identical output
|
||||
--stats Create statistics file
|
||||
--stats-vars Provide statistics on variables
|
||||
--no-std Prevent parsing standard library
|
||||
--structs-packed Convert all unpacked structures to packed structures
|
||||
-sv Enable SystemVerilog parsing
|
||||
+systemverilogext+<ext> Synonym for +1800-2017ext+<ext>
|
||||
|
|
@ -445,6 +446,7 @@ detailed descriptions of these arguments.
|
|||
--unused-regexp <regexp> Tune UNUSED lint signals
|
||||
-V Verbose version and config
|
||||
-v <filename> Verilog library
|
||||
--verilate-jobs Job threads for Verilation stage
|
||||
--no-verilate Skip Verilation and just compile previously Verilated code
|
||||
+verilog1995ext+<ext> Synonym for +1364-1995ext+<ext>
|
||||
+verilog2001ext+<ext> Synonym for +1364-2001ext+<ext>
|
||||
|
|
|
|||
|
|
@ -169,6 +169,7 @@ L<https://verilator.org/guide/latest/exe_verilator_coverage.html>.
|
|||
--annotate <output_dir> Directory name for source annotation.
|
||||
--annotate-all All files should be shown.
|
||||
--annotate-min <count> Minimum occurrence count for uncovered.
|
||||
--annotate-points Annotates info from each coverage point.
|
||||
--help Displays this message and version and exits.
|
||||
--rank Compute relative importance of tests.
|
||||
--unlink With --write, unlink all inputs
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@ def diff_dir(a, b):
|
|||
continue
|
||||
a = files[base]['a']
|
||||
b = files[base]['b']
|
||||
print("=" * 70)
|
||||
print("= %s <-> %s" % (a, b))
|
||||
print("=" * 70, flush=True)
|
||||
print("= %s <-> %s" % (a, b), flush=True)
|
||||
diff_file(a, b)
|
||||
anyfile = True
|
||||
if not anyfile:
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ if (-Not (Test-Path $PWD/../.ccache/win_bison.exe)) {
|
|||
mkdir build
|
||||
cd build
|
||||
cmake .. --install-prefix $PWD/../../../.ccache
|
||||
cmake --build . --config Release
|
||||
cmake --build . --config Release -j 3
|
||||
cmake --install . --prefix $PWD/../../../.ccache
|
||||
cd ../..
|
||||
}
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. --install-prefix $PWD/../install
|
||||
cmake --build . --config Release
|
||||
cmake --build . --config Release -j 3
|
||||
cmake --install . --prefix $PWD/../install
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ cd examples/cmake_tracing_c
|
|||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
cmake --build . --config Release
|
||||
cmake --build . --config Release -j 3
|
||||
Release/example.exe
|
||||
cd ..
|
||||
Remove-Item -path build -recurse
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@
|
|||
# General Public License Version 3 or the Perl Artistic License Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
# When releasing, also update header of Changes file
|
||||
# When releasing, also update header of Changes file,
|
||||
# and commit using "devel release" or "Version bump" message
|
||||
# Then 'make maintainer-dist'
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[5.006 2023-01-22],
|
||||
AC_INIT([Verilator],[5.008 2023-03-04],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ under the Developer Certificate of Origin (https://developercertificate.org/).
|
|||
|
||||
Please see the Verilator manual for 200+ additional contributors. Thanks to all.
|
||||
|
||||
Adam Bagley
|
||||
Adrien Le Masle
|
||||
Ahmed El-Mahmoudy
|
||||
Aleksander Kiryk
|
||||
|
|
@ -13,6 +14,7 @@ Ameya Vikram Singh
|
|||
Andreas Kuster
|
||||
Andrew Nolte
|
||||
Arkadiusz Kozdra
|
||||
Cameron Kirk
|
||||
Chris Randall
|
||||
Chuxuan Wang
|
||||
Conor McCullough
|
||||
|
|
@ -28,6 +30,7 @@ Driss Hafdi
|
|||
Edgar E. Iglesias
|
||||
Eric Rippey
|
||||
Fan Shupei
|
||||
Felix Neumärker
|
||||
Felix Yan
|
||||
Garrett Smith
|
||||
Geza Lore
|
||||
|
|
@ -65,6 +68,7 @@ Joey Liu
|
|||
John Coiner
|
||||
John Demme
|
||||
Jonathan Drolet
|
||||
Joseph Nwabueze
|
||||
Josh Redford
|
||||
Julie Schwartz
|
||||
Julien Margetts
|
||||
|
|
@ -75,6 +79,7 @@ Keith Colbert
|
|||
Kevin Kiningham
|
||||
Kritik Bhimani
|
||||
Krzysztof Bieganski
|
||||
Krzysztof Boroński
|
||||
Kuba Ober
|
||||
Larry Doolittle
|
||||
Ludwig Rogiers
|
||||
|
|
@ -103,6 +108,7 @@ Nathan Myers
|
|||
Patrick Stewart
|
||||
Paul Wright
|
||||
Pawel Sagan
|
||||
Peter Debacker
|
||||
Peter Horvath
|
||||
Peter Monsson
|
||||
Philipp Wagner
|
||||
|
|
@ -148,3 +154,4 @@ Yutetsu TAKATSUKASA
|
|||
Yu-Sheng Lin
|
||||
Yves Mathieu
|
||||
Zhanglei Wang
|
||||
Zixi Li
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
.. comment: generated by t_lint_width_docs_bad
|
||||
.. comment: generated by t_lint_widthexpand_docs_bad
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 3
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
.. comment: generated by t_lint_width_docs_bad
|
||||
.. comment: generated by t_lint_widthexpand_docs_bad
|
||||
.. code-block:: sv
|
||||
:emphasize-lines: 1
|
||||
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
.. comment: generated by t_lint_widthexpand_docs_bad
|
||||
.. code-block::
|
||||
|
||||
%Warning-WIDTHEXPAND: example.v:3:29 Bit extraction of array[4:0] requires 3 bit index, not 2 bits.
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
.. comment: generated by t_lint_width_docs_bad
|
||||
.. code-block::
|
||||
|
||||
%Warning-WIDTH: example.v:3:29 Bit extraction of array[4:0] requires 3 bit index, not 2 bits.
|
||||
|
|
@ -10,7 +10,6 @@
|
|||
# ----------------------------------------------------------------------
|
||||
# -- Path setup
|
||||
|
||||
from datetime import datetime
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
|
@ -24,10 +23,18 @@ def get_vlt_version():
|
|||
filename = "../../Makefile"
|
||||
with open(filename, "r", encoding="utf8") as fh:
|
||||
for line in fh:
|
||||
match = re.search(r"PACKAGE_VERSION_NUMBER *= *([a-z0-9.]+)", line)
|
||||
match = re.search(r"PACKAGE_VERSION *= *([a-z0-9.]+) +([-0-9]+)",
|
||||
line)
|
||||
if match:
|
||||
return match.group(1)
|
||||
return "unknown"
|
||||
return match.group(1), match.group(2)
|
||||
match = re.search(r"PACKAGE_VERSION *= *([a-z0-9.]+) +devel", line)
|
||||
if match:
|
||||
try:
|
||||
data = os.popen('git log -n 1 --pretty=%cs').read()
|
||||
except Exception:
|
||||
data = "" # fallback, and Sphinx will fill in today's date
|
||||
return "Devel " + match.group(1), data
|
||||
return "unknown", "unknown"
|
||||
|
||||
|
||||
def setup(app):
|
||||
|
|
@ -44,8 +51,8 @@ author = 'Wilson Snyder'
|
|||
# The master toctree document.
|
||||
master_doc = "index"
|
||||
|
||||
version = get_vlt_version()
|
||||
release = get_vlt_version()
|
||||
version, today_fmt = get_vlt_version()
|
||||
release = version
|
||||
|
||||
rst_prolog = """
|
||||
.. role:: vlopt(option)
|
||||
|
|
@ -89,9 +96,6 @@ source_suffix = [".rst"]
|
|||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# Date format to ISO
|
||||
today_fmt = datetime.now().strftime("%F")
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = True
|
||||
|
||||
|
|
|
|||
|
|
@ -374,9 +374,11 @@ changed on the specified clock edge.
|
|||
.. code-block:: bash
|
||||
|
||||
cat >our.v <<'EOF'
|
||||
module our (input clk);
|
||||
reg readme /*verilator public_flat_rd*/;
|
||||
reg writeme /*verilator public_flat_rw @(posedge clk) */;
|
||||
module our #(
|
||||
parameter WIDTH /*verilator public_flat_rd*/ = 32
|
||||
) (input clk);
|
||||
reg [WIDTH-1:0] readme /*verilator public_flat_rd*/;
|
||||
reg [WIDTH-1:0] writeme /*verilator public_flat_rw @(posedge clk) */;
|
||||
initial $finish;
|
||||
endmodule
|
||||
EOF
|
||||
|
|
@ -398,12 +400,14 @@ accesses the above signal "readme" would be:
|
|||
vpiHandle vh1 = vpi_handle_by_name((PLI_BYTE8*)"TOP.our.readme", NULL);
|
||||
if (!vh1) vl_fatal(__FILE__, __LINE__, "sim_main", "No handle found");
|
||||
const char* name = vpi_get_str(vpiName, vh1);
|
||||
printf("Module name: %s\n", name); // Prints "readme"
|
||||
const char* type = vpi_get_str(vpiType, vh1);
|
||||
const int size = vpi_get(vpiSize, vh1);
|
||||
printf("register name: %s, type: %s, size: %d\n", name, type, size); // Prints "register name: readme, type: vpiReg, size: 32"
|
||||
|
||||
s_vpi_value v;
|
||||
v.format = vpiIntVal;
|
||||
vpi_get_value(vh1, &v);
|
||||
printf("Value of v: %d\n", v.value.integer); // Prints "readme"
|
||||
printf("Value of %s: %d\n", name, v.value.integer); // Prints "Value of readme: 0"
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
|
@ -479,11 +483,12 @@ distribution.
|
|||
Verilated and VerilatedContext
|
||||
==============================
|
||||
|
||||
Multiple Verilated models may be part of the same simulation context, that
|
||||
is share a VPI interface, sense of time, and common settings. This common
|
||||
simulation context information is stored in a ``VerilatedContext``
|
||||
Multiple C++ Verilated models may be part of the same simulation context,
|
||||
that is share a VPI interface, sense of time, and common settings. This
|
||||
common simulation context information is stored in a ``VerilatedContext``
|
||||
structure. If a ``VerilatedContext`` is not created prior to creating a
|
||||
model, a default global one is created automatically.
|
||||
model, a default global one is created automatically. SystemC requires
|
||||
using only the single, default VerilatedContext.
|
||||
|
||||
The ``Verilated::`` methods, including the ``Verilated::commandArgs`` call
|
||||
shown above, call VerilatedContext methods using the default global
|
||||
|
|
|
|||
|
|
@ -22,10 +22,6 @@ Verilated_heavy.h
|
|||
"verilated.h". Verilated_heavy.h is planned for removal no sooner than
|
||||
July 2022.
|
||||
|
||||
Option `--cdc`
|
||||
The experimental `--cdc` option is believed to be generally unused and is
|
||||
planned for removal no sooner than January 2023.
|
||||
|
||||
Option `-O<letter>`
|
||||
The debug `-O<letter>` options have been replaced with
|
||||
`-fno-<optimization>` debug options to match GCC. The old options are
|
||||
|
|
|
|||
|
|
@ -155,18 +155,6 @@ Summary:
|
|||
Specify C++ without SystemC output mode; see also the :vlopt:`--sc`
|
||||
option.
|
||||
|
||||
.. option:: --cdc
|
||||
|
||||
Permanently experimental. Perform some clock domain crossing checks and
|
||||
issue related warnings (CDCRSTLOGIC) and then exit; if warnings other
|
||||
than CDC warnings are needed, make a second run with
|
||||
:vlopt:`--lint-only`. Additional warning information is also written to
|
||||
the file :file:`<prefix>__cdc.txt`.
|
||||
|
||||
Currently only checks some items that other CDC tools missed; if you are
|
||||
interested in adding more traditional CDC checks, please contact the
|
||||
authors.
|
||||
|
||||
.. option:: -CFLAGS <flags>
|
||||
|
||||
Add specified C compiler argument to the generated makefiles. For
|
||||
|
|
@ -434,7 +422,7 @@ Summary:
|
|||
Preprocess the source code, but do not compile, similar to C++
|
||||
preprocessing using :command:`gcc -E`. Output is written to standard
|
||||
out. Beware of enabling debugging messages, as they will also go to
|
||||
standard out.
|
||||
standard out. See :vlopt:`--no-std`, which is implied by this.
|
||||
|
||||
See also :vlopt:`--dump-defines`, :vlopt:`-P`, and
|
||||
:vlopt:`--pp-comments` options.
|
||||
|
|
@ -707,9 +695,10 @@ Summary:
|
|||
.. option:: -j [<value>]
|
||||
|
||||
Specify the level of parallelism for :vlopt:`--build` if
|
||||
:vlopt:`--build-jobs` isn't provided. If zero, uses the number of threads
|
||||
in the current hardware. Otherwise, the <value> must be a positive
|
||||
integer specifying the maximum number of parallel build jobs.
|
||||
:vlopt:`--build-jobs` isn't provided, and the internal compilation steps
|
||||
of Verilator if :vlopt:`--verilate-jobs` isn't provided. If zero, uses
|
||||
the number of threads in the current hardware. Otherwise, must be a
|
||||
positive integer specifying the maximum number of parallel build jobs.
|
||||
|
||||
.. option:: --l2-name <value>
|
||||
|
||||
|
|
@ -1201,6 +1190,10 @@ Summary:
|
|||
by size (plain :vlopt:`--stats` just gives a count). See
|
||||
:vlopt:`--stats`, which is implied by this.
|
||||
|
||||
.. option:: --no-std
|
||||
|
||||
Prevents parsing standard library.
|
||||
|
||||
.. option:: --structs-packed
|
||||
|
||||
Converts all unpacked structures to packed structures, and issues an
|
||||
|
|
@ -1429,6 +1422,15 @@ Summary:
|
|||
execute only the build. This can be useful for rebuilding the Verilated code
|
||||
produced by a previous invocation of Verilator.
|
||||
|
||||
.. option:: --verilate-jobs [<value>]
|
||||
|
||||
Specify the level of parallelism for the internal compilation steps of
|
||||
Verilator. If zero, uses the number of threads in the current hardware.
|
||||
Otherwise, must be a positive integer specifying the maximum number of
|
||||
parallel build jobs.
|
||||
|
||||
See also :vlopt:`-j`.
|
||||
|
||||
.. option:: +verilog1995ext+<ext>
|
||||
|
||||
Synonym for :vlopt:`+1364-1995ext+\<ext\>`.
|
||||
|
|
@ -1528,6 +1530,8 @@ Summary:
|
|||
:command:`gcc -Wpedantic`. Rarely used, and intended only for strict
|
||||
compliance tests.
|
||||
|
||||
This option changes :option:`ASSIGNIN` from an error to a warning.
|
||||
|
||||
.. option:: -Wwarn-<message>
|
||||
|
||||
Enables the specified warning message.
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ verilator_coverage
|
|||
|
||||
Verilator_coverage processes Verilated model-generated coverage reports.
|
||||
|
||||
With --annotate, it reads the specified coverage data file and generates
|
||||
annotated source code with coverage metrics annotated. If multiple
|
||||
coverage points exist on the same source code line, additional lines will
|
||||
be inserted to report the additional points.
|
||||
With `--annotate`, it reads the specified coverage data file and generates
|
||||
annotated source code with coverage metrics annotated. With
|
||||
`--annotate-points` the coverage points corresponding to each line are also
|
||||
shown.
|
||||
|
||||
Additional Verilog-XL-style standard arguments specify the search paths
|
||||
necessary to find the source code on which the coverage analysis was
|
||||
|
|
@ -58,6 +58,18 @@ to read multiple inputs. If no data file is specified, by default,
|
|||
Specifies the directory name to which source files with annotated coverage
|
||||
data should be written.
|
||||
|
||||
Converting from the Verilator coverage data format to the info format is
|
||||
lossy; the info will have all forms of coverage merged line coverage, and
|
||||
if there are multiple coverage points on a single line they will merge.
|
||||
The minimum coverage across all merged points will be used to report
|
||||
coverage of the line.
|
||||
|
||||
The first character of the line shows a summary of the coverage; this
|
||||
allows use of grep to filter the report. `%` indicates at least one point
|
||||
on the line was below the coverage limit. `+` indicates an
|
||||
:option:`--annotate-points` point was at or above the limit, and `-` that
|
||||
the point was below the limit.
|
||||
|
||||
.. option:: --annotate-all
|
||||
|
||||
Specifies all files should be shown. By default, only those source files
|
||||
|
|
@ -70,6 +82,13 @@ coverage hits, then the coverage point will be considered above the
|
|||
threshold, and the coverage report will put a "%" to indicate the coverage
|
||||
is insufficient. Defaults to 10.
|
||||
|
||||
.. option:: --annotate-points
|
||||
|
||||
Specifies all coverage points should be shown after each line of text. By
|
||||
default, only source lines are shown.
|
||||
|
||||
with low coverage are written to the output directory.
|
||||
|
||||
.. option:: --help
|
||||
|
||||
Displays a help summary, the program version, and exits.
|
||||
|
|
|
|||
|
|
@ -446,6 +446,33 @@ or "`ifdef`"'s may break other tools.
|
|||
|
||||
Same as :option:`public_flat_rw` configuration file option.
|
||||
|
||||
.. option:: /*verilator&32;public_[|flat|flat_rd|flat_rw]_on [@(<edge_list>)]*/ (as scope)
|
||||
|
||||
Used to wrap multiple signals and parameters with the respective public attribute.
|
||||
See attribute above for their respective behavior. Cannot be nested. e.g:
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
/*verilator public_flat_rw_on*/
|
||||
logic clk;
|
||||
logic rst;
|
||||
parameter width = 8;
|
||||
/* verilator public_off*/
|
||||
logic data;
|
||||
|
||||
Is equivalent to:
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
logic clk /*verilator public_flat_rw*/;
|
||||
logic rst /*verilator public_flat_rw*/;
|
||||
parameter width /*verilator public_flat_rw*/ = 8;
|
||||
logic data;
|
||||
|
||||
.. option:: /*verilator&32;public_off*/
|
||||
|
||||
Terminates the previous `/*verilator public*_on*/` directive; see above.
|
||||
|
||||
.. option:: /*verilator&32;public_module*/
|
||||
|
||||
Used after a module statement to indicate the module should not be
|
||||
|
|
|
|||
|
|
@ -461,6 +461,10 @@ chandle
|
|||
Treated as a "longint"; does not yet warn about operations specified as
|
||||
illegal on chandles.
|
||||
|
||||
checker
|
||||
Treated as a "module"; does not yet warn about many constructs illegal
|
||||
inside a checker.
|
||||
|
||||
disable
|
||||
Disable statements may be used only if the block being disabled is a
|
||||
block the disable statement itself is inside. This was commonly used to
|
||||
|
|
|
|||
|
|
@ -74,7 +74,8 @@ List Of Warnings
|
|||
.. option:: Unsupported: ....
|
||||
|
||||
This error indicates that the code uses a Verilog language construct
|
||||
that is not yet supported in Verilator. See the Limitations chapter.
|
||||
that is not yet supported in Verilator. See also :ref:`Language
|
||||
Limitations`.
|
||||
|
||||
|
||||
.. option:: ALWCOMBORDER
|
||||
|
|
@ -317,9 +318,11 @@ List Of Warnings
|
|||
|
||||
.. option:: CDCRSTLOGIC
|
||||
|
||||
With :vlopt:`--cdc` only, it warns that asynchronous flop reset terms come
|
||||
from other than primary inputs or flopped outputs, creating the
|
||||
potential for reset glitches.
|
||||
Historical, never issued since version 5.008.
|
||||
|
||||
Warned with a no longer supported clock domain crossing option that
|
||||
asynchronous flop reset terms came from other than primary inputs or
|
||||
flopped outputs, creating the potential for reset glitches.
|
||||
|
||||
|
||||
.. option:: CLKDATA
|
||||
|
|
@ -847,14 +850,14 @@ List Of Warnings
|
|||
|
||||
.. TODO better example
|
||||
|
||||
Warns that a packed vector is declared with little endian bit numbering
|
||||
(i.e. [0:7]). Big endian bit numbering is now the overwhelming
|
||||
standard, and little numbering is now thus often due to simple oversight
|
||||
Warns that a packed vector is declared with big endian bit numbering
|
||||
(i.e. [0:7]). Little endian bit numbering is now the overwhelming
|
||||
standard, and big numbering is now thus often due to simple oversight
|
||||
instead of intent.
|
||||
|
||||
It also warns that an instance is declared with little endian range
|
||||
(i.e. [0:7] or [7]) and is connected to an N-wide signal. Based on IEEE
|
||||
the bits will likely be backward from what people may expect
|
||||
It also warns that an instance is declared with big endian range
|
||||
(i.e. [0:7] or [7]) and is connected to an N-wide signal.
|
||||
The bits will likely be backward from what people may expect
|
||||
(i.e., instance [0] will connect to signal bit [N-1] not bit [0]).
|
||||
|
||||
Ignoring this warning will only suppress the lint check; it will
|
||||
|
|
@ -1790,16 +1793,27 @@ List Of Warnings
|
|||
|
||||
For example, this is a missized index:
|
||||
|
||||
.. include:: ../../docs/gen/ex_WIDTH_1_faulty.rst
|
||||
.. include:: ../../docs/gen/ex_WIDTHEXPAND_1_faulty.rst
|
||||
|
||||
Results in:
|
||||
Results in a WIDTHEXPAND warning:
|
||||
|
||||
.. include:: ../../docs/gen/ex_WIDTH_1_msg.rst
|
||||
.. include:: ../../docs/gen/ex_WIDTHEXPAND_1_msg.rst
|
||||
|
||||
One possible fix:
|
||||
|
||||
.. include:: ../../docs/gen/ex_WIDTH_1_fixed.rst
|
||||
.. include:: ../../docs/gen/ex_WIDTHEXPAND_1_fixed.rst
|
||||
|
||||
.. option:: WIDTHTRUNC
|
||||
|
||||
A more granular WIDTH warning, for when a value is truncated
|
||||
|
||||
.. option:: WIDTHEXPAND
|
||||
|
||||
A more granular WIDTH warning, for when a value is zero expanded
|
||||
|
||||
.. option:: WIDTHXZEXPAND
|
||||
|
||||
A more granular WIDTH warning, for when a value is xz expanded
|
||||
|
||||
.. option:: WIDTHCONCAT
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@
|
|||
# cmake ..
|
||||
# cmake --build .
|
||||
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
cmake_policy(SET CMP0074 NEW)
|
||||
project(cmake_hello_c)
|
||||
|
||||
find_package(verilator HINTS $ENV{VERILATOR_ROOT} ${VERILATOR_ROOT})
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@
|
|||
# cmake ..
|
||||
# cmake --build .
|
||||
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
cmake_policy(SET CMP0074 NEW)
|
||||
project(cmake_hello_sc CXX)
|
||||
|
||||
find_package(verilator HINTS $ENV{VERILATOR_ROOT} ${VERILATOR_ROOT})
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@
|
|||
# cmake ..
|
||||
# cmake --build .
|
||||
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
cmake_policy(SET CMP0074 NEW)
|
||||
project(cmake_protect_lib)
|
||||
|
||||
find_package(verilator HINTS $ENV{VERILATOR_ROOT} ${VERILATOR_ROOT})
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@
|
|||
# cmake ..
|
||||
# cmake --build .
|
||||
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
cmake_policy(SET CMP0074 NEW)
|
||||
project(cmake_tracing_c)
|
||||
|
||||
find_package(verilator HINTS $ENV{VERILATOR_ROOT} ${VERILATOR_ROOT})
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@
|
|||
# cmake ..
|
||||
# cmake --build .
|
||||
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
cmake_policy(SET CMP0074 NEW)
|
||||
|
||||
project(cmake_tracing_sc_example CXX)
|
||||
|
||||
find_package(verilator HINTS $ENV{VERILATOR_ROOT} ${VERILATOR_ROOT})
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ CPPFLAGS += -DVL_DEBUG=1
|
|||
# Turn on some more compiler lint flags (when configured appropriately)
|
||||
# For testing inside Verilator, "configure --enable-ccwarn" will do this
|
||||
# automatically; otherwise you may want this unconditionally enabled
|
||||
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
|
||||
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
|
||||
USER_CPPFLAGS_WALL += -W -Werror -Wall
|
||||
endif
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ CPPFLAGS += -Wno-deprecated
|
|||
# Turn on some more flags (when configured appropriately)
|
||||
# For testing inside Verilator, "configure --enable-ccwarn" will do this
|
||||
# automatically; otherwise you may want this unconditionally enabled
|
||||
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
|
||||
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
|
||||
USER_CPPFLAGS_WALL += -W -Werror -Wall
|
||||
endif
|
||||
|
||||
|
|
|
|||
|
|
@ -1411,6 +1411,12 @@ IData VL_FERROR_IN(IData, std::string& outputr) VL_MT_SAFE {
|
|||
outputr = std::string{::std::strerror(ret)};
|
||||
return ret;
|
||||
}
|
||||
IData VL_FERROR_IW(IData fpi, int obits, WDataOutP outwp) VL_MT_SAFE {
|
||||
std::string output;
|
||||
const IData ret = VL_FERROR_IN(fpi, output /*ref*/);
|
||||
_vl_string_to_vint(obits, outwp, output.length(), output.c_str());
|
||||
return ret;
|
||||
}
|
||||
|
||||
IData VL_FOPEN_NN(const std::string& filename, const std::string& mode) {
|
||||
return Verilated::threadContextp()->impp()->fdNew(filename.c_str(), mode.c_str());
|
||||
|
|
@ -1793,6 +1799,7 @@ std::string VL_TO_STRING(CData lhs) { return VL_SFORMATF_NX("'h%0x", 8, lhs); }
|
|||
std::string VL_TO_STRING(SData lhs) { return VL_SFORMATF_NX("'h%0x", 16, lhs); }
|
||||
std::string VL_TO_STRING(IData lhs) { return VL_SFORMATF_NX("'h%0x", 32, lhs); }
|
||||
std::string VL_TO_STRING(QData lhs) { return VL_SFORMATF_NX("'h%0x", 64, lhs); }
|
||||
std::string VL_TO_STRING(double lhs) { return VL_SFORMATF_NX("%d", 64, lhs); }
|
||||
std::string VL_TO_STRING_W(int words, const WDataInP obj) {
|
||||
return VL_SFORMATF_NX("'h%0x", words * VL_EDATASIZE, obj);
|
||||
}
|
||||
|
|
@ -1861,6 +1868,33 @@ IData VL_ATOI_N(const std::string& str, int base) VL_PURE {
|
|||
if (errno != 0) v = 0;
|
||||
return static_cast<IData>(v);
|
||||
}
|
||||
IData VL_NTOI_I(int obits, const std::string& str) VL_PURE { return VL_NTOI_Q(obits, str); }
|
||||
QData VL_NTOI_Q(int obits, const std::string& str) VL_PURE {
|
||||
QData out = 0;
|
||||
const size_t procLen = std::min(str.length(), static_cast<size_t>(8));
|
||||
const char* const datap = str.data();
|
||||
int pos = static_cast<int>(str.length()) - 1;
|
||||
int bit = 0;
|
||||
while (bit < obits && pos >= 0) {
|
||||
out |= static_cast<QData>(datap[pos]) << VL_BITBIT_Q(bit);
|
||||
bit += 8;
|
||||
--pos;
|
||||
}
|
||||
return out & VL_MASK_Q(obits);
|
||||
}
|
||||
void VL_NTOI_W(int obits, WDataOutP owp, const std::string& str) VL_PURE {
|
||||
const int words = VL_WORDS_I(obits);
|
||||
for (int i = 0; i < words; ++i) owp[i] = 0;
|
||||
const char* const datap = str.data();
|
||||
int pos = static_cast<int>(str.length()) - 1;
|
||||
int bit = 0;
|
||||
while (bit < obits && pos >= 0) {
|
||||
owp[VL_BITWORD_I(bit)] |= static_cast<EData>(datap[pos]) << VL_BITBIT_I(bit);
|
||||
bit += 8;
|
||||
--pos;
|
||||
}
|
||||
owp[words - 1] &= VL_MASK_E(obits);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Readmem/writemem
|
||||
|
|
@ -2948,7 +2982,7 @@ VerilatedModule::~VerilatedModule() {
|
|||
// VerilatedVar:: Methods
|
||||
|
||||
// cppcheck-suppress unusedFunction // Used by applications
|
||||
uint32_t VerilatedVarProps::entSize() const {
|
||||
uint32_t VerilatedVarProps::entSize() const VL_MT_SAFE {
|
||||
uint32_t size = 1;
|
||||
switch (vltype()) {
|
||||
case VLVT_PTR: size = sizeof(void*); break;
|
||||
|
|
@ -2968,7 +3002,7 @@ size_t VerilatedVarProps::totalSize() const {
|
|||
return size;
|
||||
}
|
||||
|
||||
void* VerilatedVarProps::datapAdjustIndex(void* datap, int dim, int indx) const {
|
||||
void* VerilatedVarProps::datapAdjustIndex(void* datap, int dim, int indx) const VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(dim <= 0 || dim > udims())) return nullptr;
|
||||
if (VL_UNLIKELY(indx < low(dim) || indx > high(dim))) return nullptr;
|
||||
const int indxAdj = indx - low(dim);
|
||||
|
|
@ -3087,7 +3121,7 @@ void* VerilatedScope::exportFindNullError(int funcnum) VL_MT_SAFE {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void* VerilatedScope::exportFindError(int funcnum) const {
|
||||
void* VerilatedScope::exportFindError(int funcnum) const VL_MT_SAFE {
|
||||
// Slowpath - Called only when find has failed
|
||||
const std::string msg
|
||||
= (std::string{"Testbench C called '"} + VerilatedImp::exportName(funcnum)
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@
|
|||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
|
@ -175,6 +176,19 @@ public:
|
|||
void unlock() VL_RELEASE() VL_MT_SAFE { m_mutex.unlock(); }
|
||||
/// Try to acquire mutex. Returns true on success, and false on failure.
|
||||
bool try_lock() VL_TRY_ACQUIRE(true) VL_MT_SAFE { return m_mutex.try_lock(); }
|
||||
/// Acquire/lock mutex and check for stop request
|
||||
/// It tries to lock the mutex and if it fails, it check if stop request was send.
|
||||
/// It returns after locking mutex.
|
||||
/// This function should be extracted to V3ThreadPool, but due to clang thread-safety
|
||||
/// limitations it needs to be placed here.
|
||||
void lockCheckStopRequest(std::function<void()> checkStopRequestFunction)
|
||||
VL_ACQUIRE() VL_MT_SAFE {
|
||||
while (true) {
|
||||
checkStopRequestFunction();
|
||||
if (m_mutex.try_lock()) return;
|
||||
VL_CPU_RELAX();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Lock guard for mutex (ala std::unique_lock), wrapped to allow -fthread_safety checks
|
||||
|
|
@ -192,10 +206,19 @@ public:
|
|||
}
|
||||
/// Destruct and unlock the mutex
|
||||
~VerilatedLockGuard() VL_RELEASE() { m_mutexr.unlock(); }
|
||||
/// Unlock the mutex
|
||||
void lock() VL_ACQUIRE() VL_MT_SAFE { m_mutexr.lock(); }
|
||||
/// Lock the mutex
|
||||
void lock() VL_ACQUIRE() VL_MT_SAFE { m_mutexr.lock(); }
|
||||
/// Unlock the mutex
|
||||
void unlock() VL_RELEASE() VL_MT_SAFE { m_mutexr.unlock(); }
|
||||
/// Acquire/lock mutex and check for stop request.
|
||||
/// It tries to lock the mutex and if it fails, it check if stop request was send.
|
||||
/// It returns after locking mutex.
|
||||
/// This function should be extracted to V3ThreadPool, but due to clang thread-safety
|
||||
/// limitations it needs to be placed here.
|
||||
void lockCheckStopRequest(std::function<void()> checkStopRequestFunction)
|
||||
VL_ACQUIRE() VL_MT_SAFE {
|
||||
m_mutexr.lockCheckStopRequest(checkStopRequestFunction);
|
||||
}
|
||||
};
|
||||
|
||||
// Internals: Remember the calling thread at construction time, and make
|
||||
|
|
@ -624,7 +647,7 @@ public: // But internals only - called from VerilatedModule's
|
|||
VerilatedVar* varFind(const char* namep) const VL_MT_SAFE_POSTINIT;
|
||||
VerilatedVarNameMap* varsp() const VL_MT_SAFE_POSTINIT { return m_varsp; }
|
||||
void scopeDump() const;
|
||||
void* exportFindError(int funcnum) const;
|
||||
void* exportFindError(int funcnum) const VL_MT_SAFE;
|
||||
static void* exportFindNullError(int funcnum) VL_MT_SAFE;
|
||||
static void* exportFind(const VerilatedScope* scopep, int funcnum) VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(!scopep)) return exportFindNullError(funcnum);
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ VK_CPPFLAGS_ALWAYS += \
|
|||
-DVM_TRACE_VCD=$(VM_TRACE_VCD) \
|
||||
$(CFG_CXXFLAGS_NO_UNUSED) \
|
||||
|
||||
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
|
||||
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
|
||||
VK_CPPFLAGS_WALL += -Wall $(CFG_CXXFLAGS_WEXTRA) -Werror
|
||||
endif
|
||||
|
||||
|
|
|
|||
|
|
@ -2164,6 +2164,9 @@ inline IData VL_CMP_NN(const std::string& lhs, const std::string& rhs, bool igno
|
|||
}
|
||||
|
||||
extern IData VL_ATOI_N(const std::string& str, int base) VL_PURE;
|
||||
extern IData VL_NTOI_I(int obits, const std::string& str) VL_PURE;
|
||||
extern QData VL_NTOI_Q(int obits, const std::string& str) VL_PURE;
|
||||
extern void VL_NTOI_W(int obits, WDataOutP owp, const std::string& str) VL_PURE;
|
||||
|
||||
extern IData VL_FGETS_NI(std::string& dest, IData fpi) VL_MT_SAFE;
|
||||
|
||||
|
|
@ -2212,6 +2215,7 @@ extern std::string VL_TOLOWER_NN(const std::string& ld) VL_PURE;
|
|||
extern std::string VL_TOUPPER_NN(const std::string& ld) VL_PURE;
|
||||
|
||||
extern IData VL_FERROR_IN(IData fpi, std::string& outputr) VL_MT_SAFE;
|
||||
extern IData VL_FERROR_IW(IData fpi, int obits, WDataOutP outwp) VL_MT_SAFE;
|
||||
extern IData VL_FOPEN_NN(const std::string& filename, const std::string& mode) VL_MT_SAFE;
|
||||
extern IData VL_FOPEN_MCD_N(const std::string& filename) VL_MT_SAFE;
|
||||
extern void VL_READMEM_N(bool hex, int bits, QData depth, int array_lsb,
|
||||
|
|
|
|||
|
|
@ -26,6 +26,10 @@
|
|||
// verilator lint_off TIMESCALEMOD
|
||||
// verilator lint_off UNUSEDSIGNAL
|
||||
package std;
|
||||
// The process class is not implemented, but it's predeclared here,
|
||||
// so the linter accepts references to it.
|
||||
typedef class process;
|
||||
|
||||
class mailbox #(type T);
|
||||
protected int m_bound;
|
||||
protected T m_queue[$];
|
||||
|
|
@ -112,6 +116,3 @@ package std;
|
|||
endfunction
|
||||
endclass
|
||||
endpackage
|
||||
|
||||
// verilator lint_off IMPORTSTAR
|
||||
import std::*;
|
||||
|
|
|
|||
|
|
@ -137,35 +137,35 @@ public:
|
|||
~VerilatedVarProps() = default;
|
||||
// METHODS
|
||||
bool magicOk() const { return m_magic == MAGIC; }
|
||||
VerilatedVarType vltype() const { return m_vltype; }
|
||||
VerilatedVarType vltype() const VL_MT_SAFE { return m_vltype; }
|
||||
VerilatedVarFlags vldir() const {
|
||||
return static_cast<VerilatedVarFlags>(static_cast<int>(m_vlflags) & VLVF_MASK_DIR);
|
||||
}
|
||||
uint32_t entSize() const;
|
||||
uint32_t entSize() const VL_MT_SAFE;
|
||||
bool isPublicRW() const { return ((m_vlflags & VLVF_PUB_RW) != 0); }
|
||||
// DPI compatible C standard layout
|
||||
bool isDpiCLayout() const { return ((m_vlflags & VLVF_DPI_CLAY) != 0); }
|
||||
int udims() const { return m_udims; }
|
||||
int udims() const VL_MT_SAFE { return m_udims; }
|
||||
int dims() const { return m_pdims + m_udims; }
|
||||
const VerilatedRange& packed() const { return m_packed; }
|
||||
const VerilatedRange& packed() const VL_MT_SAFE { return m_packed; }
|
||||
const VerilatedRange& unpacked() const { return m_unpacked[0]; }
|
||||
// DPI accessors
|
||||
int left(int dim) const {
|
||||
int left(int dim) const VL_MT_SAFE {
|
||||
return dim == 0 ? m_packed.left()
|
||||
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].left()
|
||||
: 0;
|
||||
}
|
||||
int right(int dim) const {
|
||||
int right(int dim) const VL_MT_SAFE {
|
||||
return dim == 0 ? m_packed.right()
|
||||
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].right()
|
||||
: 0;
|
||||
}
|
||||
int low(int dim) const {
|
||||
int low(int dim) const VL_MT_SAFE {
|
||||
return dim == 0 ? m_packed.low()
|
||||
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].low()
|
||||
: 0;
|
||||
}
|
||||
int high(int dim) const {
|
||||
int high(int dim) const VL_MT_SAFE {
|
||||
return dim == 0 ? m_packed.high()
|
||||
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].high()
|
||||
: 0;
|
||||
|
|
@ -175,7 +175,7 @@ public:
|
|||
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].increment()
|
||||
: 0;
|
||||
}
|
||||
int elements(int dim) const {
|
||||
int elements(int dim) const VL_MT_SAFE {
|
||||
return dim == 0 ? m_packed.elements()
|
||||
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].elements()
|
||||
: 0;
|
||||
|
|
@ -183,7 +183,7 @@ public:
|
|||
// Total size in bytes (note DPI limited to 4GB)
|
||||
size_t totalSize() const;
|
||||
// Adjust a data pointer to access a given array element, NULL if something goes bad
|
||||
void* datapAdjustIndex(void* datap, int dim, int indx) const;
|
||||
void* datapAdjustIndex(void* datap, int dim, int indx) const VL_MT_SAFE;
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
|
|
@ -203,22 +203,22 @@ public:
|
|||
, m_datap{const_cast<void*>(datap)} {}
|
||||
~VerilatedDpiOpenVar() = default;
|
||||
// METHODS
|
||||
void* datap() const { return m_datap; }
|
||||
void* datap() const VL_MT_SAFE { return m_datap; }
|
||||
// METHODS - from VerilatedVarProps
|
||||
bool magicOk() const { return m_propsp->magicOk(); }
|
||||
VerilatedVarType vltype() const { return m_propsp->vltype(); }
|
||||
bool isDpiStdLayout() const { return m_propsp->isDpiCLayout(); }
|
||||
const VerilatedRange& packed() const { return m_propsp->packed(); }
|
||||
const VerilatedRange& unpacked() const { return m_propsp->unpacked(); }
|
||||
int udims() const { return m_propsp->udims(); }
|
||||
int left(int dim) const { return m_propsp->left(dim); }
|
||||
int right(int dim) const { return m_propsp->right(dim); }
|
||||
int udims() const VL_MT_SAFE { return m_propsp->udims(); }
|
||||
int left(int dim) const VL_MT_SAFE { return m_propsp->left(dim); }
|
||||
int right(int dim) const VL_MT_SAFE { return m_propsp->right(dim); }
|
||||
int low(int dim) const { return m_propsp->low(dim); }
|
||||
int high(int dim) const { return m_propsp->high(dim); }
|
||||
int increment(int dim) const { return m_propsp->increment(dim); }
|
||||
int elements(int dim) const { return m_propsp->elements(dim); }
|
||||
size_t totalSize() const { return m_propsp->totalSize(); }
|
||||
void* datapAdjustIndex(void* datap, int dim, int indx) const {
|
||||
void* datapAdjustIndex(void* datap, int dim, int indx) const VL_MT_SAFE {
|
||||
return m_propsp->datapAdjustIndex(datap, dim, indx);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ extern std::string VL_TO_STRING(CData lhs);
|
|||
extern std::string VL_TO_STRING(SData lhs);
|
||||
extern std::string VL_TO_STRING(IData lhs);
|
||||
extern std::string VL_TO_STRING(QData lhs);
|
||||
extern std::string VL_TO_STRING(double lhs);
|
||||
inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; }
|
||||
extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
|
||||
|
||||
|
|
@ -1168,7 +1169,9 @@ public:
|
|||
explicit VlClassRef(VlNull){};
|
||||
template <typename... T_Args>
|
||||
VlClassRef(VlDeleter& deleter, T_Args&&... args)
|
||||
: m_objp{new T_Class{std::forward<T_Args>(args)...}} {
|
||||
// () required here to avoid narrowing conversion warnings,
|
||||
// when a new() has an e.g. CData type and passed a 1U.
|
||||
: m_objp{new T_Class(std::forward<T_Args>(args)...)} {
|
||||
m_objp->m_deleterp = &deleter;
|
||||
refCountInc();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -519,6 +519,8 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
|
|||
# define VL_CPU_RELAX() asm volatile("nop" ::: "memory");
|
||||
#elif defined(__aarch64__) || defined(__arm__)
|
||||
# define VL_CPU_RELAX() asm volatile("yield" ::: "memory")
|
||||
#elif defined(__hppa__) // HPPA does not currently have yield/pause
|
||||
# define VL_CPU_RELAX() asm volatile("nop" ::: "memory")
|
||||
#elif defined(__loongarch__) // LoongArch does not currently have yield/pause
|
||||
# define VL_CPU_RELAX() asm volatile("nop" ::: "memory")
|
||||
#elif defined(__mips64el__) || defined(__mips__) || defined(__mips64__) || defined(__mips64)
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ done
|
|||
|
||||
if [ x"$src" = x ]
|
||||
then
|
||||
echo "install: no input file specified"
|
||||
echo "install: no input file specified"
|
||||
exit 1
|
||||
else
|
||||
true
|
||||
|
|
@ -137,7 +137,7 @@ else
|
|||
|
||||
if [ x"$dst" = x ]
|
||||
then
|
||||
echo "install: no destination specified"
|
||||
echo "install: no destination specified"
|
||||
exit 1
|
||||
else
|
||||
true
|
||||
|
|
|
|||
|
|
@ -239,6 +239,7 @@ class CallAnnotationsValidator:
|
|||
self._call_location: Optional[FunctionInfo] = None
|
||||
self._caller: Optional[FunctionInfo] = None
|
||||
self._level: int = 0
|
||||
self._constructor_context: int = 0
|
||||
|
||||
def compile_and_analyze_file(self, source_file: str,
|
||||
compiler_args: list[str],
|
||||
|
|
@ -300,6 +301,67 @@ class CallAnnotationsValidator:
|
|||
annotations = VlAnnotations.from_nodes_list(children)
|
||||
return (True, refd, annotations, children)
|
||||
|
||||
def check_mt_safe_call(self, node: clang.cindex.Cursor,
|
||||
refd: clang.cindex.Cursor,
|
||||
annotations: VlAnnotations):
|
||||
is_mt_safe = False
|
||||
|
||||
if annotations.is_mt_safe_call():
|
||||
is_mt_safe = True
|
||||
elif not annotations.is_mt_unsafe_call():
|
||||
# Check whether the object the method is called on is mt-safe
|
||||
def find_object_ref(node):
|
||||
try:
|
||||
node = next(node.get_children())
|
||||
if node.kind == CursorKind.DECL_REF_EXPR:
|
||||
# Operator on an argument or local object
|
||||
return node
|
||||
if node.kind != CursorKind.MEMBER_REF_EXPR:
|
||||
return None
|
||||
if node.referenced and node.referenced.kind == CursorKind.FIELD_DECL:
|
||||
# Operator on a member object
|
||||
return node
|
||||
node = next(node.get_children())
|
||||
if node.kind == CursorKind.UNEXPOSED_EXPR:
|
||||
node = next(node.get_children())
|
||||
return node
|
||||
except StopIteration:
|
||||
return None
|
||||
|
||||
refn = find_object_ref(node)
|
||||
if self._constructor_context and not refn:
|
||||
# we are in constructor and no object reference means
|
||||
# we are calling local method. It is MT safe
|
||||
# only if this method is also only calling local methods or
|
||||
# MT-safe methods
|
||||
self.iterate_children(refd.get_children(),
|
||||
self.dispatch_node_inside_definition)
|
||||
is_mt_safe = True
|
||||
# class/struct member
|
||||
elif refn and refn.kind == CursorKind.MEMBER_REF_EXPR and refn.referenced:
|
||||
refn = refn.referenced
|
||||
refna = VlAnnotations.from_nodes_list(refn.get_children())
|
||||
if refna.guarded or self._constructor_context:
|
||||
is_mt_safe = True
|
||||
# variable
|
||||
elif refn and refn.kind == CursorKind.DECL_REF_EXPR and refn.referenced:
|
||||
# This is probably a local or an argument.
|
||||
# Calling methods on local pointers or references is MT-safe,
|
||||
# but on argument pointers or references is not.
|
||||
if "*" not in refn.type.spelling and "&" not in refn.type.spelling:
|
||||
is_mt_safe = True
|
||||
# local variable
|
||||
if refn.referenced.kind == CursorKind.VAR_DECL:
|
||||
is_mt_safe = True
|
||||
elif refn and refn.kind == CursorKind.CALL_EXPR:
|
||||
if self._constructor_context:
|
||||
# call to local function from constructor context
|
||||
# safe if this function also calling local methods or
|
||||
# MT-safe methods
|
||||
self.dispatch_call_node(refn)
|
||||
is_mt_safe = True
|
||||
return is_mt_safe
|
||||
|
||||
# Call handling
|
||||
|
||||
def process_method_call(self, node: clang.cindex.Cursor,
|
||||
|
|
@ -310,43 +372,7 @@ class CallAnnotationsValidator:
|
|||
|
||||
# MT-safe context
|
||||
if ctx.is_mt_safe_context():
|
||||
is_mt_safe = False
|
||||
|
||||
if annotations.is_mt_safe_call():
|
||||
is_mt_safe = True
|
||||
elif not annotations.is_mt_unsafe_call():
|
||||
# Check whether the object the method is called on is mt-safe
|
||||
def find_object_ref(node):
|
||||
try:
|
||||
node = next(node.get_children())
|
||||
if node.kind == CursorKind.DECL_REF_EXPR:
|
||||
# Operator on an argument or local object
|
||||
return node
|
||||
if node.kind != CursorKind.MEMBER_REF_EXPR:
|
||||
return None
|
||||
if node.referenced and node.referenced.kind == CursorKind.FIELD_DECL:
|
||||
# Operator on a member object
|
||||
return node
|
||||
node = next(node.get_children())
|
||||
if node.kind == CursorKind.UNEXPOSED_EXPR:
|
||||
node = next(node.get_children())
|
||||
return node
|
||||
except StopIteration:
|
||||
return None
|
||||
|
||||
refn = find_object_ref(node)
|
||||
# class/struct member
|
||||
if refn and refn.kind == CursorKind.MEMBER_REF_EXPR and refn.referenced:
|
||||
refn = refn.referenced
|
||||
refna = VlAnnotations.from_nodes_list(refn.get_children())
|
||||
if refna.guarded:
|
||||
is_mt_safe = True
|
||||
# variable
|
||||
elif refn and refn.kind == CursorKind.DECL_REF_EXPR and refn.referenced:
|
||||
# This is probably a local or an argument. Assume it's safe.
|
||||
is_mt_safe = True
|
||||
|
||||
if not is_mt_safe:
|
||||
if not self.check_mt_safe_call(node, refd, annotations):
|
||||
self.emit_diagnostic(
|
||||
FunctionInfo.from_node(refd, refd, annotations),
|
||||
DiagnosticKind.NON_MT_SAFE_CALL_IN_MT_SAFE_CTX)
|
||||
|
|
@ -380,7 +406,13 @@ class CallAnnotationsValidator:
|
|||
assert self._call_location
|
||||
ctx = self._call_location.annotations
|
||||
|
||||
# Constructors are always OK in MT-safe context.
|
||||
# Constructors are OK in MT-safe context
|
||||
# only if they call local methods or MT-safe functions.
|
||||
if ctx.is_mt_safe_context() or self._constructor_context:
|
||||
self._constructor_context += 1
|
||||
self.iterate_children(refd.get_children(),
|
||||
self.dispatch_node_inside_definition)
|
||||
self._constructor_context -= 1
|
||||
|
||||
# pure context
|
||||
if ctx.is_pure_context():
|
||||
|
|
@ -412,6 +444,8 @@ class CallAnnotationsValidator:
|
|||
or refd.kind == CursorKind.CXX_METHOD
|
||||
and refd.is_static_method()):
|
||||
self.process_function_call(refd, annotations)
|
||||
self.iterate_children(node.get_children(),
|
||||
self.dispatch_node_inside_definition)
|
||||
return
|
||||
# Function pointer
|
||||
if refd.kind in [
|
||||
|
|
@ -419,18 +453,26 @@ class CallAnnotationsValidator:
|
|||
CursorKind.PARM_DECL
|
||||
]:
|
||||
self.process_function_call(refd, annotations)
|
||||
self.iterate_children(node.get_children(),
|
||||
self.dispatch_node_inside_definition)
|
||||
return
|
||||
# Non-static class methods
|
||||
if refd.kind == CursorKind.CXX_METHOD:
|
||||
self.process_method_call(node, refd, annotations)
|
||||
self.iterate_children(node.get_children(),
|
||||
self.dispatch_node_inside_definition)
|
||||
return
|
||||
# Conversion method (e.g. `operator int()`)
|
||||
if refd.kind == CursorKind.CONVERSION_FUNCTION:
|
||||
self.process_method_call(node, refd, annotations)
|
||||
self.iterate_children(node.get_children(),
|
||||
self.dispatch_node_inside_definition)
|
||||
return
|
||||
# Constructors
|
||||
if refd.kind == CursorKind.CONSTRUCTOR:
|
||||
self.process_constructor_call(refd, annotations)
|
||||
self.iterate_children(node.get_children(),
|
||||
self.dispatch_node_inside_definition)
|
||||
return
|
||||
|
||||
# Ignore other callables
|
||||
|
|
@ -461,9 +503,6 @@ class CallAnnotationsValidator:
|
|||
|
||||
assert refd is not None
|
||||
|
||||
prev_caller = self._caller
|
||||
prev_call_location = self._call_location
|
||||
|
||||
def_annotations = VlAnnotations.from_nodes_list(node_children)
|
||||
|
||||
if not (def_annotations.is_empty() or def_annotations == annotations):
|
||||
|
|
@ -485,8 +524,7 @@ class CallAnnotationsValidator:
|
|||
self.iterate_children(node_children,
|
||||
self.dispatch_node_inside_definition)
|
||||
|
||||
self._call_location = prev_call_location
|
||||
self._caller = prev_caller
|
||||
self._call_location = None
|
||||
|
||||
return None
|
||||
|
||||
|
|
@ -704,8 +742,7 @@ class TopDownSummaryPrinter():
|
|||
info: FunctionInfo
|
||||
calees: set[FunctionInfo]
|
||||
mismatch: Optional[FunctionInfo] = None
|
||||
non_mt_safe_call_in_mt_safe: Optional[FunctionInfo] = None
|
||||
non_pure_call_in_pure: Optional[FunctionInfo] = None
|
||||
reason: Optional[DiagnosticKind] = None
|
||||
|
||||
def __init__(self):
|
||||
self._is_first_group = True
|
||||
|
|
@ -727,15 +764,12 @@ class TopDownSummaryPrinter():
|
|||
if func is None:
|
||||
func = TopDownSummaryPrinter.FunctionCallees(diag.source, set())
|
||||
self._funcs[usr] = func
|
||||
func.reason = diag.kind
|
||||
if diag.kind == DiagnosticKind.ANNOTATIONS_DEF_DECL_MISMATCH:
|
||||
func.mismatch = diag.target
|
||||
else:
|
||||
func.calees.add(diag.target)
|
||||
self._unsafe_in_safe.add(diag.target.usr)
|
||||
if diag.kind == DiagnosticKind.NON_MT_SAFE_CALL_IN_MT_SAFE_CTX:
|
||||
func.non_mt_safe_call_in_mt_safe = diag.target
|
||||
elif diag.kind == DiagnosticKind.NON_PURE_CALL_IN_PURE_CTX:
|
||||
func.non_pure_call_in_pure = diag.target
|
||||
|
||||
def print_summary(self, root_dir: str):
|
||||
row_groups: dict[str, list[list[str]]] = {}
|
||||
|
|
@ -748,11 +782,11 @@ class TopDownSummaryPrinter():
|
|||
|
||||
row_group = []
|
||||
name = f"\"{func_info.name}\" "
|
||||
if func.mismatch:
|
||||
if func.reason == DiagnosticKind.ANNOTATIONS_DEF_DECL_MISMATCH:
|
||||
name += "declaration does not match definition"
|
||||
elif func.non_mt_safe_call_in_mt_safe:
|
||||
elif func.reason == DiagnosticKind.NON_MT_SAFE_CALL_IN_MT_SAFE_CTX:
|
||||
name += "is mtsafe but calls non-mtsafe function(s)"
|
||||
elif func.non_pure_call_in_pure:
|
||||
elif func.reason == DiagnosticKind.NON_PURE_CALL_IN_PURE_CTX:
|
||||
name += "is pure but calls non-pure function(s)"
|
||||
else:
|
||||
name += "for unknown reason (please add description)"
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ set(HEADERS
|
|||
V3Case.h
|
||||
V3Cast.h
|
||||
V3CCtors.h
|
||||
V3Cdc.h
|
||||
V3Class.h
|
||||
V3Clean.h
|
||||
V3Clock.h
|
||||
|
|
@ -150,6 +149,7 @@ set(HEADERS
|
|||
V3SymTable.h
|
||||
V3Table.h
|
||||
V3Task.h
|
||||
V3ThreadPool.h
|
||||
V3Timing.h
|
||||
V3Trace.h
|
||||
V3TraceDecl.h
|
||||
|
|
@ -186,7 +186,6 @@ set(COMMON_SOURCES
|
|||
V3CUse.cpp
|
||||
V3Case.cpp
|
||||
V3Cast.cpp
|
||||
V3Cdc.cpp
|
||||
V3Class.cpp
|
||||
V3Clean.cpp
|
||||
V3Clock.cpp
|
||||
|
|
@ -283,6 +282,7 @@ set(COMMON_SOURCES
|
|||
V3Subst.cpp
|
||||
V3Table.cpp
|
||||
V3Task.cpp
|
||||
V3ThreadPool.cpp
|
||||
V3Trace.cpp
|
||||
V3TraceDecl.cpp
|
||||
V3Tristate.cpp
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ obj_dbg:
|
|||
.PHONY: ../bin/verilator_bin$(EXEEXT) ../bin/verilator_bin_dbg$(EXEEXT) ../bin/verilator_coverage_bin_dbg$(EXEEXT)
|
||||
|
||||
opt: ../bin/verilator_bin$(EXEEXT)
|
||||
ifeq ($(VERILATOR_NO_OPT_BUILD),1) # Faster laptop development... One build
|
||||
ifeq ($(VERILATOR_NO_OPT_BUILD),1) # Faster laptop development... One build
|
||||
../bin/verilator_bin$(EXEEXT): ../bin/verilator_bin_dbg$(EXEEXT)
|
||||
-cp -p $< $@.tmp
|
||||
-mv -f $@.tmp $@
|
||||
|
|
@ -70,7 +70,7 @@ dbg: ../bin/verilator_bin_dbg$(EXEEXT) ../bin/verilator_coverage_bin_dbg$(EXEEXT
|
|||
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj
|
||||
|
||||
ifneq ($(VERILATOR_NO_OPT_BUILD),1) # Faster laptop development... don't rebuild each commit
|
||||
ifneq ($(UNDER_GIT),) # If local git tree... else don't burden users
|
||||
ifneq ($(UNDER_GIT),) # If local git tree... else don't burden users
|
||||
GIT_CHANGE_DEP = ${srcdir}/../.git/logs/HEAD
|
||||
endif
|
||||
endif
|
||||
|
|
@ -78,7 +78,7 @@ endif
|
|||
prefiles::
|
||||
prefiles:: config_rev.h
|
||||
# This output goes into srcdir if locally configured, as we need to distribute it as part of the kit.
|
||||
config_rev.h: ${srcdir}/config_rev $(GIT_CHANGE_DEP)
|
||||
config_rev.h: ${srcdir}/config_rev $(GIT_CHANGE_DEP)
|
||||
$(PYTHON3) ${srcdir}/config_rev ${srcdir} >$@
|
||||
|
||||
# Human convenience
|
||||
|
|
|
|||
|
|
@ -94,9 +94,9 @@ LIBS = $(CFG_LIBS) -lm
|
|||
|
||||
CPPFLAGS += -MMD
|
||||
CPPFLAGS += -I. -I$(bldsrc) -I$(srcdir) -I$(incdir) -I../../include
|
||||
#CPPFLAGS += -DVL_LEAK_CHECKS # If running valgrind or other hunting tool
|
||||
#CPPFLAGS += -DVL_LEAK_CHECKS # If running valgrind or other hunting tool
|
||||
CPPFLAGS += -MP # Only works on recent GCC versions
|
||||
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
|
||||
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
|
||||
CPPFLAGS += -W -Wall $(CFG_CXXFLAGS_WEXTRA) $(CFG_CXXFLAGS_SRC) -Werror
|
||||
#CPPFLAGS += -pedantic-errors
|
||||
else
|
||||
|
|
@ -114,7 +114,7 @@ ifeq ($(CFG_WITH_DEFENV),yes)
|
|||
CPPFLAGS += -DDEFENV_SYSTEMC_ARCH=\"$(SYSTEMC_ARCH)\"
|
||||
CPPFLAGS += -DDEFENV_SYSTEMC_INCLUDE=\"$(SYSTEMC_INCLUDE)\"
|
||||
CPPFLAGS += -DDEFENV_SYSTEMC_LIBDIR=\"$(SYSTEMC_LIBDIR)\"
|
||||
ifeq ($(VERILATOR_ROOT),) # Use what we're given, or intuit
|
||||
ifeq ($(VERILATOR_ROOT),) # Use what we're given, or intuit
|
||||
CPPFLAGS += -DDEFENV_VERILATOR_ROOT=\"$(pkgdatadir)\"
|
||||
else
|
||||
CPPFLAGS += -DDEFENV_VERILATOR_ROOT=\"$(VERILATOR_ROOT)\"
|
||||
|
|
@ -155,7 +155,7 @@ RAW_OBJS = \
|
|||
V3ActiveTop.o \
|
||||
V3Assert.o \
|
||||
V3AssertPre.o \
|
||||
V3Ast.o \
|
||||
V3Ast.o \
|
||||
V3AstNodes.o \
|
||||
V3Begin.o \
|
||||
V3Branch.o \
|
||||
|
|
@ -164,7 +164,6 @@ RAW_OBJS = \
|
|||
V3CUse.o \
|
||||
V3Case.o \
|
||||
V3Cast.o \
|
||||
V3Cdc.o \
|
||||
V3Class.o \
|
||||
V3Clean.o \
|
||||
V3Clock.o \
|
||||
|
|
@ -265,6 +264,7 @@ RAW_OBJS = \
|
|||
V3TSP.o \
|
||||
V3Table.o \
|
||||
V3Task.o \
|
||||
V3ThreadPool.o \
|
||||
V3Timing.o \
|
||||
V3Trace.o \
|
||||
V3TraceDecl.o \
|
||||
|
|
@ -322,26 +322,26 @@ V3Number_test: V3Number_test.o
|
|||
|
||||
#### Modules
|
||||
|
||||
%__gen.cpp: %.cpp $(ASTGEN) $(AST_DEFS) $(DFG_DEFS)
|
||||
%__gen.cpp: %.cpp $(ASTGEN) $(AST_DEFS) $(DFG_DEFS)
|
||||
$(PYTHON3) $(ASTGEN) $(ASTGENFLAGS) $*.cpp
|
||||
|
||||
.SECONDARY:
|
||||
|
||||
%.o: %.cpp
|
||||
%.o: %.cpp
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSWALL} -c $< -o $@
|
||||
%.o: %.c
|
||||
%.o: %.c
|
||||
$(OBJCACHE) ${CC} ${CFLAGS} ${CPPFLAGSWALL} -c $< -o $@
|
||||
|
||||
V3ParseLex.o: V3ParseLex.cpp V3Lexer.yy.cpp V3ParseBison.c
|
||||
V3ParseLex.o: V3ParseLex.cpp V3Lexer.yy.cpp V3ParseBison.c
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSPARSER} -c $< -o $@
|
||||
|
||||
V3ParseGrammar.o: V3ParseGrammar.cpp V3ParseBison.c
|
||||
V3ParseGrammar.o: V3ParseGrammar.cpp V3ParseBison.c
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSPARSER} -c $< -o $@
|
||||
|
||||
V3ParseImp.o: V3ParseImp.cpp V3ParseBison.c
|
||||
V3ParseImp.o: V3ParseImp.cpp V3ParseBison.c
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSPARSER} -c $< -o $@
|
||||
|
||||
V3PreProc.o: V3PreProc.cpp V3PreLex.yy.cpp
|
||||
V3PreProc.o: V3PreProc.cpp V3PreLex.yy.cpp
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSPARSER} -c $< -o $@
|
||||
|
||||
#### Generated files
|
||||
|
|
@ -365,14 +365,14 @@ V3ParseBison.c: verilog.y $(BISONPRE)
|
|||
@echo "If you get errors from verilog.y below, try upgrading bison to version 1.875 or newer."
|
||||
$(PYTHON3) $(BISONPRE) --yacc ${YACC} -d -v -o V3ParseBison.c $<
|
||||
|
||||
V3Lexer_pregen.yy.cpp: verilog.l V3ParseBison.h $(HEADERS)
|
||||
V3Lexer_pregen.yy.cpp: verilog.l V3ParseBison.h $(HEADERS)
|
||||
${LEX} --version
|
||||
${LEX} ${LFLAGS} -o$@ $<
|
||||
|
||||
V3Lexer.yy.cpp: V3Lexer_pregen.yy.cpp $(FLEXFIX)
|
||||
V3Lexer.yy.cpp: V3Lexer_pregen.yy.cpp $(FLEXFIX)
|
||||
$(PYTHON3) $(FLEXFIX) V3Lexer <$< >$@
|
||||
|
||||
V3PreLex_pregen.yy.cpp: V3PreLex.l $(HEADERS)
|
||||
V3PreLex_pregen.yy.cpp: V3PreLex.l $(HEADERS)
|
||||
${LEX} --version
|
||||
${LEX} ${LFLAGS} -o$@ $<
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ private:
|
|||
AstVar* m_monitorOffVarp = nullptr; // $monitoroff variable
|
||||
unsigned m_modPastNum = 0; // Module past numbering
|
||||
unsigned m_modStrobeNum = 0; // Module $strobe numbering
|
||||
const AstNodeProcedure* m_procedurep = nullptr; // Current procedure
|
||||
VDouble0 m_statCover; // Statistic tracking
|
||||
VDouble0 m_statAsNotImm; // Statistic tracking
|
||||
VDouble0 m_statAsImm; // Statistic tracking
|
||||
|
|
@ -145,6 +146,11 @@ private:
|
|||
} else {
|
||||
UASSERT_OBJ(sentreep, nodep, "Concurrent assertions must have sensitivity");
|
||||
sentreep->unlinkFrBack();
|
||||
if (m_procedurep) {
|
||||
// To support this need queue of asserts to activate
|
||||
nodep->v3error("Unsupported: Procedural concurent assertion with"
|
||||
" clocking event inside always (IEEE 1800-2917 16.14.6)");
|
||||
}
|
||||
}
|
||||
//
|
||||
AstNode* bodysp = nullptr;
|
||||
|
|
@ -350,8 +356,7 @@ private:
|
|||
AstNodeExpr* const exprp = nodep->exprp()->unlinkFrBack();
|
||||
AstNodeExpr* inp = newSampledExpr(exprp);
|
||||
AstVar* invarp = nullptr;
|
||||
AstSenTree* const sentreep = nodep->sentreep();
|
||||
sentreep->unlinkFrBack();
|
||||
AstSenTree* const sentreep = nodep->sentreep()->unlinkFrBack();
|
||||
AstAlways* const alwaysp
|
||||
= new AstAlways{nodep->fileline(), VAlwaysKwd::ALWAYS, sentreep, nullptr};
|
||||
m_modp->addStmtsp(alwaysp);
|
||||
|
|
@ -496,6 +501,11 @@ private:
|
|||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstNodeProcedure* nodep) override {
|
||||
VL_RESTORER(m_procedurep);
|
||||
m_procedurep = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstBegin* nodep) override {
|
||||
// This code is needed rather than a visitor in V3Begin,
|
||||
// because V3Assert is called before V3Begin
|
||||
|
|
|
|||
|
|
@ -63,9 +63,10 @@ private:
|
|||
|
||||
// METHODS
|
||||
|
||||
AstSenTree* newSenTree(AstNode* nodep) {
|
||||
AstSenTree* newSenTree(AstNode* nodep, AstSenTree* useTreep = nullptr) {
|
||||
// Create sentree based on clocked or default clock
|
||||
// Return nullptr for always
|
||||
if (useTreep) return useTreep;
|
||||
AstSenTree* newp = nullptr;
|
||||
AstSenItem* senip = m_senip;
|
||||
if (!senip && m_defaultClockingp) senip = m_defaultClockingp->sensesp();
|
||||
|
|
@ -362,17 +363,19 @@ private:
|
|||
clearAssertInfo();
|
||||
}
|
||||
void visit(AstFell* nodep) override {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateChildren(nodep);
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
||||
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
||||
AstSenTree* sentreep = nodep->sentreep();
|
||||
if (sentreep) sentreep->unlinkFrBack();
|
||||
AstNodeExpr* const past = new AstPast{fl, exprp, nullptr};
|
||||
past->dtypeFrom(exprp);
|
||||
exprp = new AstAnd{fl, past, new AstNot{fl, exprp->cloneTree(false)}};
|
||||
exprp->dtypeSetBit();
|
||||
nodep->replaceWith(exprp);
|
||||
nodep->sentreep(newSenTree(nodep));
|
||||
nodep->sentreep(newSenTree(nodep, sentreep));
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
void visit(AstPast* nodep) override {
|
||||
|
|
@ -381,30 +384,34 @@ private:
|
|||
nodep->sentreep(newSenTree(nodep));
|
||||
}
|
||||
void visit(AstRose* nodep) override {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateChildren(nodep);
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
||||
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
||||
AstSenTree* sentreep = nodep->sentreep();
|
||||
if (sentreep) sentreep->unlinkFrBack();
|
||||
AstNodeExpr* const past = new AstPast{fl, exprp, nullptr};
|
||||
past->dtypeFrom(exprp);
|
||||
exprp = new AstAnd{fl, new AstNot{fl, past}, exprp->cloneTree(false)};
|
||||
exprp->dtypeSetBit();
|
||||
nodep->replaceWith(exprp);
|
||||
nodep->sentreep(newSenTree(nodep));
|
||||
nodep->sentreep(newSenTree(nodep, sentreep));
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
void visit(AstStable* nodep) override {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateChildren(nodep);
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
||||
AstSenTree* sentreep = nodep->sentreep();
|
||||
if (sentreep) sentreep->unlinkFrBack();
|
||||
AstNodeExpr* const past = new AstPast{fl, exprp, nullptr};
|
||||
past->dtypeFrom(exprp);
|
||||
exprp = new AstEq{fl, past, exprp->cloneTree(false)};
|
||||
exprp->dtypeSetBit();
|
||||
nodep->replaceWith(exprp);
|
||||
nodep->sentreep(newSenTree(nodep));
|
||||
nodep->sentreep(newSenTree(nodep, sentreep));
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
|
||||
|
|
|
|||
111
src/V3Ast.cpp
111
src/V3Ast.cpp
|
|
@ -87,8 +87,8 @@ string AstNode::encodeName(const string& namein) {
|
|||
// Encode signal name raw from parser, then not called again on same signal
|
||||
string out;
|
||||
for (string::const_iterator pos = namein.begin(); pos != namein.end(); ++pos) {
|
||||
if ((pos == namein.begin()) ? isalpha(pos[0]) // digits can't lead identifiers
|
||||
: isalnum(pos[0])) {
|
||||
if ((pos == namein.begin()) ? std::isalpha(pos[0]) // digits can't lead identifiers
|
||||
: std::isalnum(pos[0])) {
|
||||
out += pos[0];
|
||||
} else if (pos[0] == '_') {
|
||||
if (pos[1] == '_') {
|
||||
|
|
@ -158,7 +158,6 @@ string AstNode::vcdName(const string& namein) {
|
|||
string AstNode::prettyName(const string& namein) {
|
||||
// This function is somewhat hot, so we short-circuit some compares
|
||||
string pretty;
|
||||
pretty = "";
|
||||
pretty.reserve(namein.length());
|
||||
for (const char* pos = namein.c_str(); *pos;) {
|
||||
if (pos[0] == '-' && pos[1] == '>') { // ->
|
||||
|
|
@ -187,11 +186,14 @@ string AstNode::prettyName(const string& namein) {
|
|||
pos += 7;
|
||||
continue;
|
||||
}
|
||||
if (pos[0] == '_' && pos[1] == '_' && pos[2] == '0' && isxdigit(pos[3])
|
||||
&& isxdigit(pos[4])) {
|
||||
if (pos[0] == '_' && pos[1] == '_' && pos[2] == '0' && std::isxdigit(pos[3])
|
||||
&& std::isxdigit(pos[4])) {
|
||||
char value = 0;
|
||||
value += 16 * (isdigit(pos[3]) ? (pos[3] - '0') : (tolower(pos[3]) - 'a' + 10));
|
||||
value += (isdigit(pos[4]) ? (pos[4] - '0') : (tolower(pos[4]) - 'a' + 10));
|
||||
value += 16
|
||||
* (std::isdigit(pos[3]) ? (pos[3] - '0')
|
||||
: (std::tolower(pos[3]) - 'a' + 10));
|
||||
value
|
||||
+= (std::isdigit(pos[4]) ? (pos[4] - '0') : (std::tolower(pos[4]) - 'a' + 10));
|
||||
pretty += value;
|
||||
pos += 5;
|
||||
continue;
|
||||
|
|
@ -206,6 +208,81 @@ string AstNode::prettyName(const string& namein) {
|
|||
return pretty;
|
||||
}
|
||||
|
||||
string AstNode::vpiName(const string& namein) {
|
||||
// This is slightly different from prettyName, in that when we encounter escaped characters,
|
||||
// we change that identifier to an escaped identifier, wrapping it with '\' and ' '
|
||||
// as specified in LRM 23.6
|
||||
string pretty;
|
||||
pretty.reserve(namein.length());
|
||||
bool inEscapedIdent = false;
|
||||
int lastIdent = 0;
|
||||
|
||||
for (const char* pos = namein.c_str(); *pos;) {
|
||||
char specialChar = 0;
|
||||
if (pos[0] == '-' && pos[1] == '>') { // ->
|
||||
specialChar = '.';
|
||||
pos += 2;
|
||||
} else if (pos[0] == '_' && pos[1] == '_') { // __
|
||||
if (0 == std::strncmp(pos, "__BRA__", 7)) {
|
||||
specialChar = '[';
|
||||
pos += 7;
|
||||
} else if (0 == std::strncmp(pos, "__KET__", 7)) {
|
||||
specialChar = ']';
|
||||
pos += 7;
|
||||
} else if (0 == std::strncmp(pos, "__DOT__", 7)) {
|
||||
specialChar = '.';
|
||||
pos += 7;
|
||||
} else if (0 == std::strncmp(pos, "__PVT__", 7)) {
|
||||
pos += 7;
|
||||
continue;
|
||||
} else if (pos[0] == '_' && pos[1] == '_' && pos[2] == '0' && std::isxdigit(pos[3])
|
||||
&& std::isxdigit(pos[4])) {
|
||||
char value = 0;
|
||||
value += 16
|
||||
* (std::isdigit(pos[3]) ? (pos[3] - '0')
|
||||
: (std::tolower(pos[3]) - 'a' + 10));
|
||||
value
|
||||
+= (std::isdigit(pos[4]) ? (pos[4] - '0') : (std::tolower(pos[4]) - 'a' + 10));
|
||||
|
||||
// __ doesn't always imply escaped ident
|
||||
if (value != '_') inEscapedIdent = true;
|
||||
|
||||
pretty += value;
|
||||
pos += 5;
|
||||
continue;
|
||||
}
|
||||
} else if (pos[0] == '.') {
|
||||
specialChar = '.';
|
||||
++pos;
|
||||
}
|
||||
|
||||
if (specialChar) {
|
||||
if (inEscapedIdent && (specialChar == '[' || specialChar == '.')) {
|
||||
pretty += " ";
|
||||
pretty.insert(lastIdent, "\\");
|
||||
inEscapedIdent = false;
|
||||
}
|
||||
|
||||
pretty += specialChar;
|
||||
|
||||
if (specialChar == ']' || specialChar == '.') {
|
||||
lastIdent = pretty.length();
|
||||
inEscapedIdent = false;
|
||||
}
|
||||
} else {
|
||||
pretty += pos[0];
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
if (inEscapedIdent) {
|
||||
pretty += " ";
|
||||
pretty.insert(lastIdent, "\\");
|
||||
}
|
||||
if (pretty[0] == 'T' && pretty.substr(0, 4) == "TOP.") pretty.replace(0, 4, "");
|
||||
if (pretty[0] == 'T' && pretty.substr(0, 5) == "TOP->") pretty.replace(0, 5, "");
|
||||
return pretty;
|
||||
}
|
||||
|
||||
string AstNode::prettyTypeName() const {
|
||||
if (name() == "") return typeName();
|
||||
return std::string{typeName()} + " '" + prettyName() + "'";
|
||||
|
|
@ -1186,7 +1263,17 @@ void AstNode::dumpTreeDotFile(const string& filename, bool append, bool doDump)
|
|||
}
|
||||
}
|
||||
|
||||
void AstNode::v3errorEndFatal(std::ostringstream& str) const VL_MT_SAFE {
|
||||
bool AstNode::isTreePureRecurse() const {
|
||||
// Should memoize this if call commonly
|
||||
if (!this->isPure()) return false;
|
||||
if (this->op1p() && !this->op1p()->isTreePureRecurse()) return false;
|
||||
if (this->op2p() && !this->op2p()->isTreePureRecurse()) return false;
|
||||
if (this->op3p() && !this->op3p()->isTreePureRecurse()) return false;
|
||||
if (this->op4p() && !this->op4p()->isTreePureRecurse()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void AstNode::v3errorEndFatal(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
v3errorEnd(str);
|
||||
assert(0); // LCOV_EXCL_LINE
|
||||
VL_UNREACHABLE;
|
||||
|
|
@ -1216,9 +1303,9 @@ string AstNode::instanceStr() const {
|
|||
|
||||
return "";
|
||||
}
|
||||
void AstNode::v3errorEnd(std::ostringstream& str) const {
|
||||
void AstNode::v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
if (!m_fileline) {
|
||||
V3Error::v3errorEnd(str, instanceStr());
|
||||
V3Error::s().v3errorEnd(str, instanceStr());
|
||||
} else {
|
||||
std::ostringstream nsstr;
|
||||
nsstr << str.str();
|
||||
|
|
@ -1231,8 +1318,8 @@ void AstNode::v3errorEnd(std::ostringstream& str) const {
|
|||
// Don't look for instance name when warning is disabled.
|
||||
// In case of large number of warnings, this can
|
||||
// take significant amount of time
|
||||
m_fileline->v3errorEnd(nsstr,
|
||||
m_fileline->warnIsOff(V3Error::errorCode()) ? "" : instanceStr());
|
||||
m_fileline->v3errorEnd(
|
||||
nsstr, m_fileline->warnIsOff(V3Error::s().errorCode()) ? "" : instanceStr());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
32
src/V3Ast.h
32
src/V3Ast.h
|
|
@ -371,7 +371,6 @@ public:
|
|||
//
|
||||
DT_PUBLIC, // V3LinkParse moves to AstTypedef::attrPublic
|
||||
//
|
||||
ENUM_BASE, // V3LinkResolve creates for AstPreSel, V3LinkParam removes
|
||||
ENUM_FIRST, // V3Width processes
|
||||
ENUM_LAST, // V3Width processes
|
||||
ENUM_NUM, // V3Width processes
|
||||
|
|
@ -380,8 +379,7 @@ public:
|
|||
ENUM_NAME, // V3Width processes
|
||||
ENUM_VALID, // V3Width processes
|
||||
//
|
||||
MEMBER_BASE, // V3LinkResolve creates for AstPreSel, V3LinkParam removes
|
||||
//
|
||||
TYPEID, // V3Width processes
|
||||
TYPENAME, // V3Width processes
|
||||
//
|
||||
VAR_BASE, // V3LinkResolve creates for AstPreSel, V3LinkParam removes
|
||||
|
|
@ -407,10 +405,9 @@ public:
|
|||
"DIM_BITS", "DIM_DIMENSIONS", "DIM_HIGH", "DIM_INCREMENT", "DIM_LEFT",
|
||||
"DIM_LOW", "DIM_RIGHT", "DIM_SIZE", "DIM_UNPK_DIMENSIONS",
|
||||
"DT_PUBLIC",
|
||||
"ENUM_BASE", "ENUM_FIRST", "ENUM_LAST", "ENUM_NUM",
|
||||
"ENUM_FIRST", "ENUM_LAST", "ENUM_NUM",
|
||||
"ENUM_NEXT", "ENUM_PREV", "ENUM_NAME", "ENUM_VALID",
|
||||
"MEMBER_BASE",
|
||||
"TYPENAME",
|
||||
"TYPEID", "TYPENAME",
|
||||
"VAR_BASE", "VAR_CLOCK_ENABLE", "VAR_FORCEABLE", "VAR_PUBLIC",
|
||||
"VAR_PUBLIC_FLAT", "VAR_PUBLIC_FLAT_RD", "VAR_PUBLIC_FLAT_RW",
|
||||
"VAR_ISOLATE_ASSIGNMENTS", "VAR_SC_BV", "VAR_SFORMAT", "VAR_CLOCKER",
|
||||
|
|
@ -570,7 +567,8 @@ public:
|
|||
}
|
||||
bool isIntNumeric() const { // Enum increment supported
|
||||
return (m_e == BIT || m_e == BYTE || m_e == INT || m_e == INTEGER || m_e == LOGIC
|
||||
|| m_e == LONGINT || m_e == SHORTINT || m_e == UINT32 || m_e == UINT64);
|
||||
|| m_e == LONGINT || m_e == SHORTINT || m_e == UINT32 || m_e == UINT64
|
||||
|| m_e == TIME);
|
||||
}
|
||||
bool isBitLogic() const { // Bit/logic vector types; can form a packed array
|
||||
return (m_e == LOGIC || m_e == BIT);
|
||||
|
|
@ -805,6 +803,10 @@ public:
|
|||
bool isTemp() const {
|
||||
return (m_e == BLOCKTEMP || m_e == MODULETEMP || m_e == STMTTEMP || m_e == XTEMP);
|
||||
}
|
||||
bool isVPIAccessible() const {
|
||||
return (m_e == VAR || m_e == GPARAM || m_e == LPARAM || m_e == PORT || m_e == WIRE
|
||||
|| m_e == TRI0 || m_e == TRI1);
|
||||
}
|
||||
};
|
||||
constexpr bool operator==(const VVarType& lhs, const VVarType& rhs) VL_MT_SAFE {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
|
|
@ -1666,6 +1668,7 @@ public:
|
|||
string shortName() const; // Name with __PVT__ removed for concatenating scopes
|
||||
static string dedotName(const string& namein); // Name with dots removed
|
||||
static string prettyName(const string& namein); // Name for printing out to the user
|
||||
static string vpiName(const string& namein); // Name for vpi access
|
||||
static string prettyNameQ(const string& namein) { // Quoted pretty name (for errors)
|
||||
return std::string{"'"} + prettyName(namein) + "'";
|
||||
}
|
||||
|
|
@ -1852,12 +1855,15 @@ public:
|
|||
static AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
|
||||
|
||||
// METHODS - dump and error
|
||||
void v3errorEnd(std::ostringstream& str) const VL_MT_SAFE;
|
||||
void v3errorEndFatal(std::ostringstream& str) const VL_ATTR_NORETURN VL_MT_SAFE;
|
||||
string warnContextPrimary() const { return fileline()->warnContextPrimary(); }
|
||||
void v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex);
|
||||
void v3errorEndFatal(std::ostringstream& str) const VL_ATTR_NORETURN
|
||||
VL_REQUIRES(V3Error::s().m_mutex);
|
||||
string warnContextPrimary() const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
return fileline()->warnContextPrimary();
|
||||
}
|
||||
string warnContextSecondary() const { return fileline()->warnContextSecondary(); }
|
||||
string warnMore() const { return fileline()->warnMore(); }
|
||||
string warnOther() const { return fileline()->warnOther(); }
|
||||
string warnMore() const VL_REQUIRES(V3Error::s().m_mutex) { return fileline()->warnMore(); }
|
||||
string warnOther() const VL_REQUIRES(V3Error::s().m_mutex) { return fileline()->warnOther(); }
|
||||
|
||||
virtual void dump(std::ostream& str = std::cout) const;
|
||||
static void dumpGdb(const AstNode* nodep); // For GDB only
|
||||
|
|
@ -1920,6 +1926,8 @@ public:
|
|||
void dumpTreeDot(std::ostream& os = std::cout) const;
|
||||
void dumpTreeDotFile(const string& filename, bool append = false, bool doDump = true);
|
||||
|
||||
bool isTreePureRecurse() const;
|
||||
|
||||
// METHODS - queries
|
||||
// Changes control flow, disable some optimizations
|
||||
virtual bool isBrancher() const { return false; }
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@ public:
|
|||
/// are compound when methods calls operate on object, or when
|
||||
/// under another compound-requiring object e.g. class
|
||||
virtual bool isCompound() const = 0;
|
||||
// Integral or packed, allowed inside an unpacked union/struct
|
||||
virtual bool isIntegralOrPacked() const { return !isCompound(); }
|
||||
// (Slow) recurse down to find basic data type
|
||||
virtual AstBasicDType* basicp() const = 0;
|
||||
// recurses over typedefs/const/enum to next non-typeref type
|
||||
|
|
@ -200,6 +202,7 @@ private:
|
|||
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;
|
||||
const int m_uniqueNum;
|
||||
bool m_packed;
|
||||
|
|
@ -255,6 +258,8 @@ public:
|
|||
static int lo() { return 0; }
|
||||
int hi() const { return dtypep()->width() - 1; } // Packed classes look like arrays
|
||||
VNumRange declRange() const { return VNumRange{hi(), lo()}; }
|
||||
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
|
||||
void classOrPackagep(AstNodeModule* classpackagep) { m_classOrPackagep = classpackagep; }
|
||||
};
|
||||
|
||||
// === Concrete node types =====================================================
|
||||
|
|
@ -470,6 +475,7 @@ public:
|
|||
VNumRange declRange() const { return isRanged() ? VNumRange{left(), right()} : VNumRange{}; }
|
||||
void cvtRangeConst(); // Convert to smaller representation
|
||||
bool isCompound() const override { return isString(); }
|
||||
bool isIntegralOrPacked() const override { return keyword().isIntNumeric(); }
|
||||
};
|
||||
class AstBracketArrayDType final : public AstNodeDType {
|
||||
// Associative/Queue/Normal array data type, ie "[dtype_or_expr]"
|
||||
|
|
@ -839,6 +845,7 @@ class AstMemberDType final : public AstNodeDType {
|
|||
// A member of a struct/union
|
||||
// PARENT: AstNodeUOrStructDType
|
||||
// @astgen op1 := childDTypep : Optional[AstNodeDType]
|
||||
// @astgen op3 := valuep : Optional[AstNode]
|
||||
private:
|
||||
AstNodeDType* m_refDTypep = nullptr; // Elements of this type (after widthing)
|
||||
string m_name; // Name of variable
|
||||
|
|
@ -846,10 +853,12 @@ private:
|
|||
int m_lsb = -1; // Within this level's packed struct, the LSB of the first bit of the member
|
||||
// UNSUP: int m_randType; // Randomization type (IEEE)
|
||||
public:
|
||||
AstMemberDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp)
|
||||
AstMemberDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp,
|
||||
AstNode* valuep)
|
||||
: ASTGEN_SUPER_MemberDType(fl)
|
||||
, m_name{name} {
|
||||
childDTypep(dtp); // Only for parser
|
||||
this->valuep(valuep);
|
||||
dtypep(nullptr); // V3Width will resolve
|
||||
refDTypep(nullptr);
|
||||
}
|
||||
|
|
@ -862,6 +871,7 @@ public:
|
|||
widthFromSub(subDTypep());
|
||||
}
|
||||
ASTGEN_MEMBERS_AstMemberDType;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
string name() const override { return m_name; } // * = Var name
|
||||
bool hasDType() const override { return true; }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
|
|
@ -1315,19 +1325,17 @@ public:
|
|||
std::vector<AstUnpackArrayDType*> unpackDimensions();
|
||||
void isCompound(bool flag) { m_isCompound = flag; }
|
||||
bool isCompound() const override VL_MT_SAFE { return m_isCompound; }
|
||||
bool isIntegralOrPacked() const override { return false; }
|
||||
};
|
||||
|
||||
// === AstNodeUOrStructDType ===
|
||||
class AstStructDType final : public AstNodeUOrStructDType {
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
|
||||
public:
|
||||
// VSigning below is mispurposed to indicate if packed or not
|
||||
AstStructDType(FileLine* fl, VSigning numericUnpack)
|
||||
: ASTGEN_SUPER_StructDType(fl, numericUnpack) {}
|
||||
ASTGEN_MEMBERS_AstStructDType;
|
||||
string verilogKwd() const override { return "struct"; }
|
||||
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
|
||||
void classOrPackagep(AstNodeModule* classpackagep) { m_classOrPackagep = classpackagep; }
|
||||
};
|
||||
class AstUnionDType final : public AstNodeUOrStructDType {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -249,6 +249,7 @@ public:
|
|||
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
|
||||
bool pli() const { return m_pli; }
|
||||
void pli(bool flag) { m_pli = flag; }
|
||||
bool isPure() const override;
|
||||
|
||||
string emitVerilog() final override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() final override { V3ERROR_NA_RETURN(""); }
|
||||
|
|
@ -790,6 +791,51 @@ public:
|
|||
int instrCount() const override { return widthInstrs(); }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstConsPackMember final : public AstNodeExpr {
|
||||
// Construct a packed array single emement [member1: value1]
|
||||
// Don't need the member we are constructing, as the dtypep can get us to it
|
||||
// @astgen op2 := rhsp : AstNodeExpr
|
||||
public:
|
||||
explicit AstConsPackMember(FileLine* fl, AstMemberDType* dtypep, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_ConsPackMember(fl) {
|
||||
this->dtypep(dtypep);
|
||||
this->rhsp(rhsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstConsPackMember;
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(dtypep() && !VN_IS(dtypep(), MemberDType));
|
||||
return nullptr;
|
||||
}
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstConsPackUOrStruct final : public AstNodeExpr {
|
||||
// Construct a packed struct and return object, '{member1: value1, member2: value2}
|
||||
// Don't need the class we are constructing, as the dtypep can get us to it
|
||||
// @astgen op1 := membersp : List[AstConsPackMember]
|
||||
public:
|
||||
explicit AstConsPackUOrStruct(FileLine* fl, AstNodeUOrStructDType* dtypep,
|
||||
AstConsPackMember* membersp = nullptr)
|
||||
: ASTGEN_SUPER_ConsPackUOrStruct(fl) {
|
||||
this->dtypep(dtypep);
|
||||
this->addMembersp(membersp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstConsPackUOrStruct;
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(dtypep() && !VN_IS(dtypep(), NodeUOrStructDType));
|
||||
return nullptr;
|
||||
}
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstConsQueue final : public AstNodeExpr {
|
||||
// Construct a queue and return object, '{}. '{lhs}, '{lhs. rhs}
|
||||
// @astgen op1 := lhsp : Optional[AstNode]
|
||||
|
|
@ -1183,9 +1229,10 @@ class AstFell final : public AstNodeExpr {
|
|||
// @astgen op1 := exprp : AstNodeExpr
|
||||
// @astgen op2 := sentreep : Optional[AstSenTree]
|
||||
public:
|
||||
AstFell(FileLine* fl, AstNodeExpr* exprp)
|
||||
AstFell(FileLine* fl, AstNodeExpr* exprp, AstSenTree* sentreep)
|
||||
: ASTGEN_SUPER_Fell(fl) {
|
||||
this->exprp(exprp);
|
||||
this->sentreep(sentreep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstFell;
|
||||
string emitVerilog() override { return "$fell(%l)"; }
|
||||
|
|
@ -1345,6 +1392,7 @@ public:
|
|||
const char* broken() const override;
|
||||
void dump(std::ostream& str) const override;
|
||||
string name() const override { return m_name; }
|
||||
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; }
|
||||
|
|
@ -1536,9 +1584,10 @@ class AstRose final : public AstNodeExpr {
|
|||
// @astgen op1 := exprp : AstNodeExpr
|
||||
// @astgen op2 := sentreep : Optional[AstSenTree]
|
||||
public:
|
||||
AstRose(FileLine* fl, AstNodeExpr* exprp)
|
||||
AstRose(FileLine* fl, AstNodeExpr* exprp, AstSenTree* sentreep)
|
||||
: ASTGEN_SUPER_Rose(fl) {
|
||||
this->exprp(exprp);
|
||||
this->sentreep(sentreep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstRose;
|
||||
string emitVerilog() override { return "$rose(%l)"; }
|
||||
|
|
@ -1754,9 +1803,10 @@ class AstStable final : public AstNodeExpr {
|
|||
// @astgen op1 := exprp : AstNodeExpr
|
||||
// @astgen op2 := sentreep : Optional[AstSenTree]
|
||||
public:
|
||||
AstStable(FileLine* fl, AstNodeExpr* exprp)
|
||||
AstStable(FileLine* fl, AstNodeExpr* exprp, AstSenTree* sentreep)
|
||||
: ASTGEN_SUPER_Stable(fl) {
|
||||
this->exprp(exprp);
|
||||
this->sentreep(sentreep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstStable;
|
||||
string emitVerilog() override { return "$stable(%l)"; }
|
||||
|
|
@ -1801,6 +1851,7 @@ public:
|
|||
}
|
||||
ASTGEN_MEMBERS_AstStructSel;
|
||||
string name() const override { return m_name; }
|
||||
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; }
|
||||
|
|
@ -3304,6 +3355,30 @@ public:
|
|||
int instrCount() const override { return INSTR_COUNT_STR; }
|
||||
bool stringFlavor() const override { return true; }
|
||||
};
|
||||
class AstEqT final : public AstNodeBiCom {
|
||||
// Equal (==) for data types
|
||||
public:
|
||||
AstEqT(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_EqT(fl, lhsp, rhsp) {
|
||||
dtypeSetBit();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstEqT;
|
||||
AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
|
||||
return new AstEqT{fileline(), lhsp, rhsp};
|
||||
}
|
||||
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
|
||||
V3ERROR_NA;
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f== %r)"; }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitSimpleOperator() override { return "=="; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return false; }
|
||||
bool cleanRhs() const override { return false; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
int instrCount() const override { return INSTR_COUNT_STR; }
|
||||
};
|
||||
class AstLogEq final : public AstNodeBiCom {
|
||||
public:
|
||||
AstLogEq(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
|
|
@ -3419,6 +3494,30 @@ public:
|
|||
int instrCount() const override { return INSTR_COUNT_STR; }
|
||||
bool stringFlavor() const override { return true; }
|
||||
};
|
||||
class AstNeqT final : public AstNodeBiCom {
|
||||
// Not-equal (!=) for data types
|
||||
public:
|
||||
AstNeqT(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_NeqT(fl, lhsp, rhsp) {
|
||||
dtypeSetBit();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstNeqT;
|
||||
AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
|
||||
return new AstNeqT{fileline(), lhsp, rhsp};
|
||||
}
|
||||
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
|
||||
V3ERROR_NA;
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f!= %r)"; }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitSimpleOperator() override { return "!="; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return false; }
|
||||
bool cleanRhs() const override { return false; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
int instrCount() const override { return INSTR_COUNT_STR; }
|
||||
};
|
||||
|
||||
// === AstNodeBiComAsv ===
|
||||
class AstAdd final : public AstNodeBiComAsv {
|
||||
|
|
@ -4355,8 +4454,8 @@ public:
|
|||
case ATOOCT: return "atooct";
|
||||
case ATOBIN: return "atobin";
|
||||
case ATOREAL: return "atoreal";
|
||||
default: V3ERROR_NA;
|
||||
}
|
||||
V3ERROR_NA_RETURN("");
|
||||
}
|
||||
string emitVerilog() override { return "%l." + name() + "()"; }
|
||||
string emitC() override {
|
||||
|
|
@ -4366,8 +4465,8 @@ public:
|
|||
case ATOOCT: return "VL_ATOI_N(%li, 8)";
|
||||
case ATOBIN: return "VL_ATOI_N(%li, 2)";
|
||||
case ATOREAL: return "std::atof(%li.c_str())";
|
||||
default: V3ERROR_NA;
|
||||
}
|
||||
V3ERROR_NA_RETURN("");
|
||||
}
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
|
|
@ -4495,7 +4594,7 @@ public:
|
|||
dtypeSetString();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCvtPackString;
|
||||
void numberOperate(V3Number& out, const V3Number& lhs) override { V3ERROR_NA; }
|
||||
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)"; }
|
||||
bool cleanOut() const override { return true; }
|
||||
|
|
@ -4559,6 +4658,7 @@ public:
|
|||
bool cleanLhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
int instrCount() const override { return widthInstrs() * 16; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
AstNode* filep() const { return lhsp(); }
|
||||
};
|
||||
|
|
@ -4575,6 +4675,7 @@ public:
|
|||
bool cleanLhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
int instrCount() const override { return widthInstrs() * 64; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
AstNode* filep() const { return lhsp(); }
|
||||
};
|
||||
|
|
@ -4674,6 +4775,21 @@ public:
|
|||
bool cleanLhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
};
|
||||
class AstNToI final : public AstNodeUniop {
|
||||
// String to any-size integral
|
||||
public:
|
||||
AstNToI(FileLine* fl, AstNodeExpr* lhsp, AstNodeDType* dtypep = nullptr)
|
||||
: ASTGEN_SUPER_NToI(fl, lhsp) {
|
||||
this->dtypep(dtypep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstNToI;
|
||||
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opNToI(lhs); }
|
||||
string emitVerilog() override { return "'(%l)"; }
|
||||
string emitC() override { return "VL_NTOI_%nq(%nw, %P, %li)"; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
};
|
||||
class AstNegate final : public AstNodeUniop {
|
||||
public:
|
||||
AstNegate(FileLine* fl, AstNodeExpr* lhsp)
|
||||
|
|
|
|||
|
|
@ -210,6 +210,7 @@ private:
|
|||
bool m_modTrace : 1; // Tracing this module
|
||||
bool m_inLibrary : 1; // From a library, no error if not used, never top level
|
||||
bool m_dead : 1; // LinkDot believes is dead; will remove in Dead visitors
|
||||
bool m_hasGParam : 1; // Has global parameter (for link)
|
||||
bool m_hierBlock : 1; // Hierarchical Block marked by HIER_BLOCK pragma
|
||||
bool m_internal : 1; // Internally created
|
||||
bool m_recursive : 1; // Recursive module
|
||||
|
|
@ -223,6 +224,7 @@ protected:
|
|||
, m_modTrace{false}
|
||||
, m_inLibrary{false}
|
||||
, m_dead{false}
|
||||
, m_hasGParam{false}
|
||||
, m_hierBlock{false}
|
||||
, m_internal{false}
|
||||
, m_recursive{false}
|
||||
|
|
@ -250,6 +252,8 @@ public:
|
|||
bool modTrace() const { return m_modTrace; }
|
||||
void dead(bool flag) { m_dead = flag; }
|
||||
bool dead() const { return m_dead; }
|
||||
void hasGParam(bool flag) { m_hasGParam = flag; }
|
||||
bool hasGParam() const { return m_hasGParam; }
|
||||
void hierBlock(bool flag) { m_hierBlock = flag; }
|
||||
bool hierBlock() const { return m_hierBlock; }
|
||||
void internal(bool flag) { m_internal = flag; }
|
||||
|
|
@ -799,19 +803,24 @@ public:
|
|||
VTimescale timeunit() const { return m_timeunit; }
|
||||
};
|
||||
class AstClassExtends final : public AstNode {
|
||||
// class extends class name, or class implements class name
|
||||
// Children: List of AstParseRef for packages/classes
|
||||
// during early parse, then moves to dtype
|
||||
// @astgen op1 := childDTypep : Optional[AstNodeDType]
|
||||
// @astgen op2 := classOrPkgsp : Optional[AstNode]
|
||||
const bool m_isImplements = false; // class implements
|
||||
public:
|
||||
AstClassExtends(FileLine* fl, AstNode* classOrPkgsp)
|
||||
: ASTGEN_SUPER_ClassExtends(fl) {
|
||||
AstClassExtends(FileLine* fl, AstNode* classOrPkgsp, bool isImplements)
|
||||
: ASTGEN_SUPER_ClassExtends(fl)
|
||||
, m_isImplements{isImplements} {
|
||||
this->classOrPkgsp(classOrPkgsp); // Only for parser
|
||||
}
|
||||
ASTGEN_MEMBERS_AstClassExtends;
|
||||
void dump(std::ostream& str) const override;
|
||||
bool hasDType() const override { return true; }
|
||||
string verilogKwd() const override { return "extends"; }
|
||||
string verilogKwd() const override { return isImplements() ? "implements" : "extends"; }
|
||||
AstClass* classp() const; // Class being extended (after link)
|
||||
bool isImplements() const { return m_isImplements; }
|
||||
};
|
||||
class AstClocking final : public AstNode {
|
||||
// Parents: MODULE
|
||||
|
|
@ -822,19 +831,23 @@ class AstClocking final : public AstNode {
|
|||
// @astgen op4 := eventp : Optional[AstVar]
|
||||
std::string m_name; // Clocking block name
|
||||
const bool m_isDefault = false; // True if default clocking
|
||||
const bool m_isGlobal = false; // True if global clocking
|
||||
|
||||
public:
|
||||
AstClocking(FileLine* fl, const std::string& name, AstSenItem* sensesp,
|
||||
AstClockingItem* itemsp, bool isDefault)
|
||||
AstClockingItem* itemsp, bool isDefault, bool isGlobal)
|
||||
: ASTGEN_SUPER_Clocking(fl)
|
||||
, m_isDefault{isDefault} {
|
||||
, m_isDefault{isDefault}
|
||||
, m_isGlobal{isGlobal} {
|
||||
m_name = name;
|
||||
this->sensesp(sensesp);
|
||||
addItemsp(itemsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstClocking;
|
||||
void dump(std::ostream& str) const override;
|
||||
std::string name() const override { return m_name; }
|
||||
bool isDefault() const { return m_isDefault; }
|
||||
bool isGlobal() const { return m_isGlobal; }
|
||||
};
|
||||
class AstClockingItem final : public AstNode {
|
||||
// Parents: CLOCKING
|
||||
|
|
@ -1224,7 +1237,8 @@ private:
|
|||
AstVar* m_modVarp = nullptr; // Input/output this pin connects to on submodule.
|
||||
AstParamTypeDType* m_modPTypep = nullptr; // Param type this pin connects to on submodule.
|
||||
bool m_param = false; // Pin connects to parameter
|
||||
bool m_svImplicit = false; // Pin is SystemVerilog .name'ed
|
||||
bool m_svDotName = false; // Pin is SystemVerilog .name'ed
|
||||
bool m_svImplicit = false; // Pin is SystemVerilog .name'ed, allow implicit
|
||||
public:
|
||||
AstPin(FileLine* fl, int pinNum, const string& name, AstNode* exprp)
|
||||
: ASTGEN_SUPER_Pin(fl)
|
||||
|
|
@ -1248,6 +1262,8 @@ public:
|
|||
void modPTypep(AstParamTypeDType* nodep) { m_modPTypep = nodep; }
|
||||
bool param() const { return m_param; }
|
||||
void param(bool flag) { m_param = flag; }
|
||||
bool svDotName() const { return m_svDotName; }
|
||||
void svDotName(bool flag) { m_svDotName = flag; }
|
||||
bool svImplicit() const { return m_svImplicit; }
|
||||
void svImplicit(bool flag) { m_svImplicit = flag; }
|
||||
};
|
||||
|
|
@ -2090,14 +2106,15 @@ public:
|
|||
|
||||
// === AstNodeModule ===
|
||||
class AstClass final : public AstNodeModule {
|
||||
// @astgen op4 := extendsp : Optional[AstClassExtends]
|
||||
// @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
|
||||
bool m_virtual = false; // Virtual class
|
||||
bool m_extended = false; // Is extension or extended by other classes
|
||||
bool m_interfaceClass = false; // Interface class
|
||||
bool m_virtual = false; // Virtual class
|
||||
void insertCache(AstNode* nodep);
|
||||
|
||||
public:
|
||||
|
|
@ -2125,6 +2142,8 @@ public:
|
|||
}
|
||||
bool isExtended() const { return m_extended; }
|
||||
void isExtended(bool flag) { m_extended = flag; }
|
||||
bool isInterfaceClass() const { return m_interfaceClass; }
|
||||
void isInterfaceClass(bool flag) { m_interfaceClass = flag; }
|
||||
bool isVirtual() const { return m_virtual; }
|
||||
void isVirtual(bool flag) { m_virtual = flag; }
|
||||
// Return true if this class is an extension of base class (SLOW)
|
||||
|
|
|
|||
|
|
@ -60,6 +60,12 @@ void AstNodeFTaskRef::cloneRelink() {
|
|||
}
|
||||
}
|
||||
|
||||
bool AstNodeFTaskRef::isPure() const {
|
||||
// TODO: For non-DPI functions we could traverse the AST of function's body to determine
|
||||
// pureness.
|
||||
return this->taskp() && this->taskp()->dpiImport() && this->taskp()->pure();
|
||||
}
|
||||
|
||||
bool AstNodeFTaskRef::isGateOptimizable() const { return m_taskp && m_taskp->isGateOptimizable(); }
|
||||
|
||||
const char* AstNodeVarRef::broken() const {
|
||||
|
|
@ -675,7 +681,7 @@ AstVar* AstVar::scVarRecurse(AstNode* nodep) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool AstNodeDType::isFourstate() const { return basicp()->isFourstate(); }
|
||||
bool AstNodeDType::isFourstate() const { return basicp() && basicp()->isFourstate(); }
|
||||
|
||||
class AstNodeDType::CTypeRecursed final {
|
||||
public:
|
||||
|
|
@ -737,8 +743,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
|
|||
info.m_type = "VlUnpacked<" + sub.m_type;
|
||||
info.m_type += ", " + cvtToStr(adtypep->declRange().elements());
|
||||
info.m_type += ">";
|
||||
} else if (VN_IS(dtypep, StructDType) && !VN_AS(dtypep, StructDType)->packed()) {
|
||||
const auto* const sdtypep = VN_AS(dtypep, StructDType);
|
||||
} else if (VN_IS(dtypep, NodeUOrStructDType) && !VN_AS(dtypep, NodeUOrStructDType)->packed()) {
|
||||
const auto* const sdtypep = VN_AS(dtypep, NodeUOrStructDType);
|
||||
info.m_type = EmitCBaseVisitor::prefixNameProtect(sdtypep);
|
||||
} else if (const AstBasicDType* const bdtypep = dtypep->basicp()) {
|
||||
// We don't print msb()/lsb() as multidim packed would require recursion,
|
||||
|
|
@ -869,7 +875,7 @@ AstNode* AstArraySel::baseFromp(AstNode* nodep, bool overMembers) {
|
|||
continue;
|
||||
}
|
||||
// AstNodeSelPre stashes the associated variable under an ATTROF
|
||||
// of VAttrType::VAR_BASE/MEMBER_BASE so it isn't constified
|
||||
// of VAttrType::VAR_BASE so it isn't constified
|
||||
else if (VN_IS(nodep, AttrOf)) {
|
||||
nodep = VN_AS(nodep, AttrOf)->fromp();
|
||||
continue;
|
||||
|
|
@ -1458,6 +1464,7 @@ bool AstClass::isClassExtendedFrom(const AstClass* refClassp, const AstClass* ba
|
|||
void AstClass::dump(std::ostream& str) const {
|
||||
this->AstNodeModule::dump(str);
|
||||
if (isExtended()) str << " [EXT]";
|
||||
if (isInterfaceClass()) str << " [IFCCLS]";
|
||||
if (isVirtual()) str << " [VIRT]";
|
||||
}
|
||||
const char* AstClass::broken() const {
|
||||
|
|
@ -1471,6 +1478,10 @@ void AstClass::cloneRelink() {
|
|||
m_classOrPackagep = m_classOrPackagep->clonep();
|
||||
}
|
||||
}
|
||||
void AstClassExtends::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (isImplements()) str << " [IMPLEMENTS]";
|
||||
}
|
||||
AstClass* AstClassExtends::classp() const {
|
||||
const AstClassRefDType* refp = VN_CAST(dtypep(), ClassRefDType);
|
||||
if (VL_UNLIKELY(!refp)) { // LinkDot uses this for 'super.'
|
||||
|
|
@ -1509,6 +1520,11 @@ void AstNodeCoverOrAssert::dump(std::ostream& str) const {
|
|||
this->AstNodeStmt::dump(str);
|
||||
if (immediate()) str << " [IMMEDIATE]";
|
||||
}
|
||||
void AstClocking::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (isDefault()) str << " [DEFAULT]";
|
||||
if (isGlobal()) str << " [GLOBAL]";
|
||||
}
|
||||
void AstDisplay::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
// str << " " << displayType().ascii();
|
||||
|
|
@ -1636,6 +1652,10 @@ void AstLogOr::dump(std::ostream& str) const {
|
|||
this->AstNodeExpr::dump(str);
|
||||
if (sideEffect()) str << " [SIDE]";
|
||||
}
|
||||
void AstMemberDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "member";
|
||||
}
|
||||
void AstMemberSel::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
str << " -> ";
|
||||
|
|
@ -1704,6 +1724,7 @@ void AstPin::dump(std::ostream& str) const {
|
|||
} else {
|
||||
str << " ->UNLINKED";
|
||||
}
|
||||
if (svDotName()) str << " [.n]";
|
||||
if (svImplicit()) str << " [.SV]";
|
||||
}
|
||||
const char* AstPin::broken() const {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
// Look for BEGINs
|
||||
// BEGIN(VAR...) -> VAR ... {renamed}
|
||||
// FOR -> WHILEs
|
||||
// Move static function variables and their AstInitialStatic blocks before a function
|
||||
//
|
||||
// There are two scopes; named BEGINs change %m and variable scopes.
|
||||
// Unnamed BEGINs change only variable, not $display("%m") scope.
|
||||
|
|
@ -64,13 +65,14 @@ private:
|
|||
// STATE
|
||||
BeginState* const m_statep; // Current global state
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
const AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
||||
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
||||
AstNode* m_liftedp = nullptr; // Local nodes we are lifting into m_ftaskp
|
||||
string m_displayScope; // Name of %m in $display/AstScopeName
|
||||
string m_namedScope; // Name of begin blocks above us
|
||||
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
|
||||
|
||||
|
|
@ -121,6 +123,25 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
void renameAndStaticsRecurse(AstNode* const nodep) {
|
||||
// Rename references and move InitialStatic items
|
||||
if (AstVarRef* const varrefp = VN_CAST(nodep, VarRef)) {
|
||||
const auto it = m_staticFuncVars.find(varrefp->varp());
|
||||
if (it != m_staticFuncVars.end()) varrefp->name((*it)->name());
|
||||
}
|
||||
|
||||
if (nodep->op1p()) renameAndStaticsRecurse(nodep->op1p());
|
||||
if (nodep->op2p()) renameAndStaticsRecurse(nodep->op2p());
|
||||
if (nodep->op3p()) renameAndStaticsRecurse(nodep->op3p());
|
||||
if (nodep->op4p()) renameAndStaticsRecurse(nodep->op4p());
|
||||
if (nodep->nextp()) renameAndStaticsRecurse(nodep->nextp());
|
||||
|
||||
if (VN_IS(nodep, InitialStatic)) {
|
||||
nodep->unlinkFrBack();
|
||||
m_ftaskp->addHereThisAsNext(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstFork* nodep) override {
|
||||
// Keep this begin to group its statements together
|
||||
|
|
@ -165,6 +186,7 @@ private:
|
|||
m_ftaskp = nodep;
|
||||
m_liftedp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
renameAndStaticsRecurse(nodep);
|
||||
if (m_liftedp) {
|
||||
// Place lifted nodes at beginning of stmtsp, so Var nodes appear before referenced
|
||||
if (AstNode* const stmtsp = nodep->stmtsp()) {
|
||||
|
|
@ -174,6 +196,7 @@ private:
|
|||
nodep->addStmtsp(m_liftedp);
|
||||
m_liftedp = nullptr;
|
||||
}
|
||||
m_staticFuncVars.clear();
|
||||
m_ftaskp = nullptr;
|
||||
}
|
||||
}
|
||||
|
|
@ -210,7 +233,15 @@ private:
|
|||
}
|
||||
}
|
||||
void visit(AstVar* nodep) override {
|
||||
if (m_unnamedScope != "") {
|
||||
// If static variable, move it outside a function.
|
||||
if (nodep->lifetime().isStatic() && m_ftaskp) {
|
||||
const std::string newName = m_ftaskp->name() + "__Vstatic__" + nodep->name();
|
||||
nodep->name(newName);
|
||||
nodep->unlinkFrBack();
|
||||
m_ftaskp->addHereThisAsNext(nodep);
|
||||
m_staticFuncVars.insert(nodep);
|
||||
nodep->funcLocal(false);
|
||||
} else if (m_unnamedScope != "") {
|
||||
// Rename it
|
||||
nodep->name(dot(m_unnamedScope, nodep->name()));
|
||||
m_statep->userMarkChanged(nodep);
|
||||
|
|
|
|||
|
|
@ -155,6 +155,9 @@ private:
|
|||
V3CCtorsBuilder configure_coverage{nodep, "_configure_coverage", VCtorType::COVERAGE};
|
||||
for (AstNode* np = nodep->stmtsp(); np; np = np->nextp()) {
|
||||
if (AstCoverDecl* const coverp = VN_CAST(np, CoverDecl)) {
|
||||
// ... else we don't have a static VlSym to be able to coverage insert
|
||||
UASSERT_OBJ(!VN_IS(nodep, Class), coverp,
|
||||
"CoverDecl should be in class's package, not class itself");
|
||||
np = coverp->backp();
|
||||
configure_coverage.add(coverp->unlinkFrBack());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ class CUseVisitor final : public VNVisitor {
|
|||
if (nodep->virtRefDType2p()) iterate(nodep->virtRefDType2p());
|
||||
|
||||
// Add a CUse for every struct that requires a declaration
|
||||
AstStructDType* const stypep = VN_CAST(nodep->skipRefp(), StructDType);
|
||||
AstNodeUOrStructDType* const stypep = VN_CAST(nodep->skipRefp(), NodeUOrStructDType);
|
||||
if (stypep && stypep->classOrPackagep()) {
|
||||
addNewUse(nodep, VUseType::INT_INCLUDE, stypep->classOrPackagep()->name());
|
||||
iterateChildren(stypep);
|
||||
|
|
|
|||
765
src/V3Cdc.cpp
765
src/V3Cdc.cpp
|
|
@ -1,765 +0,0 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Clock Domain Crossing Lint
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//*************************************************************************
|
||||
// V3Cdc's Transformations:
|
||||
//
|
||||
// Create V3Graph-ish graph
|
||||
// Find all negedge reset flops
|
||||
// Trace back to previous flop
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Cdc.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Const.h"
|
||||
#include "V3EmitV.h"
|
||||
#include "V3File.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Graph.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
constexpr int CDC_WEIGHT_ASYNC = 0x1000; // Weight for edges that feed async logic
|
||||
|
||||
//######################################################################
|
||||
|
||||
class CdcBaseVisitor VL_NOT_FINAL : public VNVisitor {
|
||||
public:
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Graph support classes
|
||||
|
||||
class CdcEitherVertex VL_NOT_FINAL : public V3GraphVertex {
|
||||
AstScope* const m_scopep;
|
||||
AstNode* const m_nodep;
|
||||
AstSenTree* m_srcDomainp = nullptr;
|
||||
AstSenTree* m_dstDomainp = nullptr;
|
||||
bool m_srcDomainSet : 1;
|
||||
bool m_dstDomainSet : 1;
|
||||
bool m_asyncPath : 1;
|
||||
|
||||
public:
|
||||
CdcEitherVertex(V3Graph* graphp, AstScope* scopep, AstNode* nodep)
|
||||
: V3GraphVertex{graphp}
|
||||
, m_scopep{scopep}
|
||||
, m_nodep{nodep}
|
||||
, m_srcDomainSet{false}
|
||||
, m_dstDomainSet{false}
|
||||
, m_asyncPath{false} {}
|
||||
~CdcEitherVertex() override = default;
|
||||
// ACCESSORS
|
||||
FileLine* fileline() const override { return nodep()->fileline(); }
|
||||
AstScope* scopep() const { return m_scopep; }
|
||||
AstNode* nodep() const { return m_nodep; }
|
||||
AstSenTree* srcDomainp() const { return m_srcDomainp; }
|
||||
void srcDomainp(AstSenTree* nodep) { m_srcDomainp = nodep; }
|
||||
bool srcDomainSet() const { return m_srcDomainSet; }
|
||||
void srcDomainSet(bool flag) { m_srcDomainSet = flag; }
|
||||
AstSenTree* dstDomainp() const { return m_dstDomainp; }
|
||||
void dstDomainp(AstSenTree* nodep) { m_dstDomainp = nodep; }
|
||||
bool dstDomainSet() const { return m_dstDomainSet; }
|
||||
void dstDomainSet(bool flag) { m_dstDomainSet = flag; }
|
||||
bool asyncPath() const { return m_asyncPath; }
|
||||
void asyncPath(bool flag) { m_asyncPath = flag; }
|
||||
};
|
||||
|
||||
class CdcVarVertex final : public CdcEitherVertex {
|
||||
AstVarScope* const m_varScp;
|
||||
int m_cntAsyncRst = 0;
|
||||
bool m_fromFlop = false;
|
||||
|
||||
public:
|
||||
CdcVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
|
||||
: CdcEitherVertex{graphp, scopep, varScp}
|
||||
, m_varScp{varScp} {}
|
||||
~CdcVarVertex() override = default;
|
||||
// ACCESSORS
|
||||
AstVarScope* varScp() const { return m_varScp; }
|
||||
string name() const override { return (cvtToHex(m_varScp) + " " + varScp()->name()); }
|
||||
string dotColor() const override {
|
||||
return fromFlop() ? "green" : cntAsyncRst() ? "red" : "blue";
|
||||
}
|
||||
int cntAsyncRst() const { return m_cntAsyncRst; }
|
||||
void cntAsyncRst(int flag) { m_cntAsyncRst = flag; }
|
||||
bool fromFlop() const { return m_fromFlop; }
|
||||
void fromFlop(bool flag) { m_fromFlop = flag; }
|
||||
};
|
||||
|
||||
class CdcLogicVertex final : public CdcEitherVertex {
|
||||
bool m_hazard : 1;
|
||||
bool m_isFlop : 1;
|
||||
|
||||
public:
|
||||
CdcLogicVertex(V3Graph* graphp, AstScope* scopep, AstNode* nodep, AstSenTree* sensenodep)
|
||||
: CdcEitherVertex{graphp, scopep, nodep}
|
||||
, m_hazard{false}
|
||||
, m_isFlop{false} {
|
||||
srcDomainp(sensenodep);
|
||||
dstDomainp(sensenodep);
|
||||
}
|
||||
~CdcLogicVertex() override = default;
|
||||
// ACCESSORS
|
||||
string name() const override { return (cvtToHex(nodep()) + "@" + scopep()->prettyName()); }
|
||||
string dotColor() const override { return hazard() ? "black" : "yellow"; }
|
||||
bool hazard() const { return m_hazard; }
|
||||
void setHazard(AstNode* nodep) {
|
||||
m_hazard = true;
|
||||
nodep->user3(true);
|
||||
}
|
||||
void clearHazard() { m_hazard = false; }
|
||||
bool isFlop() const { return m_isFlop; }
|
||||
void isFlop(bool flag) { m_isFlop = flag; }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
class CdcDumpVisitor final : public CdcBaseVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// {statement}Node::user3 -> bool, indicating not hazard
|
||||
std::ofstream* const m_ofp = nullptr; // Output file
|
||||
string m_prefix;
|
||||
|
||||
void visit(AstNode* nodep) override {
|
||||
*m_ofp << m_prefix;
|
||||
if (nodep->user3()) {
|
||||
*m_ofp << " %%";
|
||||
} else {
|
||||
*m_ofp << " ";
|
||||
}
|
||||
*m_ofp << nodep->prettyTypeName() << "\n";
|
||||
const string lastPrefix = m_prefix;
|
||||
m_prefix = lastPrefix + "1:";
|
||||
iterateAndNextNull(nodep->op1p());
|
||||
m_prefix = lastPrefix + "2:";
|
||||
iterateAndNextNull(nodep->op2p());
|
||||
m_prefix = lastPrefix + "3:";
|
||||
iterateAndNextNull(nodep->op3p());
|
||||
m_prefix = lastPrefix + "4:";
|
||||
iterateAndNextNull(nodep->op4p());
|
||||
m_prefix = lastPrefix;
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
CdcDumpVisitor(AstNode* nodep, std::ofstream* ofp, const string& prefix)
|
||||
: m_ofp{ofp}
|
||||
, m_prefix{prefix} {
|
||||
iterate(nodep);
|
||||
}
|
||||
~CdcDumpVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
class CdcWidthVisitor final : public CdcBaseVisitor {
|
||||
private:
|
||||
size_t m_maxFilenameLen = 0;
|
||||
int m_maxLineno = 0;
|
||||
|
||||
void visit(AstNode* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
// Keeping line+filename lengths separate is much faster than calling ascii().length()
|
||||
if (nodep->fileline()->lineno() >= m_maxLineno) {
|
||||
m_maxLineno = nodep->fileline()->lineno() + 1;
|
||||
}
|
||||
if (nodep->fileline()->filename().length() >= m_maxFilenameLen) {
|
||||
m_maxFilenameLen = nodep->fileline()->filename().length() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CdcWidthVisitor(AstNode* nodep) { iterate(nodep); }
|
||||
~CdcWidthVisitor() override = default;
|
||||
// ACCESSORS
|
||||
int maxWidth() const {
|
||||
size_t width = 1;
|
||||
width += m_maxFilenameLen;
|
||||
width += 1; // The :
|
||||
width += cvtToStr(m_maxLineno).length();
|
||||
width += 1; // Final :
|
||||
return static_cast<int>(width);
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Cdc class functions
|
||||
|
||||
class CdcVisitor final : public CdcBaseVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstVarScope::user1p -> CdcVarVertex* for usage var, 0=not set yet
|
||||
// AstVarScope::user2 -> bool Used in sensitivity list
|
||||
// {statement}Node::user1p -> CdcLogicVertex* for this statement
|
||||
// AstNode::user3 -> bool True indicates to print %% (via V3EmitV)
|
||||
const VNUser1InUse m_inuser1;
|
||||
const VNUser2InUse m_inuser2;
|
||||
const VNUser3InUse m_inuser3;
|
||||
|
||||
// STATE
|
||||
V3Graph m_graph; // Scoreboard of var usages/dependencies
|
||||
CdcLogicVertex* m_logicVertexp = nullptr; // Current statement being tracked, nullptr=ignored
|
||||
AstScope* m_scopep = nullptr; // Current scope being processed
|
||||
const AstNodeModule* m_modp = nullptr; // Current module
|
||||
AstSenTree* m_domainp = nullptr; // Current sentree
|
||||
bool m_inDly = false; // In delayed assign
|
||||
int m_inSenItem = 0; // Number of senitems
|
||||
string m_ofFilename; // Output filename
|
||||
std::ofstream* m_ofp; // Output file
|
||||
uint32_t m_userGeneration = 0; // Generation count to avoid slow userClearVertices
|
||||
int m_filelineWidth = 0; // Characters in longest fileline
|
||||
|
||||
// METHODS
|
||||
void iterateNewStmt(AstNode* nodep) {
|
||||
if (m_scopep) {
|
||||
VL_RESTORER(m_logicVertexp);
|
||||
UINFO(4, " STMT " << nodep << endl);
|
||||
m_logicVertexp = new CdcLogicVertex{&m_graph, m_scopep, nodep, m_domainp};
|
||||
if (m_domainp && m_domainp->hasClocked()) { // To/from a flop
|
||||
m_logicVertexp->isFlop(true);
|
||||
m_logicVertexp->srcDomainp(m_domainp);
|
||||
m_logicVertexp->srcDomainSet(true);
|
||||
m_logicVertexp->dstDomainp(m_domainp);
|
||||
m_logicVertexp->dstDomainSet(true);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
|
||||
if (false && debug() >= 9) {
|
||||
UINFO(9, "Trace Logic:\n");
|
||||
nodep->dumpTree("- log1: ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CdcVarVertex* makeVarVertex(AstVarScope* varscp) {
|
||||
CdcVarVertex* vertexp = reinterpret_cast<CdcVarVertex*>(varscp->user1p());
|
||||
if (!vertexp) {
|
||||
UINFO(6, "New vertex " << varscp << endl);
|
||||
vertexp = new CdcVarVertex{&m_graph, m_scopep, varscp};
|
||||
varscp->user1p(vertexp);
|
||||
if (varscp->varp()->isUsedClock()) {}
|
||||
if (varscp->varp()->isPrimaryIO()) {
|
||||
// Create IO vertex - note it's relative to the pointed to var, not where we are
|
||||
// now This allows reporting to easily print the input statement
|
||||
CdcLogicVertex* const ioVertexp
|
||||
= new CdcLogicVertex{&m_graph, varscp->scopep(), varscp->varp(), nullptr};
|
||||
if (varscp->varp()->isWritable()) {
|
||||
new V3GraphEdge{&m_graph, vertexp, ioVertexp, 1};
|
||||
} else {
|
||||
new V3GraphEdge{&m_graph, ioVertexp, vertexp, 1};
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_inSenItem) {
|
||||
varscp->user2(true); // It's like a clock...
|
||||
// TODO: In the future mark it here and do normal clock tree glitch checks also
|
||||
} else if (varscp->user2()) { // It was detected in a sensitivity list earlier
|
||||
// And now it's used as logic. So must be a reset.
|
||||
vertexp->cntAsyncRst(vertexp->cntAsyncRst() + 1);
|
||||
}
|
||||
return vertexp;
|
||||
}
|
||||
|
||||
void warnAndFile(AstNode* nodep, V3ErrorCode code, const string& msg) {
|
||||
static bool told_file = false;
|
||||
nodep->v3warnCode(code, msg);
|
||||
if (!told_file) {
|
||||
told_file = true;
|
||||
std::cerr << V3Error::msgPrefix() << " See details in " << m_ofFilename << endl;
|
||||
}
|
||||
*m_ofp << "%Warning-" << code.ascii() << ": " << nodep->fileline() << " " << msg << '\n';
|
||||
}
|
||||
|
||||
void setNodeHazard(AstNode* nodep) {
|
||||
// Need to not clear if warnings are off (rather than when report it)
|
||||
// as bypassing this warning may turn up another path that isn't warning off'ed.
|
||||
// We can't modifyWarnOff here, as one instantiation might not be
|
||||
// an issue until we find a hitting flop.
|
||||
// Furthermore, a module like a "Or" module would only get flagged
|
||||
// once, even though the signals feeding it are radically different.
|
||||
if (!m_domainp || m_domainp->hasCombo()) {
|
||||
// Source flop logic in a posedge block is OK for reset (not async though)
|
||||
if (m_logicVertexp && !nodep->fileline()->warnIsOff(V3ErrorCode::CDCRSTLOGIC)) {
|
||||
UINFO(8, "Set hazard " << nodep << endl);
|
||||
m_logicVertexp->setHazard(nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static string spaces(int level) {
|
||||
string out;
|
||||
while (level--) out += " ";
|
||||
return out;
|
||||
} // LCOV_EXCL_LINE
|
||||
static string pad(unsigned column, const string& in) {
|
||||
string out = in;
|
||||
while (out.length() < column) out += ' ';
|
||||
return out;
|
||||
}
|
||||
|
||||
void analyze() {
|
||||
UINFO(3, __FUNCTION__ << ": " << endl);
|
||||
if (dumpGraph() > 6) m_graph.dumpDotFilePrefixed("cdc_pre");
|
||||
// This will MAX across edge weights
|
||||
m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue);
|
||||
//
|
||||
if (dumpGraph() >= 3) m_graph.dumpDotFilePrefixed("cdc_simp");
|
||||
//
|
||||
analyzeReset();
|
||||
}
|
||||
|
||||
int filelineWidth() {
|
||||
if (!m_filelineWidth) {
|
||||
const CdcWidthVisitor visitor{v3Global.rootp()};
|
||||
m_filelineWidth = visitor.maxWidth();
|
||||
}
|
||||
return m_filelineWidth;
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// RESET REPORT
|
||||
|
||||
void analyzeReset() {
|
||||
// Find all async reset wires, and trace backwards
|
||||
// userClearVertices is very slow, so we use a generation count instead
|
||||
m_graph.userClearVertices(); // user1: uint32_t - was analyzed generation
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (CdcVarVertex* const vvertexp = dynamic_cast<CdcVarVertex*>(itp)) {
|
||||
if (vvertexp->cntAsyncRst()) {
|
||||
m_userGeneration++; // Effectively a userClearVertices()
|
||||
UINFO(8, " Trace One async: " << vvertexp << endl);
|
||||
// Twice, as we need to detect, then propagate
|
||||
CdcEitherVertex* const markp = traceAsyncRecurse(vvertexp, false);
|
||||
if (markp) { // Mark is non-nullptr if something bad on this path
|
||||
UINFO(9, " Trace One bad! " << vvertexp << endl);
|
||||
m_userGeneration++; // Effectively a userClearVertices()
|
||||
traceAsyncRecurse(vvertexp, true);
|
||||
m_userGeneration++; // Effectively a userClearVertices()
|
||||
dumpAsync(vvertexp, markp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CdcEitherVertex* traceAsyncRecurse(CdcEitherVertex* vertexp, bool mark) {
|
||||
// First pass: Return vertex of any hazardous stuff attached, or nullptr if OK
|
||||
// If first pass returns true, second pass calls asyncPath() on appropriate nodes
|
||||
if (vertexp->user() >= m_userGeneration) return nullptr; // Processed - prevent loop
|
||||
vertexp->user(m_userGeneration);
|
||||
|
||||
CdcEitherVertex* mark_outp = nullptr;
|
||||
UINFO(9, " Trace: " << vertexp << endl);
|
||||
|
||||
// Clear out in prep for marking next path
|
||||
if (!mark) vertexp->asyncPath(false);
|
||||
|
||||
if (CdcLogicVertex* const vvertexp = dynamic_cast<CdcLogicVertex*>(vertexp)) {
|
||||
// Any logic considered bad, at the moment, anyhow
|
||||
if (vvertexp->hazard() && !mark_outp) mark_outp = vvertexp;
|
||||
// And keep tracing back so the user can understand what's up
|
||||
} else if (CdcVarVertex* const vvertexp = dynamic_cast<CdcVarVertex*>(vertexp)) {
|
||||
if (mark) vvertexp->asyncPath(true);
|
||||
// If primary I/O, it's ok here back
|
||||
if (vvertexp->varScp()->varp()->isPrimaryInish()) {
|
||||
// Show the source "input" statement if it exists
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
CdcEitherVertex* const eFromVertexp
|
||||
= static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
eFromVertexp->asyncPath(true);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
// Also ok if from flop, but partially trace the flop so more obvious to users
|
||||
if (vvertexp->fromFlop()) {
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
CdcEitherVertex* const eFromVertexp
|
||||
= static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
eFromVertexp->asyncPath(true);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
CdcEitherVertex* const eFromVertexp = static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
CdcEitherVertex* const submarkp = traceAsyncRecurse(eFromVertexp, mark);
|
||||
if (submarkp && !mark_outp) mark_outp = submarkp;
|
||||
}
|
||||
|
||||
if (mark) vertexp->asyncPath(true);
|
||||
return mark_outp;
|
||||
}
|
||||
|
||||
void dumpAsync(CdcVarVertex* vertexp, CdcEitherVertex* markp) {
|
||||
const AstNode* const nodep = vertexp->varScp();
|
||||
*m_ofp << "\n";
|
||||
*m_ofp << "\n";
|
||||
CdcEitherVertex* targetp = vertexp; // One example destination flop (of possibly many)
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
CdcEitherVertex* const eToVertexp = static_cast<CdcEitherVertex*>(edgep->top());
|
||||
if (!eToVertexp) targetp = eToVertexp;
|
||||
if (const CdcLogicVertex* const vvertexp = dynamic_cast<CdcLogicVertex*>(eToVertexp)) {
|
||||
if (vvertexp->isFlop() // IE the target flop that is upsetting us
|
||||
&& edgep->weight() >= CDC_WEIGHT_ASYNC) { // And var feeds an async reset line
|
||||
targetp = eToVertexp;
|
||||
// UINFO(9," targetasync "<<targetp->name()<<" "<<" from
|
||||
// "<<vertexp->name()<<endl);
|
||||
break;
|
||||
}
|
||||
} // else it might be random logic that's not relevant
|
||||
}
|
||||
// UINFO(9," finalflop "<<targetp->name()<<" "<<targetp->nodep()->fileline()<<endl);
|
||||
warnAndFile(markp->nodep(), V3ErrorCode::CDCRSTLOGIC,
|
||||
"Logic in path that feeds async reset, via signal: " + nodep->prettyNameQ());
|
||||
dumpAsyncRecurse(targetp, "", " ", 0);
|
||||
}
|
||||
bool dumpAsyncRecurse(CdcEitherVertex* vertexp, const string& prefix, const string& sep,
|
||||
int level) {
|
||||
// level=0 is special, indicates to dump destination flop
|
||||
// Return true if printed anything
|
||||
// If mark, also mark the output even if nothing hazardous below
|
||||
if (vertexp->user() >= m_userGeneration) return false; // Processed - prevent loop
|
||||
vertexp->user(m_userGeneration);
|
||||
if (!vertexp->asyncPath() && level != 0) return false; // Not part of path
|
||||
|
||||
// Other logic in the path
|
||||
const string cont = prefix + sep;
|
||||
string nextsep = " ";
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
CdcEitherVertex* const eFromVertexp = static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
if (dumpAsyncRecurse(eFromVertexp, cont, nextsep, level + 1)) nextsep = " | ";
|
||||
}
|
||||
|
||||
// Dump single variable/logic block
|
||||
// See also OrderGraph::loopsVertexCb(V3GraphVertex* vertexp)
|
||||
AstNode* const nodep = vertexp->nodep();
|
||||
const string front
|
||||
= pad(filelineWidth(), nodep->fileline()->ascii() + ":") + " " + prefix + " +- ";
|
||||
if (VN_IS(nodep, VarScope)) {
|
||||
*m_ofp << front << "Variable: " << nodep->prettyName() << '\n';
|
||||
} else {
|
||||
V3EmitV::verilogPrefixedTree(nodep, *m_ofp, prefix + " +- ", filelineWidth(),
|
||||
vertexp->srcDomainp(), true);
|
||||
if (debug()) { CdcDumpVisitor{nodep, m_ofp, front + "DBG: "}; }
|
||||
}
|
||||
|
||||
nextsep = " | ";
|
||||
if (level)
|
||||
*m_ofp << V3OutFile::indentSpaces(filelineWidth()) << " " << prefix << nextsep << "\n";
|
||||
|
||||
if (CdcLogicVertex* const vvertexp = dynamic_cast<CdcLogicVertex*>(vertexp)) {
|
||||
// Now that we've printed a path with this hazard, don't bother to print any more
|
||||
// Otherwise, we'd get a path for almost every destination flop
|
||||
vvertexp->clearHazard();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// EDGE REPORTS
|
||||
|
||||
void edgeReport() {
|
||||
// Make report of all signal names and what clock edges they have
|
||||
//
|
||||
// Due to flattening, many interesting direct-connect signals are
|
||||
// lost, so we can't make a report showing I/Os for a low level
|
||||
// module. Disabling flattening though makes us consider each
|
||||
// signal in it's own unique clock domain.
|
||||
|
||||
UINFO(3, __FUNCTION__ << ": " << endl);
|
||||
|
||||
// Trace all sources and sinks
|
||||
for (const bool& traceDests : {false, true}) {
|
||||
UINFO(9, " Trace Direction " << (traceDests ? "dst" : "src") << endl);
|
||||
m_graph.userClearVertices(); // user1: bool - was analyzed
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (CdcVarVertex* const vvertexp = dynamic_cast<CdcVarVertex*>(itp)) {
|
||||
UINFO(9, " Trace One edge: " << vvertexp << endl);
|
||||
edgeDomainRecurse(vvertexp, traceDests, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const string filename
|
||||
= v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__cdc_edges.txt";
|
||||
const std::unique_ptr<std::ofstream> ofp{V3File::new_ofstream(filename)};
|
||||
if (ofp->fail()) v3fatal("Can't write " << filename);
|
||||
*ofp << "Edge Report for " << v3Global.opt.prefix() << '\n';
|
||||
|
||||
std::deque<string> report; // Sort output by name
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (const CdcVarVertex* const vvertexp = dynamic_cast<CdcVarVertex*>(itp)) {
|
||||
const AstVar* const varp = vvertexp->varScp()->varp();
|
||||
{
|
||||
string what = "wire";
|
||||
if (varp->isPrimaryIO()) what = varp->direction().prettyName();
|
||||
|
||||
std::ostringstream os;
|
||||
os.setf(std::ios::left);
|
||||
// Module name - doesn't work due to flattening having lost the original
|
||||
// so we assume the modulename matches the filebasename
|
||||
const string fname = vvertexp->varScp()->fileline()->filebasename() + ":";
|
||||
os << " " << std::setw(20) << fname;
|
||||
os << " " << std::setw(8) << what;
|
||||
os << " " << std::setw(40) << vvertexp->varScp()->prettyName();
|
||||
os << " SRC=";
|
||||
if (vvertexp->srcDomainp()) {
|
||||
V3EmitV::verilogForTree(vvertexp->srcDomainp(), os);
|
||||
}
|
||||
os << " DST=";
|
||||
if (vvertexp->dstDomainp()) {
|
||||
V3EmitV::verilogForTree(vvertexp->dstDomainp(), os);
|
||||
}
|
||||
os << std::setw(0);
|
||||
os << '\n';
|
||||
report.push_back(os.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
stable_sort(report.begin(), report.end());
|
||||
for (const auto& line : report) *ofp << line;
|
||||
}
|
||||
|
||||
void edgeDomainRecurse(CdcEitherVertex* vertexp, bool traceDests, int level) {
|
||||
// Scan back to inputs/outputs, flops, and compute clock domain information
|
||||
UINFO(8, spaces(level) << " Tracein " << vertexp << endl);
|
||||
if (vertexp->user() >= m_userGeneration) return; // Mid-Processed - prevent loop
|
||||
vertexp->user(m_userGeneration);
|
||||
|
||||
// Variables from flops already are domained
|
||||
if (traceDests ? vertexp->dstDomainSet() : vertexp->srcDomainSet()) {
|
||||
return;
|
||||
} // Fully computed
|
||||
|
||||
std::set<AstSenTree*> senouts; // List of all sensitivities for new signal
|
||||
if (dynamic_cast<CdcLogicVertex*>(vertexp)) {
|
||||
} else if (const CdcVarVertex* const vvertexp = dynamic_cast<CdcVarVertex*>(vertexp)) {
|
||||
// If primary I/O, give it domain of the input
|
||||
const AstVar* const varp = vvertexp->varScp()->varp();
|
||||
if (varp->isPrimaryIO() && varp->isNonOutput() && !traceDests) {
|
||||
senouts.insert(new AstSenTree{
|
||||
varp->fileline(), new AstSenItem{varp->fileline(), AstSenItem::Combo{}}});
|
||||
}
|
||||
}
|
||||
|
||||
// Now combine domains of sources/dests
|
||||
if (traceDests) {
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
CdcEitherVertex* const eToVertexp = static_cast<CdcEitherVertex*>(edgep->top());
|
||||
edgeDomainRecurse(eToVertexp, traceDests, level + 1);
|
||||
if (eToVertexp->dstDomainp()) senouts.insert(eToVertexp->dstDomainp());
|
||||
}
|
||||
} else {
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
CdcEitherVertex* const eFromVertexp
|
||||
= static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
edgeDomainRecurse(eFromVertexp, traceDests, level + 1);
|
||||
if (eFromVertexp->srcDomainp()) senouts.insert(eFromVertexp->srcDomainp());
|
||||
}
|
||||
}
|
||||
|
||||
// Convert list of senses into one sense node
|
||||
AstSenTree* senoutp = nullptr;
|
||||
bool senedited = false;
|
||||
for (const auto& itr : senouts) {
|
||||
if (!senoutp) {
|
||||
senoutp = itr;
|
||||
} else {
|
||||
if (!senedited) {
|
||||
senedited = true;
|
||||
senoutp = senoutp->cloneTree(true);
|
||||
}
|
||||
senoutp->addSensesp(itr->sensesp()->cloneTree(true));
|
||||
}
|
||||
}
|
||||
// If multiple domains need to do complicated optimizations
|
||||
if (senedited) senoutp = VN_AS(V3Const::constifyExpensiveEdit(senoutp), SenTree);
|
||||
if (traceDests) {
|
||||
vertexp->dstDomainSet(true); // Note it's set - domainp may be null, so can't use that
|
||||
vertexp->dstDomainp(senoutp);
|
||||
if (debug() >= 9) {
|
||||
UINFO(9, spaces(level) + " Tracedst " << vertexp);
|
||||
if (senoutp) {
|
||||
V3EmitV::verilogForTree(senoutp, cout);
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
vertexp->srcDomainSet(true); // Note it's set - domainp may be null, so can't use that
|
||||
vertexp->srcDomainp(senoutp);
|
||||
if (debug() >= 9) {
|
||||
UINFO(9, spaces(level) + " Tracesrc " << vertexp);
|
||||
if (senoutp) {
|
||||
V3EmitV::verilogForTree(senoutp, cout);
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
{
|
||||
m_modp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstScope* nodep) override {
|
||||
UINFO(4, " SCOPE " << nodep << endl);
|
||||
m_scopep = nodep;
|
||||
m_logicVertexp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
m_scopep = nullptr;
|
||||
}
|
||||
void visit(AstActive* nodep) override {
|
||||
// Create required blocks and add to module
|
||||
UINFO(4, " BLOCK " << nodep << endl);
|
||||
AstNode::user2ClearTree();
|
||||
m_domainp = nodep->sensesp();
|
||||
// Ignore static initializers, initial and final blocks
|
||||
if (!m_domainp || m_domainp->hasCombo() || m_domainp->hasClocked()) {
|
||||
iterateNewStmt(nodep);
|
||||
}
|
||||
m_domainp = nullptr;
|
||||
AstNode::user2ClearTree();
|
||||
}
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
if (m_scopep) {
|
||||
UASSERT_OBJ(m_logicVertexp, nodep, "Var ref not under a logic block");
|
||||
AstVarScope* const varscp = nodep->varScopep();
|
||||
UASSERT_OBJ(varscp, nodep, "Var didn't get varscoped in V3Scope.cpp");
|
||||
CdcVarVertex* const varvertexp = makeVarVertex(varscp);
|
||||
UINFO(5, " VARREF to " << varscp << endl);
|
||||
// We use weight of one for normal edges,
|
||||
// Weight of CDC_WEIGHT_ASYNC to indicate feeds async (for reporting)
|
||||
// When simplify we'll take the MAX weight
|
||||
if (nodep->access().isWriteOrRW()) {
|
||||
new V3GraphEdge{&m_graph, m_logicVertexp, varvertexp, 1};
|
||||
if (m_inDly) {
|
||||
varvertexp->fromFlop(true);
|
||||
varvertexp->srcDomainp(m_domainp);
|
||||
varvertexp->srcDomainSet(true);
|
||||
}
|
||||
} else {
|
||||
if (varvertexp->cntAsyncRst()) {
|
||||
// UINFO(9," edgeasync "<<varvertexp->name()<<" to "<<m_logicVertexp<<endl);
|
||||
new V3GraphEdge{&m_graph, varvertexp, m_logicVertexp, CDC_WEIGHT_ASYNC};
|
||||
} else {
|
||||
// UINFO(9," edgena "<<varvertexp->name()<<" to "<<m_logicVertexp<<endl);
|
||||
new V3GraphEdge{&m_graph, varvertexp, m_logicVertexp, 1};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void visit(AstAssignDly* nodep) override {
|
||||
VL_RESTORER(m_inDly);
|
||||
m_inDly = true;
|
||||
iterateChildren(nodep);
|
||||
m_inDly = false;
|
||||
}
|
||||
void visit(AstSenItem* nodep) override {
|
||||
m_inSenItem = true;
|
||||
iterateChildren(nodep);
|
||||
m_inSenItem = false;
|
||||
}
|
||||
void visit(AstAlways* nodep) override { iterateNewStmt(nodep); }
|
||||
void visit(AstAlwaysPublic* nodep) override {
|
||||
// CDC doesn't care about public variables
|
||||
}
|
||||
void visit(AstCFunc* nodep) override { iterateNewStmt(nodep); }
|
||||
void visit(AstAssignAlias* nodep) override { iterateNewStmt(nodep); }
|
||||
void visit(AstAssignW* nodep) override { iterateNewStmt(nodep); }
|
||||
|
||||
// Expressions that shouldn't cause us to clear hazard
|
||||
void visit(AstConst*) override {}
|
||||
void visit(AstReplicate* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstConcat* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNot* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstSel* nodep) override {
|
||||
if (!VN_IS(nodep->lsbp(), Const)) setNodeHazard(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNodeSel* nodep) override {
|
||||
if (!VN_IS(nodep->bitp(), Const)) setNodeHazard(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
// Ignores
|
||||
void visit(AstInitial*) override {}
|
||||
void visit(AstInitialAutomatic*) override {}
|
||||
void visit(AstInitialStatic*) override {}
|
||||
void visit(AstTraceDecl*) override {}
|
||||
void visit(AstCoverToggle*) override {}
|
||||
void visit(AstNodeDType*) override {}
|
||||
|
||||
//--------------------
|
||||
// Default
|
||||
void visit(AstNodeExpr* nodep) override {
|
||||
setNodeHazard(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CdcVisitor(AstNode* nodep) {
|
||||
// Make report of all signal names and what clock edges they have
|
||||
const string filename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__cdc.txt";
|
||||
m_ofp = V3File::new_ofstream(filename);
|
||||
if (m_ofp->fail()) v3fatal("Can't write " << filename);
|
||||
m_ofFilename = filename;
|
||||
*m_ofp << "CDC Report for " << v3Global.opt.prefix() << '\n';
|
||||
*m_ofp
|
||||
<< "Each dump below traces logic from inputs/source flops to destination flop(s).\n";
|
||||
*m_ofp << "First source logic is listed, then a variable that logic generates,\n";
|
||||
*m_ofp << "repeating recursively forwards to the destination flop(s).\n";
|
||||
*m_ofp << "%% Indicates the operator considered potentially hazardous.\n";
|
||||
|
||||
iterate(nodep);
|
||||
analyze();
|
||||
if (debug() >= 1) edgeReport(); // Not useful to users at the moment
|
||||
if (false) {
|
||||
*m_ofp << "\nDBG-test-dumper\n";
|
||||
V3EmitV::verilogPrefixedTree(nodep, *m_ofp, "DBG ", 40, nullptr, true);
|
||||
*m_ofp << '\n';
|
||||
}
|
||||
}
|
||||
~CdcVisitor() override {
|
||||
if (m_ofp) VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Cdc class functions
|
||||
|
||||
void V3Cdc::cdcAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CdcVisitor{nodep}; }
|
||||
}
|
||||
32
src/V3Cdc.h
32
src/V3Cdc.h
|
|
@ -1,32 +0,0 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Break always into sensitivity block domains
|
||||
//
|
||||
// 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_V3CDC_H_
|
||||
#define VERILATOR_V3CDC_H_
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
class AstNetlist;
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3Cdc final {
|
||||
public:
|
||||
static void cdcAll(AstNetlist* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
|
@ -156,6 +156,11 @@ private:
|
|||
// m_toScopeMoves.push_back(std::make_pair(nodep, m_classScopep));
|
||||
//}
|
||||
}
|
||||
void visit(AstCoverDecl* nodep) override {
|
||||
// Need to declare coverage in package, where we have access to symbol table
|
||||
iterateChildren(nodep);
|
||||
if (m_classPackagep) m_classPackagep->addStmtsp(nodep->unlinkFrBack());
|
||||
}
|
||||
void visit(AstInitial* nodep) override {
|
||||
// But not AstInitialAutomatic, which remains under the class
|
||||
iterateChildren(nodep);
|
||||
|
|
@ -171,24 +176,25 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
void setStructModulep(AstStructDType* const dtypep) {
|
||||
void setStructModulep(AstNodeUOrStructDType* const dtypep) {
|
||||
// Give it a pointer to its package and a final name
|
||||
dtypep->classOrPackagep(m_modp);
|
||||
dtypep->name(dtypep->name() + "__struct" + cvtToStr(dtypep->uniqueNum()));
|
||||
dtypep->name(dtypep->name() + (VN_IS(dtypep, UnionDType) ? "__union" : "__struct")
|
||||
+ cvtToStr(dtypep->uniqueNum()));
|
||||
|
||||
for (const AstMemberDType* itemp = dtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
AstStructDType* const subp = VN_CAST(itemp->skipRefp(), StructDType);
|
||||
AstNodeUOrStructDType* const subp = VN_CAST(itemp->skipRefp(), NodeUOrStructDType);
|
||||
// Recurse only into anonymous unpacked structs inside this definition,
|
||||
// other unpacked structs will be reached from another typedefs
|
||||
if (subp && !subp->packed() && subp->name().empty()) { setStructModulep(subp); }
|
||||
if (subp && !subp->packed() && subp->name().empty()) setStructModulep(subp);
|
||||
}
|
||||
}
|
||||
void visit(AstTypedef* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateChildren(nodep);
|
||||
|
||||
AstStructDType* const dtypep = VN_CAST(nodep->dtypep(), StructDType);
|
||||
AstNodeUOrStructDType* const dtypep = VN_CAST(nodep->dtypep(), NodeUOrStructDType);
|
||||
if (dtypep && !dtypep->packed()) {
|
||||
dtypep->name(nodep->name());
|
||||
setStructModulep(dtypep);
|
||||
|
|
|
|||
|
|
@ -87,7 +87,8 @@ private:
|
|||
}
|
||||
void computeCppWidth(AstNode* nodep) {
|
||||
if (!nodep->user2() && nodep->hasDType()) {
|
||||
if (VN_IS(nodep, Var)
|
||||
if (VN_IS(nodep, Var) //
|
||||
|| VN_IS(nodep, ConsPackMember) //
|
||||
|| VN_IS(nodep, NodeDType) // Don't want to change variable widths!
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), AssocArrayDType) // Or arrays
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), WildcardArrayDType)
|
||||
|
|
@ -97,9 +98,9 @@ private:
|
|||
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), VoidDType)) {
|
||||
} else {
|
||||
const AstStructDType* const dtypep
|
||||
= VN_CAST(nodep->dtypep()->skipRefp(), StructDType);
|
||||
if (!dtypep || dtypep->packed()) { setCppWidth(nodep); }
|
||||
const AstNodeUOrStructDType* const dtypep
|
||||
= VN_CAST(nodep->dtypep()->skipRefp(), NodeUOrStructDType);
|
||||
if (!dtypep || dtypep->packed()) setCppWidth(nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ static void makeVlToString(AstIface* nodep) {
|
|||
funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp});
|
||||
nodep->addStmtsp(funcp);
|
||||
}
|
||||
static void makeVlToString(AstStructDType* nodep) {
|
||||
static void makeVlToString(AstNodeUOrStructDType* nodep) {
|
||||
AstNodeModule* const modp = nodep->classOrPackagep();
|
||||
AstCFunc* const funcp
|
||||
= new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"};
|
||||
|
|
@ -79,7 +79,7 @@ static void makeVlToString(AstStructDType* nodep) {
|
|||
} else {
|
||||
stmt += ", ";
|
||||
}
|
||||
stmt += itemp->nameProtect() + ":\" + ";
|
||||
stmt += VIdProtect::protect(itemp->prettyName()) + ":\" + ";
|
||||
if (VN_IS(itemp->dtypep()->skipRefp(), BasicDType) && itemp->isWide()) {
|
||||
stmt += "VL_TO_STRING_W";
|
||||
} else {
|
||||
|
|
@ -168,7 +168,7 @@ void V3Common::commonAll() {
|
|||
}
|
||||
for (AstNode* nodep = v3Global.rootp()->typeTablep()->typesp(); nodep;
|
||||
nodep = nodep->nextp()) {
|
||||
if (AstStructDType* const dtypep = VN_CAST(nodep, StructDType)) {
|
||||
if (AstNodeUOrStructDType* const dtypep = VN_CAST(nodep, NodeUOrStructDType)) {
|
||||
if (!dtypep->packed()) makeVlToString(dtypep);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -332,8 +332,7 @@ public:
|
|||
}
|
||||
bool waive(V3ErrorCode code, const string& match) {
|
||||
for (const auto& itr : m_waivers) {
|
||||
if (((itr.first == code) || (itr.first == V3ErrorCode::I_LINT)
|
||||
|| (code.unusedError() && itr.first == V3ErrorCode::I_UNUSED))
|
||||
if ((code.isUnder(itr.first) || (itr.first == V3ErrorCode::I_LINT))
|
||||
&& VString::wildmatch(match, itr.second)) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -900,6 +900,7 @@ private:
|
|||
bool m_doV = false; // Verilog, not C++ conversion
|
||||
bool m_doGenerate = false; // Postpone width checking inside generate
|
||||
bool m_hasJumpDelay = false; // JumpGo or Delay under this while
|
||||
bool m_underRecFunc = false; // Under a recursive function
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
const AstArraySel* m_selp = nullptr; // Current select
|
||||
const AstNode* m_scopep = nullptr; // Current scope
|
||||
|
|
@ -1384,15 +1385,6 @@ private:
|
|||
// but for now can disable en masse until V3Purify takes effect.
|
||||
return m_doShort || VN_IS(nodep, VarRef) || VN_IS(nodep, Const);
|
||||
}
|
||||
bool isTreePureRecurse(AstNode* nodep) {
|
||||
// Should memoize this if call commonly
|
||||
if (!nodep->isPure()) return false;
|
||||
if (nodep->op1p() && !isTreePureRecurse(nodep->op1p())) return false;
|
||||
if (nodep->op2p() && !isTreePureRecurse(nodep->op2p())) return false;
|
||||
if (nodep->op3p() && !isTreePureRecurse(nodep->op3p())) return false;
|
||||
if (nodep->op4p() && !isTreePureRecurse(nodep->op4p())) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Extraction checks
|
||||
bool warnSelect(AstSel* nodep) {
|
||||
|
|
@ -1466,6 +1458,7 @@ private:
|
|||
if (!thensp->lhsp()->sameGateTree(elsesp->lhsp())) return false;
|
||||
if (!thensp->rhsp()->gateTree()) return false;
|
||||
if (!elsesp->rhsp()->gateTree()) return false;
|
||||
if (m_underRecFunc) return false; // This optimization may lead to infinite recursion
|
||||
return true;
|
||||
}
|
||||
bool operandIfIf(const AstNodeIf* nodep) {
|
||||
|
|
@ -2954,7 +2947,7 @@ private:
|
|||
}
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
} else if (!afterComment(nodep->thensp()) && !afterComment(nodep->elsesp())) {
|
||||
if (!isTreePureRecurse(nodep->condp())) {
|
||||
if (!nodep->condp()->isTreePureRecurse()) {
|
||||
// Condition has side effect - leave - perhaps in
|
||||
// future simplify to remove all but side effect terms
|
||||
} else {
|
||||
|
|
@ -3094,12 +3087,12 @@ private:
|
|||
if (!inPct && ch == '%') {
|
||||
inPct = true;
|
||||
fmt = ch;
|
||||
} else if (inPct && (isdigit(ch) || ch == '.' || ch == '-')) {
|
||||
} else if (inPct && (std::isdigit(ch) || ch == '.' || ch == '-')) {
|
||||
fmt += ch;
|
||||
} else if (inPct) {
|
||||
inPct = false;
|
||||
fmt += ch;
|
||||
switch (tolower(ch)) {
|
||||
switch (std::tolower(ch)) {
|
||||
case '%': break; // %% - just output a %
|
||||
case 'm': break; // %m - auto insert "name"
|
||||
case 'l': break; // %l - auto insert "library"
|
||||
|
|
@ -3137,6 +3130,11 @@ private:
|
|||
VL_DO_DANGLING(replaceConstString(nodep, nodep->name()), nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstNodeFTask* nodep) override {
|
||||
VL_RESTORER(m_underRecFunc);
|
||||
if (nodep->recursive()) m_underRecFunc = true;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstFuncRef* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
|
|
|
|||
|
|
@ -124,7 +124,10 @@ private:
|
|||
UINFO(9, "new " << declp << endl);
|
||||
|
||||
AstCoverInc* const incp = new AstCoverInc{fl, declp};
|
||||
if (!trace_var_name.empty() && v3Global.opt.traceCoverage()) {
|
||||
if (!trace_var_name.empty()
|
||||
&& v3Global.opt.traceCoverage()
|
||||
// No module handle to trace inside classes
|
||||
&& !VN_IS(m_modp, Class)) {
|
||||
FileLine* const fl_nowarn = new FileLine{incp->fileline()};
|
||||
fl_nowarn->modifyWarnOff(V3ErrorCode::UNUSEDSIGNAL, true);
|
||||
AstVar* const varp = new AstVar{fl_nowarn, VVarType::MODULETEMP, trace_var_name,
|
||||
|
|
|
|||
|
|
@ -324,7 +324,7 @@ private:
|
|||
}
|
||||
}
|
||||
bool shouldDeleteTypedef(AstTypedef* typedefp) {
|
||||
if (auto* structp = VN_CAST(typedefp->subDTypep(), StructDType)) {
|
||||
if (auto* const structp = VN_CAST(typedefp->subDTypep(), NodeUOrStructDType)) {
|
||||
if (structp->user1() && !structp->packed()) return false;
|
||||
}
|
||||
return m_elimCells && !typedefp->attrPublic();
|
||||
|
|
|
|||
15
src/V3Dfg.h
15
src/V3Dfg.h
|
|
@ -503,14 +503,19 @@ public:
|
|||
inline bool inlined() const;
|
||||
|
||||
// Methods that allow DfgVertex to participate in error reporting/messaging
|
||||
void v3errorEnd(std::ostringstream& str) const { m_filelinep->v3errorEnd(str); }
|
||||
void v3errorEndFatal(std::ostringstream& str) const VL_ATTR_NORETURN {
|
||||
void v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
m_filelinep->v3errorEnd(str);
|
||||
}
|
||||
void v3errorEndFatal(std::ostringstream& str) const VL_ATTR_NORETURN
|
||||
VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
m_filelinep->v3errorEndFatal(str);
|
||||
}
|
||||
string warnContextPrimary() const { return fileline()->warnContextPrimary(); }
|
||||
string warnContextPrimary() const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
return fileline()->warnContextPrimary();
|
||||
}
|
||||
string warnContextSecondary() const { return fileline()->warnContextSecondary(); }
|
||||
string warnMore() const { return fileline()->warnMore(); }
|
||||
string warnOther() const { return fileline()->warnOther(); }
|
||||
string warnMore() const VL_REQUIRES(V3Error::s().m_mutex) { return fileline()->warnMore(); }
|
||||
string warnOther() const VL_REQUIRES(V3Error::s().m_mutex) { return fileline()->warnOther(); }
|
||||
|
||||
private:
|
||||
// For internal use only.
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ void EmitCFunc::emitOpName(AstNode* nodep, const string& format, AstNode* lhsp,
|
|||
puts("(");
|
||||
} else {
|
||||
// Normal text
|
||||
if (isalnum(pos[0])) needComma = true;
|
||||
if (std::isalnum(pos[0])) needComma = true;
|
||||
COMMA;
|
||||
string s;
|
||||
s += pos[0];
|
||||
|
|
@ -262,7 +262,7 @@ void EmitCFunc::displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const
|
|||
}
|
||||
if (argp->widthMin() > 8 && fmtLetter == 'c') {
|
||||
// Technically legal, but surely not what the user intended.
|
||||
argp->v3warn(WIDTH, dispp->verilogKwd() << "of %c format of > 8 bit value");
|
||||
argp->v3warn(WIDTHTRUNC, dispp->verilogKwd() << "of %c format of > 8 bit value");
|
||||
}
|
||||
}
|
||||
// string pfmt = "%"+displayFormat(argp, vfmt, fmtLetter)+fmtLetter;
|
||||
|
|
@ -329,7 +329,7 @@ void EmitCFunc::displayNode(AstNode* nodep, AstScopeName* scopenamep, const stri
|
|||
m_emitDispState.pushFormat(*pos);
|
||||
} else { // Format character
|
||||
inPct = false;
|
||||
switch (tolower(pos[0])) {
|
||||
switch (std::tolower(pos[0])) {
|
||||
case '0': // FALLTHRU
|
||||
case '1': // FALLTHRU
|
||||
case '2': // FALLTHRU
|
||||
|
|
@ -541,6 +541,12 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string
|
|||
if (int(nodep->num().toDouble()) == nodep->num().toDouble()
|
||||
&& nodep->num().toDouble() < 1000 && nodep->num().toDouble() > -1000) {
|
||||
ofp()->printf("%3.1f", nodep->num().toDouble()); // Force decimal point
|
||||
} else if (std::isinf(nodep->num().toDouble())) {
|
||||
if (std::signbit(nodep->num().toDouble())) puts("-");
|
||||
ofp()->puts("std::numeric_limits<double>::infinity()");
|
||||
} else if (std::isnan(nodep->num().toDouble())) {
|
||||
if (std::signbit(nodep->num().toDouble())) puts("-");
|
||||
ofp()->puts("std::numeric_limits<double>::quiet_NaN()");
|
||||
} else {
|
||||
// Not %g as will not always put in decimal point, so not obvious to compiler
|
||||
// is a real number
|
||||
|
|
@ -673,8 +679,8 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP
|
|||
depth + 1, suffix + "[" + ivar + "]");
|
||||
const string post = "}\n";
|
||||
return below.empty() ? "" : pre + below + post;
|
||||
} else if (VN_IS(dtypep, StructDType) && !VN_AS(dtypep, StructDType)->packed()) {
|
||||
const auto* const sdtypep = VN_AS(dtypep, StructDType);
|
||||
} else if (VN_IS(dtypep, NodeUOrStructDType) && !VN_AS(dtypep, NodeUOrStructDType)->packed()) {
|
||||
const auto* const sdtypep = VN_AS(dtypep, NodeUOrStructDType);
|
||||
string literal;
|
||||
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
|
|
|
|||
|
|
@ -624,9 +624,14 @@ public:
|
|||
puts(")");
|
||||
}
|
||||
void visit(AstFError* nodep) override {
|
||||
puts("VL_FERROR_IN(");
|
||||
puts("VL_FERROR_I");
|
||||
puts(nodep->strp()->isString() ? "N(" : "W(");
|
||||
iterateAndNextNull(nodep->filep());
|
||||
putbs(", ");
|
||||
if (nodep->strp()->isWide()) {
|
||||
puts(cvtToStr(nodep->strp()->widthWords()));
|
||||
putbs(", ");
|
||||
}
|
||||
iterateAndNextNull(nodep->strp());
|
||||
puts(")");
|
||||
}
|
||||
|
|
@ -1262,6 +1267,22 @@ public:
|
|||
puts(")");
|
||||
}
|
||||
}
|
||||
void visit(AstConsPackUOrStruct* nodep) override {
|
||||
putbs(nodep->dtypep()->cType("", false, false));
|
||||
puts("{");
|
||||
for (AstNode* memberp = nodep->membersp(); memberp; memberp = memberp->nextp()) {
|
||||
iterate(memberp);
|
||||
if (memberp->nextp()) { puts(", "); }
|
||||
}
|
||||
puts("}");
|
||||
}
|
||||
void visit(AstConsPackMember* nodep) override {
|
||||
auto* const vdtypep = VN_AS(nodep->dtypep(), MemberDType);
|
||||
putbs(".");
|
||||
puts(vdtypep->name());
|
||||
puts(" = ");
|
||||
iterate(nodep->rhsp());
|
||||
}
|
||||
void visit(AstConsQueue* nodep) override {
|
||||
putbs(nodep->dtypep()->cType("", false, false));
|
||||
if (!nodep->lhsp()) {
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ class EmitCHeader final : public EmitCConstInit {
|
|||
puts("void " + protect("__Vconfigure") + "(bool first);\n");
|
||||
}
|
||||
|
||||
if (v3Global.opt.coverage()) {
|
||||
if (v3Global.opt.coverage() && !VN_IS(modp, Class)) {
|
||||
decorateFirst(first, section);
|
||||
puts("void __vlCoverInsert(");
|
||||
puts(v3Global.opt.threads() ? "std::atomic<uint32_t>" : "uint32_t");
|
||||
|
|
@ -208,13 +208,13 @@ class EmitCHeader final : public EmitCConstInit {
|
|||
}
|
||||
}
|
||||
}
|
||||
void emitStructDecl(const AstNodeModule* modp, AstStructDType* sdtypep,
|
||||
std::set<AstStructDType*>& emitted) {
|
||||
void emitStructDecl(const AstNodeModule* modp, AstNodeUOrStructDType* sdtypep,
|
||||
std::set<AstNodeUOrStructDType*>& emitted) {
|
||||
if (emitted.count(sdtypep) > 0) return;
|
||||
emitted.insert(sdtypep);
|
||||
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
AstStructDType* subp = VN_CAST(itemp->skipRefp(), StructDType);
|
||||
AstNodeUOrStructDType* subp = VN_CAST(itemp->skipRefp(), NodeUOrStructDType);
|
||||
if (subp && !subp->packed()) {
|
||||
// Recurse if it belongs to the current module
|
||||
if (subp->classOrPackagep() == modp) {
|
||||
|
|
@ -223,7 +223,8 @@ class EmitCHeader final : public EmitCConstInit {
|
|||
}
|
||||
}
|
||||
}
|
||||
puts("struct " + EmitCBaseVisitor::prefixNameProtect(sdtypep) + " {\n");
|
||||
puts(sdtypep->verilogKwd()); // "struct"/"union"
|
||||
puts(" " + EmitCBaseVisitor::prefixNameProtect(sdtypep) + " {\n");
|
||||
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
puts(itemp->dtypep()->cType(itemp->nameProtect(), false, false));
|
||||
|
|
@ -234,12 +235,12 @@ class EmitCHeader final : public EmitCConstInit {
|
|||
void emitStructs(const AstNodeModule* modp) {
|
||||
bool first = true;
|
||||
// Track structs that've been emitted already
|
||||
std::set<AstStructDType*> emitted;
|
||||
std::set<AstNodeUOrStructDType*> emitted;
|
||||
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
const AstTypedef* const tdefp = VN_CAST(nodep, Typedef);
|
||||
if (!tdefp) continue;
|
||||
AstStructDType* const sdtypep
|
||||
= VN_CAST(tdefp->dtypep()->skipRefToEnump(), StructDType);
|
||||
AstNodeUOrStructDType* const sdtypep
|
||||
= VN_CAST(tdefp->dtypep()->skipRefToEnump(), NodeUOrStructDType);
|
||||
if (!sdtypep) continue;
|
||||
if (sdtypep->packed()) continue;
|
||||
decorateFirst(first, "\n// UNPACKED STRUCT TYPES\n");
|
||||
|
|
|
|||
|
|
@ -50,7 +50,8 @@ class EmitCGatherDependencies final : VNVisitor {
|
|||
if (const AstClassRefDType* const dtypep = VN_CAST(nodep, ClassRefDType)) {
|
||||
m_dependencies.insert(
|
||||
EmitCBaseVisitor::prefixNameProtect(dtypep->classp()->classOrPackagep()));
|
||||
} else if (const AstStructDType* const dtypep = VN_CAST(nodep, StructDType)) {
|
||||
} else if (const AstNodeUOrStructDType* const dtypep
|
||||
= VN_CAST(nodep, NodeUOrStructDType)) {
|
||||
if (!dtypep->packed()) {
|
||||
m_dependencies.insert(
|
||||
EmitCBaseVisitor::prefixNameProtect(dtypep->classOrPackagep()));
|
||||
|
|
@ -454,9 +455,9 @@ class EmitCImp final : EmitCFunc {
|
|||
emitCtorImp(modp);
|
||||
emitConfigureImp(modp);
|
||||
emitDestructorImp(modp);
|
||||
emitCoverageImp();
|
||||
}
|
||||
emitSavableImp(modp);
|
||||
emitCoverageImp();
|
||||
} else {
|
||||
// From `systemc_implementation
|
||||
emitTextSection(modp, VNType::atScImp);
|
||||
|
|
|
|||
|
|
@ -156,6 +156,22 @@ class EmitCSyms final : EmitCBaseVisitor {
|
|||
string out = scpname;
|
||||
// Remove hierarchy
|
||||
string::size_type pos = out.rfind('.');
|
||||
|
||||
// If there's more than one ident and an escape, find the true last ident
|
||||
if (pos != string::npos && scpname.find('\\') != string::npos) {
|
||||
size_t i = 0;
|
||||
// always makes progress
|
||||
while (i < scpname.length()) {
|
||||
if (scpname[i] == '\\') {
|
||||
while (i < scpname.length() && scpname[i] != ' ') ++i;
|
||||
++i; // Proc ' ', it should always be there. Then grab '.' on next cycle
|
||||
} else {
|
||||
while (i < scpname.length() && scpname[i] != '.') ++i;
|
||||
if (i < scpname.length()) { pos = i++; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pos != std::string::npos) out.erase(0, pos + 1);
|
||||
// Decode all escaped characters
|
||||
while ((pos = out.find("__0")) != string::npos) {
|
||||
|
|
@ -170,7 +186,7 @@ class EmitCSyms final : EmitCBaseVisitor {
|
|||
|
||||
void varHierarchyScopes(string scp) {
|
||||
while (!scp.empty()) {
|
||||
const auto scpit = m_vpiScopeCandidates.find(scp);
|
||||
const auto scpit = m_vpiScopeCandidates.find(scopeSymString(scp));
|
||||
if ((scpit != m_vpiScopeCandidates.end())
|
||||
&& (m_scopeNames.find(scp) == m_scopeNames.end())) {
|
||||
const auto scopeNameit = m_scopeNames.find(scpit->second.m_symName);
|
||||
|
|
@ -219,19 +235,19 @@ class EmitCSyms final : EmitCBaseVisitor {
|
|||
}
|
||||
// UINFO(9, "For " << scopep->name() << " - " << varp->name() << " Scp "
|
||||
// << scpName << "Var " << varBase << endl);
|
||||
const string varBasePretty = AstNode::prettyName(varBase);
|
||||
const string scpPretty = AstNode::prettyName(scpName);
|
||||
const string scpSym = scopeSymString(scpName);
|
||||
const string varBasePretty = AstNode::prettyName(VName::dehash(varBase));
|
||||
const string scpPretty = AstNode::prettyName(VName::dehash(scpName));
|
||||
const string scpSym = scopeSymString(VName::dehash(scpName));
|
||||
// UINFO(9, " scnameins sp " << scpName << " sp " << scpPretty << " ss "
|
||||
// << scpSym << endl);
|
||||
if (v3Global.opt.vpi()) varHierarchyScopes(scpName);
|
||||
if (m_scopeNames.find(scpSym) == m_scopeNames.end()) {
|
||||
m_scopeNames.insert(std::make_pair(
|
||||
scpSym, ScopeData(scpSym, scpPretty, 0, "SCOPE_OTHER")));
|
||||
scpSym, ScopeData{scpSym, scpPretty, 0, "SCOPE_OTHER"}));
|
||||
}
|
||||
m_scopeVars.insert(
|
||||
std::make_pair(scpSym + " " + varp->name(),
|
||||
ScopeVarData(scpSym, varBasePretty, varp, modp, scopep)));
|
||||
ScopeVarData{scpSym, varBasePretty, varp, modp, scopep}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -296,10 +312,11 @@ class EmitCSyms final : EmitCBaseVisitor {
|
|||
const string type
|
||||
= (nodep->origModName() == "__BEGIN__") ? "SCOPE_OTHER" : "SCOPE_MODULE";
|
||||
const string name = nodep->scopep()->shortName() + "__DOT__" + nodep->name();
|
||||
const string name_dedot = AstNode::dedotName(name);
|
||||
const string name_pretty = AstNode::vpiName(name);
|
||||
const int timeunit = m_modp->timeunit().powerOfTen();
|
||||
m_vpiScopeCandidates.insert(
|
||||
std::make_pair(name, ScopeData(scopeSymString(name), name_dedot, timeunit, type)));
|
||||
std::make_pair(scopeSymString(name),
|
||||
ScopeData{scopeSymString(name), name_pretty, timeunit, type}));
|
||||
}
|
||||
}
|
||||
void visit(AstScope* nodep) override {
|
||||
|
|
@ -310,20 +327,20 @@ class EmitCSyms final : EmitCBaseVisitor {
|
|||
|
||||
if (v3Global.opt.vpi() && !nodep->isTop()) {
|
||||
const string type = VN_IS(nodep->modp(), Package) ? "SCOPE_OTHER" : "SCOPE_MODULE";
|
||||
const string name_dedot = AstNode::dedotName(nodep->shortName());
|
||||
const string name_pretty = AstNode::vpiName(nodep->shortName());
|
||||
const int timeunit = m_modp->timeunit().powerOfTen();
|
||||
m_vpiScopeCandidates.insert(
|
||||
std::make_pair(nodep->name(), ScopeData(scopeSymString(nodep->name()), name_dedot,
|
||||
timeunit, type)));
|
||||
m_vpiScopeCandidates.insert(std::make_pair(
|
||||
scopeSymString(nodep->name()),
|
||||
ScopeData{scopeSymString(nodep->name()), name_pretty, timeunit, type}));
|
||||
}
|
||||
}
|
||||
void visit(AstScopeName* nodep) override {
|
||||
const string name = nodep->scopeSymName();
|
||||
// UINFO(9,"scnameins sp "<<nodep->name()<<" sp "<<nodep->scopePrettySymName()
|
||||
// <<" ss"<<name<<endl);
|
||||
// UINFO(9, "scnameins sp " << nodep->name() << " sp " << nodep->scopePrettySymName()
|
||||
// << " ss" << name << endl);
|
||||
const int timeunit = m_modp ? m_modp->timeunit().powerOfTen() : 0;
|
||||
m_scopeNames.emplace(
|
||||
name, ScopeData(name, nodep->scopePrettySymName(), timeunit, "SCOPE_OTHER"));
|
||||
name, ScopeData{name, nodep->scopePrettySymName(), timeunit, "SCOPE_OTHER"});
|
||||
if (nodep->dpiExport()) {
|
||||
UASSERT_OBJ(m_cfuncp, nodep, "ScopeName not under DPI function");
|
||||
m_scopeFuncs.insert(std::make_pair(name + " " + m_cfuncp->name(),
|
||||
|
|
@ -332,8 +349,8 @@ class EmitCSyms final : EmitCBaseVisitor {
|
|||
if (m_scopeNames.find(nodep->scopeDpiName()) == m_scopeNames.end()) {
|
||||
m_scopeNames.insert(
|
||||
std::make_pair(nodep->scopeDpiName(),
|
||||
ScopeData(nodep->scopeDpiName(), nodep->scopePrettyDpiName(),
|
||||
timeunit, "SCOPE_OTHER")));
|
||||
ScopeData{nodep->scopeDpiName(), nodep->scopePrettyDpiName(),
|
||||
timeunit, "SCOPE_OTHER"}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -614,12 +614,17 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
|
|||
iterate(nodep->subDTypep());
|
||||
iterateAndNextConstNull(nodep->rangep());
|
||||
}
|
||||
void visit(AstRefDType* nodep) override { iterate(nodep->skipRefp()); }
|
||||
void visit(AstNodeUOrStructDType* nodep) override {
|
||||
puts(nodep->verilogKwd() + " ");
|
||||
if (nodep->packed()) puts("packed ");
|
||||
puts("\n");
|
||||
puts("{");
|
||||
iterateAndNextConstNull(nodep->membersp());
|
||||
for (AstMemberDType* itemp = nodep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
iterate(itemp);
|
||||
puts(";");
|
||||
}
|
||||
puts("}");
|
||||
}
|
||||
void visit(AstMemberDType* nodep) override {
|
||||
|
|
|
|||
233
src/V3Error.cpp
233
src/V3Error.cpp
|
|
@ -25,24 +25,6 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
|||
// clang-format on
|
||||
|
||||
//======================================================================
|
||||
// Statics
|
||||
|
||||
int V3Error::s_errCount = 0;
|
||||
int V3Error::s_warnCount = 0;
|
||||
int V3Error::s_debugDefault = 0;
|
||||
int V3Error::s_errorLimit = V3Error::MAX_ERRORS;
|
||||
bool V3Error::s_warnFatal = true;
|
||||
int V3Error::s_tellManual = 0;
|
||||
std::ostringstream V3Error::s_errorStr; // Error string being formed
|
||||
V3ErrorCode V3Error::s_errorCode = V3ErrorCode::EC_FATAL;
|
||||
bool V3Error::s_errorContexted = false;
|
||||
bool V3Error::s_errorSuppressed = false;
|
||||
std::array<bool, V3ErrorCode::_ENUM_MAX> V3Error::s_describedEachWarn;
|
||||
std::array<bool, V3ErrorCode::_ENUM_MAX> V3Error::s_pretendError;
|
||||
bool V3Error::s_describedWarnings = false;
|
||||
bool V3Error::s_describedWeb = false;
|
||||
V3Error::MessagesSet V3Error::s_messages;
|
||||
V3Error::ErrorExitCb V3Error::s_errorExitCb = nullptr;
|
||||
|
||||
struct v3errorIniter {
|
||||
v3errorIniter() { V3Error::init(); }
|
||||
|
|
@ -65,51 +47,10 @@ V3ErrorCode::V3ErrorCode(const char* msgp) {
|
|||
}
|
||||
|
||||
//######################################################################
|
||||
// V3Error class functions
|
||||
// V3ErrorGuarded class functions
|
||||
//
|
||||
|
||||
void V3Error::init() {
|
||||
for (int i = 0; i < V3ErrorCode::_ENUM_MAX; i++) {
|
||||
s_describedEachWarn[i] = false;
|
||||
s_pretendError[i] = V3ErrorCode{i}.pretendError();
|
||||
}
|
||||
if (VL_UNCOVERABLE(string(V3ErrorCode{V3ErrorCode::_ENUM_MAX}.ascii()) != " MAX")) {
|
||||
v3fatalSrc("Enum table in V3ErrorCode::EC_ascii() is munged");
|
||||
}
|
||||
}
|
||||
|
||||
string V3Error::lineStr(const char* filename, int lineno) {
|
||||
std::ostringstream out;
|
||||
const char* const fnslashp = std::strrchr(filename, '/');
|
||||
if (fnslashp) filename = fnslashp + 1;
|
||||
out << filename << ":" << std::dec << lineno << ":";
|
||||
const char* const spaces = " ";
|
||||
size_t numsp = out.str().length();
|
||||
if (numsp > 20) numsp = 20;
|
||||
out << (spaces + numsp);
|
||||
return out.str();
|
||||
}
|
||||
|
||||
void V3Error::incErrors() {
|
||||
s_errCount++;
|
||||
if (errorCount() == errorLimit()) { // Not >= as would otherwise recurse
|
||||
v3fatalExit("Exiting due to too many errors encountered; --error-limit=" //
|
||||
<< errorCount() << endl);
|
||||
}
|
||||
}
|
||||
|
||||
void V3Error::abortIfWarnings() {
|
||||
const bool exwarn = warnFatal() && warnCount();
|
||||
if (errorCount() && exwarn) {
|
||||
v3fatalExit("Exiting due to " << std::dec << errorCount() << " error(s), " //
|
||||
<< warnCount() << " warning(s)\n");
|
||||
} else if (errorCount()) {
|
||||
v3fatalExit("Exiting due to " << std::dec << errorCount() << " error(s)\n");
|
||||
} else if (exwarn) {
|
||||
v3fatalExit("Exiting due to " << std::dec << warnCount() << " warning(s)\n");
|
||||
}
|
||||
}
|
||||
|
||||
bool V3Error::isError(V3ErrorCode code, bool supp) {
|
||||
bool V3ErrorGuarded::isError(V3ErrorCode code, bool supp) VL_REQUIRES(m_mutex) {
|
||||
if (supp) {
|
||||
return false;
|
||||
} else if (code == V3ErrorCode::USERINFO) {
|
||||
|
|
@ -124,16 +65,16 @@ bool V3Error::isError(V3ErrorCode code, bool supp) {
|
|||
return true;
|
||||
} else if (code == V3ErrorCode::EC_ERROR) {
|
||||
return true;
|
||||
} else if (code < V3ErrorCode::EC_FIRST_WARN || s_pretendError[code]) {
|
||||
} else if (code < V3ErrorCode::EC_FIRST_WARN || pretendError(code)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
string V3Error::msgPrefix() {
|
||||
const V3ErrorCode code = s_errorCode;
|
||||
const bool supp = s_errorSuppressed;
|
||||
string V3ErrorGuarded::msgPrefix() VL_REQUIRES(m_mutex) {
|
||||
const V3ErrorCode code = m_errorCode;
|
||||
const bool supp = m_errorSuppressed;
|
||||
if (supp) {
|
||||
return "-arning-suppressed: ";
|
||||
} else if (code == V3ErrorCode::USERINFO) {
|
||||
|
|
@ -155,10 +96,7 @@ string V3Error::msgPrefix() {
|
|||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Abort/exit
|
||||
|
||||
void V3Error::vlAbortOrExit() {
|
||||
void V3ErrorGuarded::vlAbortOrExit() VL_REQUIRES(m_mutex) {
|
||||
if (V3Error::debugDefault()) {
|
||||
std::cerr << msgPrefix() << "Aborting since under --debug" << endl;
|
||||
V3Error::vlAbort();
|
||||
|
|
@ -167,50 +105,53 @@ void V3Error::vlAbortOrExit() {
|
|||
}
|
||||
}
|
||||
|
||||
void V3Error::vlAbort() {
|
||||
VL_GCOV_DUMP();
|
||||
std::abort();
|
||||
}
|
||||
string V3ErrorGuarded::warnMore() VL_REQUIRES(m_mutex) { return string(msgPrefix().size(), ' '); }
|
||||
|
||||
//======================================================================
|
||||
// Global Functions
|
||||
|
||||
void V3Error::suppressThisWarning() {
|
||||
void V3ErrorGuarded::suppressThisWarning() VL_REQUIRES(m_mutex) {
|
||||
#ifndef V3ERROR_NO_GLOBAL_
|
||||
V3Stats::addStatSum(std::string{"Warnings, Suppressed "} + s_errorCode.ascii(), 1);
|
||||
V3Stats::addStatSum(std::string{"Warnings, Suppressed "} + errorCode().ascii(), 1);
|
||||
#endif
|
||||
s_errorSuppressed = true;
|
||||
errorSuppressed(true);
|
||||
}
|
||||
|
||||
string V3Error::warnMore() { return string(msgPrefix().size(), ' '); }
|
||||
|
||||
// cppcheck-has-bug-suppress constParameter
|
||||
void V3Error::v3errorEnd(std::ostringstream& sstr, const string& extra) {
|
||||
void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra)
|
||||
VL_REQUIRES(m_mutex) {
|
||||
#if defined(__COVERITY__) || defined(__cppcheck__)
|
||||
if (s_errorCode == V3ErrorCode::EC_FATAL) __coverity_panic__(x);
|
||||
if (m_errorCode == V3ErrorCode::EC_FATAL) __coverity_panic__(x);
|
||||
#endif
|
||||
// Skip suppressed messages
|
||||
if (s_errorSuppressed
|
||||
if (m_errorSuppressed
|
||||
// On debug, show only non default-off warning to prevent pages of warnings
|
||||
&& (!debug() || s_errorCode.defaultsOff()))
|
||||
&& (!debug() || m_errorCode.defaultsOff()))
|
||||
return;
|
||||
string msg = msgPrefix() + sstr.str();
|
||||
if (s_errorSuppressed) { // If suppressed print only first line to reduce verbosity
|
||||
// If suppressed print only first line to reduce verbosity
|
||||
if (m_errorSuppressed) {
|
||||
string::size_type pos;
|
||||
if ((pos = msg.find('\n')) != string::npos) {
|
||||
msg.erase(pos, msg.length() - pos);
|
||||
msg += "...";
|
||||
}
|
||||
}
|
||||
string msg_additional;
|
||||
{
|
||||
string::size_type pos;
|
||||
if ((pos = msg.find(V3Error::warnAdditionalInfo())) != string::npos) {
|
||||
msg_additional = msg.substr(pos + V3Error::warnAdditionalInfo().size());
|
||||
msg.erase(pos);
|
||||
}
|
||||
}
|
||||
// Trailing newline (generally not on messages) & remove dup newlines
|
||||
{
|
||||
msg += '\n'; // Trailing newlines generally not put on messages so add
|
||||
string::size_type pos;
|
||||
while ((pos = msg.find("\n\n")) != string::npos) msg.erase(pos + 1, 1);
|
||||
while ((pos = msg_additional.find("\n\n")) != string::npos)
|
||||
msg_additional.erase(pos + 1, 1);
|
||||
}
|
||||
// Suppress duplicate messages
|
||||
if (s_messages.find(msg) != s_messages.end()) return;
|
||||
s_messages.insert(msg);
|
||||
if (!m_messages.insert(msg).second) return;
|
||||
if (!extra.empty()) {
|
||||
const string extraMsg = warnMore() + extra + "\n";
|
||||
const size_t pos = msg.find('\n');
|
||||
|
|
@ -219,44 +160,45 @@ void V3Error::v3errorEnd(std::ostringstream& sstr, const string& extra) {
|
|||
// Output
|
||||
if (
|
||||
#ifndef V3ERROR_NO_GLOBAL_
|
||||
!(v3Global.opt.quietExit() && s_errorCode == V3ErrorCode::EC_FATALEXIT)
|
||||
!(v3Global.opt.quietExit() && m_errorCode == V3ErrorCode::EC_FATALEXIT)
|
||||
#else
|
||||
true
|
||||
#endif
|
||||
) {
|
||||
std::cerr << msg;
|
||||
}
|
||||
if (!s_errorSuppressed
|
||||
&& !(s_errorCode == V3ErrorCode::EC_INFO || s_errorCode == V3ErrorCode::USERINFO)) {
|
||||
const bool anError = isError(s_errorCode, s_errorSuppressed);
|
||||
if (s_errorCode >= V3ErrorCode::EC_FIRST_NAMED && !s_describedWeb) {
|
||||
s_describedWeb = true;
|
||||
if (!m_errorSuppressed
|
||||
&& !(m_errorCode == V3ErrorCode::EC_INFO || m_errorCode == V3ErrorCode::USERINFO)) {
|
||||
const bool anError = isError(m_errorCode, m_errorSuppressed);
|
||||
if (m_errorCode >= V3ErrorCode::EC_FIRST_NAMED && !m_describedWeb) {
|
||||
m_describedWeb = true;
|
||||
std::cerr << warnMore() << "... For " << (anError ? "error" : "warning")
|
||||
<< " description see https://verilator.org/warn/" << s_errorCode.ascii()
|
||||
<< " description see https://verilator.org/warn/" << m_errorCode.ascii()
|
||||
<< "?v=" << PACKAGE_VERSION_NUMBER_STRING << endl;
|
||||
}
|
||||
if (!s_describedEachWarn[s_errorCode] && !s_pretendError[s_errorCode]) {
|
||||
s_describedEachWarn[s_errorCode] = true;
|
||||
if (s_errorCode >= V3ErrorCode::EC_FIRST_WARN && !s_describedWarnings) {
|
||||
s_describedWarnings = true;
|
||||
if (!m_describedEachWarn[m_errorCode] && !m_pretendError[m_errorCode]) {
|
||||
m_describedEachWarn[m_errorCode] = true;
|
||||
if (m_errorCode >= V3ErrorCode::EC_FIRST_WARN && !m_describedWarnings) {
|
||||
m_describedWarnings = true;
|
||||
std::cerr << warnMore() << "... Use \"/* verilator lint_off "
|
||||
<< s_errorCode.ascii()
|
||||
<< m_errorCode.ascii()
|
||||
<< " */\" and lint_on around source to disable this message." << endl;
|
||||
}
|
||||
if (s_errorCode.dangerous()) {
|
||||
if (m_errorCode.dangerous()) {
|
||||
std::cerr << warnMore() << "*** See https://verilator.org/warn/"
|
||||
<< s_errorCode.ascii() << " before disabling this,\n";
|
||||
<< m_errorCode.ascii() << " before disabling this,\n";
|
||||
std::cerr << warnMore() << "else you may end up with different sim results."
|
||||
<< endl;
|
||||
}
|
||||
}
|
||||
if (!msg_additional.empty()) { std::cerr << msg_additional; }
|
||||
// If first warning is not the user's fault (internal/unsupported) then give the website
|
||||
// Not later warnings, as a internal may be caused by an earlier problem
|
||||
if (s_tellManual == 0) {
|
||||
if (s_errorCode.mentionManual() || sstr.str().find("Unsupported") != string::npos) {
|
||||
s_tellManual = 1;
|
||||
if (tellManual() == 0) {
|
||||
if (m_errorCode.mentionManual() || sstr.str().find("Unsupported") != string::npos) {
|
||||
tellManual(1);
|
||||
} else {
|
||||
s_tellManual = 2;
|
||||
tellManual(2);
|
||||
}
|
||||
}
|
||||
if (anError) {
|
||||
|
|
@ -264,26 +206,34 @@ void V3Error::v3errorEnd(std::ostringstream& sstr, const string& extra) {
|
|||
} else {
|
||||
incWarnings();
|
||||
}
|
||||
if (s_errorCode == V3ErrorCode::EC_FATAL || s_errorCode == V3ErrorCode::EC_FATALEXIT
|
||||
|| s_errorCode == V3ErrorCode::EC_FATALSRC) {
|
||||
if (m_errorCode == V3ErrorCode::EC_FATAL || m_errorCode == V3ErrorCode::EC_FATALEXIT
|
||||
|| m_errorCode == V3ErrorCode::EC_FATALSRC) {
|
||||
static bool inFatal = false;
|
||||
if (!inFatal) {
|
||||
inFatal = true;
|
||||
if (s_tellManual == 1) {
|
||||
if (tellManual() == 1) {
|
||||
std::cerr << warnMore()
|
||||
<< "... See the manual at https://verilator.org/verilator_doc.html "
|
||||
"for more assistance."
|
||||
<< endl;
|
||||
s_tellManual = 2;
|
||||
tellManual(2);
|
||||
}
|
||||
#ifndef V3ERROR_NO_GLOBAL_
|
||||
if (dumpTree()) {
|
||||
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("final.tree", 990));
|
||||
}
|
||||
if (debug()) {
|
||||
if (s_errorExitCb) s_errorExitCb();
|
||||
V3Stats::statsFinalAll(v3Global.rootp());
|
||||
V3Stats::statsReport();
|
||||
if (dumpTree() || debug()) {
|
||||
V3ThreadPool::s().requestExclusiveAccess([&]() VL_REQUIRES(m_mutex) {
|
||||
if (dumpTree()) {
|
||||
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
|
||||
}
|
||||
|
|
@ -292,7 +242,52 @@ void V3Error::v3errorEnd(std::ostringstream& sstr, const string& extra) {
|
|||
} else if (anError) {
|
||||
// We don't dump tree on any error because a Visitor may be in middle of
|
||||
// a tree cleanup and cause a false broken problem.
|
||||
if (s_errorExitCb) s_errorExitCb();
|
||||
execErrorExitCb();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// V3Error class functions
|
||||
|
||||
void V3Error::init() {
|
||||
for (int i = 0; i < V3ErrorCode::_ENUM_MAX; i++) {
|
||||
describedEachWarn(static_cast<V3ErrorCode>(i), false);
|
||||
pretendError(static_cast<V3ErrorCode>(i), V3ErrorCode{i}.pretendError());
|
||||
}
|
||||
if (VL_UNCOVERABLE(string(V3ErrorCode{V3ErrorCode::_ENUM_MAX}.ascii()) != " MAX")) {
|
||||
v3fatalSrc("Enum table in V3ErrorCode::EC_ascii() is munged");
|
||||
}
|
||||
}
|
||||
|
||||
string V3Error::lineStr(const char* filename, int lineno) VL_PURE {
|
||||
std::ostringstream out;
|
||||
const char* const fnslashp = std::strrchr(filename, '/');
|
||||
if (fnslashp) filename = fnslashp + 1;
|
||||
out << filename << ":" << std::dec << lineno << ":";
|
||||
const char* const spaces = " ";
|
||||
size_t numsp = out.str().length();
|
||||
if (numsp > 20) numsp = 20;
|
||||
out << (spaces + numsp);
|
||||
return out.str();
|
||||
}
|
||||
|
||||
void V3Error::abortIfWarnings() {
|
||||
const bool exwarn = warnFatal() && warnCount();
|
||||
if (errorCount() && exwarn) {
|
||||
v3fatalExit("Exiting due to " << std::dec << V3Error::s().errorCount() << " error(s), " //
|
||||
<< V3Error::s().warnCount() << " warning(s)\n");
|
||||
} else if (errorCount()) {
|
||||
v3fatalExit("Exiting due to " << std::dec << V3Error::s().errorCount() << " error(s)\n");
|
||||
} else if (exwarn) {
|
||||
v3fatalExit("Exiting due to " << std::dec << V3Error::s().warnCount() << " warning(s)\n");
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Abort/exit
|
||||
|
||||
void V3Error::vlAbort() {
|
||||
VL_GCOV_DUMP();
|
||||
std::abort();
|
||||
}
|
||||
|
|
|
|||
336
src/V3Error.h
336
src/V3Error.h
|
|
@ -20,8 +20,13 @@
|
|||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated_threads.h"
|
||||
|
||||
// Limited V3 headers here - this is a base class for Vlc etc
|
||||
#include "V3String.h"
|
||||
#ifndef V3ERROR_NO_GLOBAL_
|
||||
#include "V3ThreadPool.h"
|
||||
#endif
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
|
|
@ -77,7 +82,7 @@ public:
|
|||
CASEWITHX, // Case with X values
|
||||
CASEX, // Casex
|
||||
CASTCONST, // Cast is constant
|
||||
CDCRSTLOGIC, // Logic in async reset path
|
||||
CDCRSTLOGIC, // Logic in async reset path. Historical, never issued.
|
||||
CLKDATA, // Clock used as data. Historical, never issued.
|
||||
CMPCONST, // Comparison is constant due to limited range
|
||||
COLONPLUS, // :+ instead of +:
|
||||
|
|
@ -147,6 +152,9 @@ public:
|
|||
VARHIDDEN, // Hiding variable
|
||||
WAITCONST, // Wait condition is constant
|
||||
WIDTH, // Width mismatch
|
||||
WIDTHTRUNC, // Width mismatch- lhs < rhs
|
||||
WIDTHEXPAND, // Width mismatch- lhs > rhs
|
||||
WIDTHXZEXPAND, // Width mismatch- lhs > rhs xz filled
|
||||
WIDTHCONCAT, // Unsized numbers/parameters in concatenations
|
||||
ZERODLY, // #0 delay
|
||||
_ENUM_MAX
|
||||
|
|
@ -184,7 +192,7 @@ public:
|
|||
"IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE",
|
||||
"INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE",
|
||||
"LATCH", "LITENDIAN", "MINTYPMAXDLY", "MODDUP",
|
||||
"MULTIDRIVEN", "MULTITOP","NOLATCH", "NULLPORT", "PINCONNECTEMPTY",
|
||||
"MULTIDRIVEN", "MULTITOP", "NOLATCH", "NULLPORT", "PINCONNECTEMPTY",
|
||||
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PROCASSWIRE",
|
||||
"PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY",
|
||||
"SELRANGE", "SHORTREAL", "SPLITVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET",
|
||||
|
|
@ -192,7 +200,7 @@ public:
|
|||
"UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS",
|
||||
"UNPACKED", "UNSIGNED", "UNUSEDGENVAR", "UNUSEDPARAM", "UNUSEDSIGNAL",
|
||||
"USERERROR", "USERFATAL", "USERINFO", "USERWARN",
|
||||
"VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHCONCAT", "ZERODLY",
|
||||
"VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHTRUNC", "WIDTHEXPAND", "WIDTHXZEXPAND", "WIDTHCONCAT", "ZERODLY",
|
||||
" MAX"
|
||||
};
|
||||
// clang-format on
|
||||
|
|
@ -223,7 +231,8 @@ public:
|
|||
|| 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 == LITENDIAN || m_e == PINMISSING || m_e == REALCVT
|
||||
|| m_e == UNSIGNED || m_e == WIDTH);
|
||||
|| 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 {
|
||||
|
|
@ -238,6 +247,20 @@ public:
|
|||
bool unusedError() const VL_MT_SAFE {
|
||||
return (m_e == UNUSEDGENVAR || m_e == UNUSEDPARAM || m_e == UNUSEDSIGNAL);
|
||||
}
|
||||
|
||||
bool isUnder(V3ErrorCode other) {
|
||||
// backwards compatibility inheritance-like warnings
|
||||
if (m_e == other) { return true; }
|
||||
if (other == V3ErrorCode::WIDTH) {
|
||||
return (m_e == WIDTH || m_e == WIDTHEXPAND || m_e == WIDTHTRUNC
|
||||
|| m_e == WIDTHXZEXPAND);
|
||||
}
|
||||
if (other == V3ErrorCode::I_UNUSED) {
|
||||
return (m_e == UNUSEDGENVAR || m_e == UNUSEDPARAM || m_e == UNUSEDSIGNAL);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool unusedMsg(const char* msgp) { return 0 == VL_STRCASECMP(msgp, "UNUSED"); }
|
||||
};
|
||||
constexpr bool operator==(const V3ErrorCode& lhs, const V3ErrorCode& rhs) {
|
||||
|
|
@ -250,71 +273,224 @@ inline std::ostream& operator<<(std::ostream& os, const V3ErrorCode& rhs) {
|
|||
}
|
||||
|
||||
// ######################################################################
|
||||
|
||||
class V3Error final {
|
||||
// Base class for any object that wants debugging and error reporting
|
||||
|
||||
class V3ErrorGuarded final {
|
||||
// Should only be used by V3ErrorGuarded::m_mutex is already locked
|
||||
// contains guarded members
|
||||
public:
|
||||
using MessagesSet = std::set<std::string>;
|
||||
using ErrorExitCb = void (*)(void);
|
||||
|
||||
private:
|
||||
static bool s_describedWarnings; // Told user how to disable warns
|
||||
static bool s_describedWeb; // Told user to see web
|
||||
static std::array<bool, V3ErrorCode::_ENUM_MAX>
|
||||
s_describedEachWarn; // Told user specifics about this warning
|
||||
static std::array<bool, V3ErrorCode::_ENUM_MAX>
|
||||
s_pretendError; // Pretend this warning is an error
|
||||
static int s_debugDefault; // Option: --debugi Default debugging level
|
||||
static int s_errorLimit; // Option: --error-limit Number of errors before exit
|
||||
static bool s_warnFatal; // Option: --warnFatal Warnings are fatal
|
||||
static int s_errCount; // Error count
|
||||
static int s_warnCount; // Warning count
|
||||
static int s_tellManual; // Tell user to see manual, 0=not yet, 1=doit, 2=disable
|
||||
static std::ostringstream s_errorStr; // Error string being formed
|
||||
static V3ErrorCode s_errorCode; // Error string being formed will abort
|
||||
static bool s_errorContexted; // Error being formed got context
|
||||
static bool s_errorSuppressed; // Error being formed should be suppressed
|
||||
static MessagesSet s_messages; // What errors we've outputted
|
||||
static ErrorExitCb s_errorExitCb; // Callback when error occurs for dumping
|
||||
|
||||
static constexpr unsigned MAX_ERRORS = 50; // Fatal after this may errors
|
||||
|
||||
bool m_describedWarnings VL_GUARDED_BY(m_mutex) = false; // Told user how to disable warns
|
||||
// Tell user to see manual, 0=not yet, 1=doit, 2=disable
|
||||
int m_tellManual VL_GUARDED_BY(m_mutex) = 0;
|
||||
V3ErrorCode m_errorCode VL_GUARDED_BY(m_mutex)
|
||||
= V3ErrorCode::EC_FATAL; // Error string being formed will abort
|
||||
bool m_errorSuppressed VL_GUARDED_BY(m_mutex)
|
||||
= false; // Error being formed should be suppressed
|
||||
MessagesSet m_messages VL_GUARDED_BY(m_mutex); // What errors we've outputted
|
||||
ErrorExitCb m_errorExitCb VL_GUARDED_BY(m_mutex)
|
||||
= nullptr; // Callback when error occurs for dumping
|
||||
bool m_errorContexted VL_GUARDED_BY(m_mutex) = false; // Error being formed got context
|
||||
int m_warnCount VL_GUARDED_BY(m_mutex) = 0; // Warning count
|
||||
int m_errCount VL_GUARDED_BY(m_mutex) = 0; // Error count
|
||||
// Pretend this warning is an error
|
||||
std::array<bool, V3ErrorCode::_ENUM_MAX> m_pretendError VL_GUARDED_BY(m_mutex);
|
||||
bool m_describedWeb VL_GUARDED_BY(m_mutex) = false; // Told user to see web
|
||||
// Told user specifics about this warning
|
||||
std::array<bool, V3ErrorCode::_ENUM_MAX> m_describedEachWarn VL_GUARDED_BY(m_mutex);
|
||||
int m_debugDefault = 0; // Option: --debugi Default debugging level
|
||||
int m_errorLimit VL_GUARDED_BY(m_mutex)
|
||||
= MAX_ERRORS; // Option: --error-limit Number of errors before exit
|
||||
bool m_warnFatal VL_GUARDED_BY(m_mutex) = true; // Option: --warnFatal Warnings are fatal
|
||||
std::ostringstream m_errorStr VL_GUARDED_BY(m_mutex); // Error string being formed
|
||||
public:
|
||||
VerilatedMutex m_mutex; // Make sure only single thread is in class
|
||||
|
||||
string msgPrefix() VL_REQUIRES(m_mutex); // returns %Error/%Warn
|
||||
string warnMore() VL_REQUIRES(m_mutex);
|
||||
void execErrorExitCb() VL_REQUIRES(m_mutex) {
|
||||
if (m_errorExitCb) m_errorExitCb();
|
||||
}
|
||||
void errorExitCb(ErrorExitCb cb) VL_REQUIRES(m_mutex) { m_errorExitCb = cb; }
|
||||
ErrorExitCb errorExitCb() VL_REQUIRES(m_mutex) { return m_errorExitCb; }
|
||||
bool isError(V3ErrorCode code, bool supp) VL_REQUIRES(m_mutex);
|
||||
void vlAbortOrExit() VL_REQUIRES(m_mutex);
|
||||
void errorContexted(bool flag) VL_REQUIRES(m_mutex) { m_errorContexted = flag; }
|
||||
void incWarnings() VL_REQUIRES(m_mutex) { m_warnCount++; }
|
||||
void incErrors() VL_REQUIRES(m_mutex) {
|
||||
m_errCount++;
|
||||
if (errorCount() == errorLimit()) { // Not >= as would otherwise recurse
|
||||
v3errorEnd(
|
||||
(v3errorPrep(V3ErrorCode::EC_FATALEXIT),
|
||||
(v3errorStr() << "Exiting due to too many errors encountered; --error-limit="
|
||||
<< errorCount() << endl),
|
||||
v3errorStr()));
|
||||
assert(0); // LCOV_EXCL_LINE
|
||||
VL_UNREACHABLE;
|
||||
}
|
||||
}
|
||||
int errorCount() VL_REQUIRES(m_mutex) { return m_errCount; }
|
||||
bool pretendError(int errorCode) VL_REQUIRES(m_mutex) { return m_pretendError[errorCode]; }
|
||||
void pretendError(V3ErrorCode code, bool flag) VL_REQUIRES(m_mutex) {
|
||||
if (code == V3ErrorCode::WIDTH) {
|
||||
m_pretendError[V3ErrorCode::WIDTHTRUNC] = flag;
|
||||
m_pretendError[V3ErrorCode::WIDTHEXPAND] = flag;
|
||||
m_pretendError[V3ErrorCode::WIDTHXZEXPAND] = flag;
|
||||
}
|
||||
m_pretendError[code] = flag;
|
||||
}
|
||||
void debugDefault(int level) VL_MT_UNSAFE { m_debugDefault = level; }
|
||||
int debugDefault() VL_MT_SAFE { return m_debugDefault; }
|
||||
void errorLimit(int level) VL_REQUIRES(m_mutex) { m_errorLimit = level; }
|
||||
int errorLimit() VL_REQUIRES(m_mutex) { return m_errorLimit; }
|
||||
void warnFatal(bool flag) VL_REQUIRES(m_mutex) { m_warnFatal = flag; }
|
||||
bool warnFatal() VL_REQUIRES(m_mutex) { return m_warnFatal; }
|
||||
void v3errorPrep(V3ErrorCode code) VL_REQUIRES(m_mutex) {
|
||||
m_errorStr.str("");
|
||||
m_errorCode = code;
|
||||
m_errorContexted = false;
|
||||
m_errorSuppressed = false;
|
||||
}
|
||||
std::ostringstream& v3errorStr() VL_REQUIRES(m_mutex) { return m_errorStr; }
|
||||
V3ErrorCode errorCode() VL_REQUIRES(m_mutex) { return m_errorCode; }
|
||||
bool errorContexted() VL_REQUIRES(m_mutex) { return m_errorContexted; }
|
||||
int warnCount() VL_REQUIRES(m_mutex) { return m_warnCount; }
|
||||
bool errorSuppressed() VL_REQUIRES(m_mutex) { return m_errorSuppressed; }
|
||||
void errorSuppressed(bool flag) VL_REQUIRES(m_mutex) { m_errorSuppressed = flag; }
|
||||
bool describedWeb() VL_REQUIRES(m_mutex) { return m_describedWeb; }
|
||||
void describedWeb(bool flag) VL_REQUIRES(m_mutex) { m_describedWeb = flag; }
|
||||
bool describedEachWarn(V3ErrorCode code) VL_REQUIRES(m_mutex) {
|
||||
return m_describedEachWarn[code];
|
||||
}
|
||||
void describedEachWarn(V3ErrorCode code, bool flag) VL_REQUIRES(m_mutex) {
|
||||
m_describedEachWarn[code] = flag;
|
||||
}
|
||||
bool describedWarnings() VL_REQUIRES(m_mutex) { return m_describedWarnings; }
|
||||
void describedWarnings(bool flag) VL_REQUIRES(m_mutex) { m_describedWarnings = flag; }
|
||||
int tellManual() VL_REQUIRES(m_mutex) { return m_tellManual; }
|
||||
void tellManual(int level) VL_REQUIRES(m_mutex) { m_tellManual = level; }
|
||||
void v3errorEnd(std::ostringstream& sstr, const string& extra = "") VL_REQUIRES(m_mutex);
|
||||
void suppressThisWarning() VL_REQUIRES(m_mutex);
|
||||
string warnContextNone() VL_REQUIRES(m_mutex) {
|
||||
errorContexted(true);
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
// ######################################################################
|
||||
class V3Error final {
|
||||
// Base class for any object that wants debugging and error reporting
|
||||
private:
|
||||
// CONSTRUCTORS
|
||||
V3Error() {
|
||||
std::cerr << ("Static class");
|
||||
V3Error::vlAbort();
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
static V3ErrorGuarded& s() VL_MT_SAFE { // Singleton
|
||||
static V3ErrorGuarded s_s;
|
||||
return s_s;
|
||||
}
|
||||
|
||||
// ACCESSORS
|
||||
static void debugDefault(int level) { s_debugDefault = level; }
|
||||
static int debugDefault() VL_MT_SAFE { return s_debugDefault; }
|
||||
static void errorLimit(int level) { s_errorLimit = level; }
|
||||
static int errorLimit() VL_MT_SAFE { return s_errorLimit; }
|
||||
static void warnFatal(bool flag) { s_warnFatal = flag; }
|
||||
static bool warnFatal() { return s_warnFatal; }
|
||||
static string msgPrefix(); // returns %Error/%Warn
|
||||
static int errorCount() VL_MT_SAFE { return s_errCount; }
|
||||
static int warnCount() { return s_warnCount; }
|
||||
static bool errorContexted() VL_MT_SAFE { return s_errorContexted; }
|
||||
static void errorContexted(bool flag) { s_errorContexted = flag; }
|
||||
static void debugDefault(int level) VL_MT_UNSAFE { s().debugDefault(level); }
|
||||
static int debugDefault() VL_MT_SAFE { return s().debugDefault(); }
|
||||
static void errorLimit(int level) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
|
||||
const VerilatedLockGuard guard{s().m_mutex};
|
||||
s().errorLimit(level);
|
||||
}
|
||||
static int errorLimit() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
|
||||
const VerilatedLockGuard guard{s().m_mutex};
|
||||
return s().errorLimit();
|
||||
}
|
||||
static void warnFatal(bool flag) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
|
||||
const VerilatedLockGuard guard{s().m_mutex};
|
||||
s().warnFatal(flag);
|
||||
}
|
||||
static bool warnFatal() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
|
||||
const VerilatedLockGuard guard{s().m_mutex};
|
||||
return s().warnFatal();
|
||||
}
|
||||
// returns %Error/%Warn
|
||||
static string msgPrefix() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
|
||||
const VerilatedLockGuard guard{s().m_mutex};
|
||||
return s().msgPrefix();
|
||||
}
|
||||
static int errorCount() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
|
||||
const VerilatedLockGuard guard{s().m_mutex};
|
||||
return s().errorCount();
|
||||
}
|
||||
static bool pretendError(int errorCode) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
|
||||
const VerilatedLockGuard guard{s().m_mutex};
|
||||
return s().pretendError(errorCode);
|
||||
}
|
||||
static int warnCount() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
|
||||
const VerilatedLockGuard guard{s().m_mutex};
|
||||
return s().warnCount();
|
||||
}
|
||||
static bool errorContexted() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
|
||||
const VerilatedLockGuard guard{s().m_mutex};
|
||||
return s().errorContexted();
|
||||
}
|
||||
static void errorContexted(bool flag) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
|
||||
const VerilatedLockGuard guard{s().m_mutex};
|
||||
s().errorContexted(flag);
|
||||
}
|
||||
static void describedEachWarn(V3ErrorCode code, bool flag) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
|
||||
const VerilatedLockGuard guard{s().m_mutex};
|
||||
s().describedEachWarn(code, flag);
|
||||
}
|
||||
// METHODS
|
||||
static void incErrors();
|
||||
static void incWarnings() { s_warnCount++; }
|
||||
static void incErrors() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
|
||||
const VerilatedLockGuard guard{s().m_mutex};
|
||||
s().incErrors();
|
||||
}
|
||||
static void incWarnings() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
|
||||
const VerilatedLockGuard guard{s().m_mutex};
|
||||
s().incWarnings();
|
||||
}
|
||||
static void init();
|
||||
static void abortIfErrors() {
|
||||
if (errorCount()) abortIfWarnings();
|
||||
}
|
||||
static void abortIfWarnings();
|
||||
static void suppressThisWarning(); // Suppress next %Warn if user has it off
|
||||
static void pretendError(V3ErrorCode code, bool flag) { s_pretendError[code] = flag; }
|
||||
static bool isError(V3ErrorCode code, bool supp);
|
||||
static string lineStr(const char* filename, int lineno);
|
||||
static V3ErrorCode errorCode() VL_MT_SAFE { return s_errorCode; }
|
||||
static void errorExitCb(ErrorExitCb cb) { s_errorExitCb = cb; }
|
||||
// Suppress next %Warn if user has it off
|
||||
static void suppressThisWarning() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
|
||||
const VerilatedLockGuard guard{s().m_mutex};
|
||||
s().suppressThisWarning();
|
||||
}
|
||||
static void pretendError(V3ErrorCode code, bool flag) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
|
||||
const VerilatedLockGuard guard{s().m_mutex};
|
||||
s().pretendError(code, flag);
|
||||
}
|
||||
static string lineStr(const char* filename, int lineno) VL_PURE;
|
||||
static V3ErrorCode errorCode() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
|
||||
const VerilatedLockGuard guard{s().m_mutex};
|
||||
return s().errorCode();
|
||||
}
|
||||
static void errorExitCb(V3ErrorGuarded::ErrorExitCb cb) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
|
||||
const VerilatedLockGuard guard{s().m_mutex};
|
||||
s().errorExitCb(cb);
|
||||
}
|
||||
|
||||
// When printing an error/warning, print prefix for multiline message
|
||||
static string warnMore();
|
||||
static string warnMore() VL_REQUIRES(s().m_mutex) { return s().warnMore(); }
|
||||
// This function should only be used when it is impossible to
|
||||
// generate whole error message inside v3warn macros and it needs to be
|
||||
// streamed directly to cerr.
|
||||
// Use with caution as this function isn't MT_SAFE.
|
||||
static string warnMoreStandalone() VL_EXCLUDES(s().m_mutex) VL_MT_UNSAFE {
|
||||
const VerilatedLockGuard guard{s().m_mutex};
|
||||
return s().warnMore();
|
||||
}
|
||||
// This function marks place in error message from which point message
|
||||
// should be printed after information on the error code.
|
||||
// The post-processing is done in v3errorEnd function.
|
||||
static string warnAdditionalInfo() VL_MT_SAFE { return "__WARNADDITIONALINFO__"; }
|
||||
/// When building an error, don't show context info
|
||||
static string warnContextNone() {
|
||||
V3Error::errorContexted(true);
|
||||
|
|
@ -323,36 +499,66 @@ public:
|
|||
|
||||
// Internals for v3error()/v3fatal() macros only
|
||||
// Error end takes the string stream to output, be careful to seek() as needed
|
||||
static void v3errorPrep(V3ErrorCode code) {
|
||||
s_errorStr.str("");
|
||||
s_errorCode = code;
|
||||
s_errorContexted = false;
|
||||
s_errorSuppressed = false;
|
||||
static void v3errorPrep(V3ErrorCode code) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
|
||||
const VerilatedLockGuard guard{s().m_mutex};
|
||||
s().v3errorPrep(code);
|
||||
}
|
||||
static std::ostringstream& v3errorStr() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
|
||||
const VerilatedLockGuard guard{s().m_mutex};
|
||||
return s().v3errorStr();
|
||||
}
|
||||
static std::ostringstream& v3errorStr() { return s_errorStr; }
|
||||
static void vlAbortOrExit();
|
||||
static void vlAbort();
|
||||
// static, but often overridden in classes.
|
||||
static void v3errorEnd(std::ostringstream& sstr, const string& extra = "");
|
||||
static void v3errorEnd(std::ostringstream& sstr, const string& extra = "")
|
||||
VL_MT_SAFE_EXCLUDES(s().m_mutex) VL_MT_SAFE {
|
||||
const VerilatedLockGuard guard{s().m_mutex};
|
||||
s().v3errorEnd(sstr, extra);
|
||||
}
|
||||
// We can't call 's().v3errorEnd' directly in 'v3ErrorEnd'/'v3errorEndFatal',
|
||||
// due to bug in GCC (tested on 11.3.0 version with --enable-m32)
|
||||
// causing internal error when backtrace is printed.
|
||||
// Instead use this wrapper.
|
||||
static void v3errorEndGuardedCall(std::ostringstream& sstr, const string& extra = "")
|
||||
VL_REQUIRES(s().m_mutex) VL_MT_SAFE {
|
||||
s().v3errorEnd(sstr, extra);
|
||||
}
|
||||
};
|
||||
|
||||
// Global versions, so that if the class doesn't define a operator, we get the functions anyways.
|
||||
inline void v3errorEnd(std::ostringstream& sstr) { V3Error::v3errorEnd(sstr); }
|
||||
inline void v3errorEndFatal(std::ostringstream& sstr) {
|
||||
V3Error::v3errorEnd(sstr);
|
||||
inline void v3errorEnd(std::ostringstream& sstr) VL_REQUIRES(V3Error::s().m_mutex) VL_MT_SAFE {
|
||||
V3Error::v3errorEndGuardedCall(sstr);
|
||||
}
|
||||
inline void v3errorEndFatal(std::ostringstream& sstr)
|
||||
VL_REQUIRES(V3Error::s().m_mutex) VL_MT_SAFE {
|
||||
V3Error::v3errorEndGuardedCall(sstr);
|
||||
assert(0); // LCOV_EXCL_LINE
|
||||
VL_UNREACHABLE;
|
||||
}
|
||||
|
||||
#ifndef V3ERROR_NO_GLOBAL_
|
||||
#define V3ErrorLockAndCheckStopRequested \
|
||||
V3Error::s().m_mutex.lockCheckStopRequest( \
|
||||
[]() -> void { V3ThreadPool::s().waitIfStopRequested(); })
|
||||
#else
|
||||
#define V3ErrorLockAndCheckStopRequested V3Error::s().m_mutex.lock()
|
||||
#endif
|
||||
|
||||
// Theses allow errors using << operators: v3error("foo"<<"bar");
|
||||
// Careful, you can't put () around msg, as you would in most macro definitions
|
||||
// Note the commas are the comma operator, not separating arguments. These are needed to ensure
|
||||
// evaluation order as otherwise we couldn't ensure v3errorPrep is called first.
|
||||
// Note: due to limitations of clang thread-safety analysis, we can't use
|
||||
// lock guard here, instead we are locking the mutex as first operation in temporary,
|
||||
// but we are unlocking the mutex after function using comma operator.
|
||||
// This way macros should also work when they are in 'if' stmt without '{}'.
|
||||
#define v3warnCode(code, msg) \
|
||||
v3errorEnd((V3Error::v3errorPrep(code), (V3Error::v3errorStr() << msg), V3Error::v3errorStr()))
|
||||
v3errorEnd((V3ErrorLockAndCheckStopRequested, V3Error::s().v3errorPrep(code), \
|
||||
(V3Error::s().v3errorStr() << msg), V3Error::s().v3errorStr())), \
|
||||
V3Error::s().m_mutex.unlock()
|
||||
#define v3warnCodeFatal(code, msg) \
|
||||
v3errorEndFatal( \
|
||||
(V3Error::v3errorPrep(code), (V3Error::v3errorStr() << msg), V3Error::v3errorStr()))
|
||||
v3errorEndFatal((V3ErrorLockAndCheckStopRequested, V3Error::s().v3errorPrep(code), \
|
||||
(V3Error::s().v3errorStr() << msg), V3Error::s().v3errorStr())), \
|
||||
V3Error::s().m_mutex.unlock()
|
||||
#define v3warn(code, msg) v3warnCode(V3ErrorCode::code, msg)
|
||||
#define v3info(msg) v3warnCode(V3ErrorCode::EC_INFO, msg)
|
||||
#define v3error(msg) v3warnCode(V3ErrorCode::EC_ERROR, msg)
|
||||
|
|
@ -365,8 +571,10 @@ inline void v3errorEndFatal(std::ostringstream& sstr) {
|
|||
__FILE__ << ":" << std::dec << __LINE__ << ": " << msg)
|
||||
// Use this when normal v3fatal is called in static method that overrides fileline.
|
||||
#define v3fatalStatic(msg) \
|
||||
(::v3errorEndFatal((V3Error::v3errorPrep(V3ErrorCode::EC_FATAL), \
|
||||
(V3Error::v3errorStr() << msg), V3Error::v3errorStr())))
|
||||
(::v3errorEndFatal((V3ErrorLockAndCheckStopRequested, \
|
||||
V3Error::s().v3errorPrep(V3ErrorCode::EC_FATAL), \
|
||||
(V3Error::s().v3errorStr() << msg), V3Error::s().v3errorStr()))), \
|
||||
V3Error::s().m_mutex.unlock()
|
||||
|
||||
#define UINFO(level, stmsg) \
|
||||
do { \
|
||||
|
|
|
|||
|
|
@ -633,7 +633,7 @@ bool V3OutFormatter::tokenMatch(const char* cp, const char* cmp) {
|
|||
++cmp;
|
||||
}
|
||||
if (*cmp) return false;
|
||||
if (*cp && !isspace(*cp)) return false;
|
||||
if (*cp && !std::isspace(*cp)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -659,7 +659,7 @@ int V3OutFormatter::endLevels(const char* strg) {
|
|||
int levels = m_indentLevel;
|
||||
{
|
||||
const char* cp = strg;
|
||||
while (isspace(*cp)) ++cp;
|
||||
while (std::isspace(*cp)) ++cp;
|
||||
switch (*cp) {
|
||||
case '\n': // Newlines.. No need for whitespace before it
|
||||
return 0;
|
||||
|
|
@ -669,7 +669,7 @@ int V3OutFormatter::endLevels(const char* strg) {
|
|||
{
|
||||
// label/public/private: Deindent by 2 spaces
|
||||
const char* mp = cp;
|
||||
for (; isalnum(*mp); ++mp) {}
|
||||
for (; std::isalnum(*mp); ++mp) {}
|
||||
if (mp[0] == ':' && mp[1] != ':') return (levels - m_blockIndent / 2);
|
||||
}
|
||||
}
|
||||
|
|
@ -708,7 +708,7 @@ void V3OutFormatter::puts(const char* strg) {
|
|||
bool equalsForBracket = false; // Looking for "= {"
|
||||
for (const char* cp = strg; *cp; ++cp) {
|
||||
putcNoTracking(*cp);
|
||||
if (isalpha(*cp)) {
|
||||
if (std::isalpha(*cp)) {
|
||||
if (wordstart && m_lang == LA_VERILOG && tokenNotStart(cp)) notstart = true;
|
||||
if (wordstart && m_lang == LA_VERILOG && !notstart && tokenStart(cp)) indentInc();
|
||||
if (wordstart && m_lang == LA_VERILOG && tokenEnd(cp)) indentDec();
|
||||
|
|
@ -870,7 +870,7 @@ string V3OutFormatter::quoteNameControls(const string& namein, V3OutFormatter::L
|
|||
out += std::string{">"};
|
||||
} else if (c == '&') {
|
||||
out += std::string{"&"};
|
||||
} else if (isprint(c)) {
|
||||
} else if (std::isprint(c)) {
|
||||
out += c;
|
||||
} else {
|
||||
out += std::string{"&#"} + cvtToStr((unsigned int)(c & 0xff)) + ";";
|
||||
|
|
@ -887,7 +887,7 @@ string V3OutFormatter::quoteNameControls(const string& namein, V3OutFormatter::L
|
|||
out += "\\r";
|
||||
} else if (c == '\t') {
|
||||
out += "\\t";
|
||||
} else if (isprint(c)) {
|
||||
} else if (std::isprint(c)) {
|
||||
out += c;
|
||||
} else {
|
||||
// This will also cover \a etc
|
||||
|
|
@ -942,7 +942,7 @@ void V3OutCFile::putsGuard() {
|
|||
string var
|
||||
= VString::upcase(std::string{"VERILATED_"} + V3Os::filenameNonDir(filename()) + "_");
|
||||
for (char& c : var) {
|
||||
if (!isalnum(c)) c = '_';
|
||||
if (!std::isalnum(c)) c = '_';
|
||||
}
|
||||
puts("\n#ifndef " + var + "\n");
|
||||
puts("#define " + var + " // guard\n");
|
||||
|
|
|
|||
|
|
@ -210,26 +210,26 @@ void FileLine::lineDirective(const char* textp, int& enterExitRef) {
|
|||
// will come from the same stream as the previous line.
|
||||
|
||||
// Skip `line
|
||||
while (*textp && isspace(*textp)) textp++;
|
||||
while (*textp && !isspace(*textp)) textp++;
|
||||
while (*textp && (isspace(*textp) || *textp == '"')) textp++;
|
||||
while (*textp && std::isspace(*textp)) ++textp;
|
||||
while (*textp && !std::isspace(*textp)) ++textp;
|
||||
while (*textp && (std::isspace(*textp) || *textp == '"')) ++textp;
|
||||
|
||||
// Grab linenumber
|
||||
bool fail = false;
|
||||
const char* const ln = textp;
|
||||
while (*textp && !isspace(*textp)) textp++;
|
||||
if (isdigit(*ln)) {
|
||||
while (*textp && !std::isspace(*textp)) ++textp;
|
||||
if (std::isdigit(*ln)) {
|
||||
lineno(std::atoi(ln));
|
||||
} else {
|
||||
fail = true;
|
||||
}
|
||||
while (*textp && (isspace(*textp))) textp++;
|
||||
while (*textp && (std::isspace(*textp))) ++textp;
|
||||
if (*textp != '"') fail = true;
|
||||
while (*textp && (isspace(*textp) || *textp == '"')) textp++;
|
||||
while (*textp && (std::isspace(*textp) || *textp == '"')) ++textp;
|
||||
|
||||
// Grab filename
|
||||
const char* const fn = textp;
|
||||
while (*textp && !(isspace(*textp) || *textp == '"')) textp++;
|
||||
while (*textp && !(std::isspace(*textp) || *textp == '"')) ++textp;
|
||||
if (textp != fn) {
|
||||
string strfn = fn;
|
||||
strfn = strfn.substr(0, textp - fn);
|
||||
|
|
@ -239,8 +239,8 @@ void FileLine::lineDirective(const char* textp, int& enterExitRef) {
|
|||
}
|
||||
|
||||
// Grab level
|
||||
while (*textp && (isspace(*textp) || *textp == '"')) textp++;
|
||||
if (isdigit(*textp)) {
|
||||
while (*textp && (std::isspace(*textp) || *textp == '"')) ++textp;
|
||||
if (std::isdigit(*textp)) {
|
||||
enterExitRef = std::atoi(textp);
|
||||
if (enterExitRef >= 3) fail = true;
|
||||
} else {
|
||||
|
|
@ -322,7 +322,7 @@ string FileLine::asciiLineCol() const {
|
|||
+ "-" + cvtToStr(lastColumn()) + "[" + (m_contentp ? m_contentp->ascii() : "ct0") + "+"
|
||||
+ cvtToStr(m_contentLineno) + "]");
|
||||
}
|
||||
string FileLine::ascii() const VL_MT_SAFE {
|
||||
string FileLine::ascii() const {
|
||||
// For most errors especially in the parser the lastLineno is more accurate than firstLineno
|
||||
return filename() + ":" + cvtToStr(lastLineno()) + ":" + cvtToStr(firstColumn());
|
||||
}
|
||||
|
|
@ -369,7 +369,7 @@ void FileLine::warnUnusedOff(bool flag) {
|
|||
warnOff(V3ErrorCode::UNUSEDSIGNAL, flag);
|
||||
}
|
||||
|
||||
bool FileLine::warnIsOff(V3ErrorCode code) const VL_MT_SAFE {
|
||||
bool FileLine::warnIsOff(V3ErrorCode code) const {
|
||||
if (!msgEn().test(code)) return true;
|
||||
if (!defaultFileLine().msgEn().test(code)) return true; // Global overrides local
|
||||
if ((code.lintError() || code.styleError()) && !msgEn().test(V3ErrorCode::I_LINT)) {
|
||||
|
|
@ -380,7 +380,8 @@ bool FileLine::warnIsOff(V3ErrorCode code) const VL_MT_SAFE {
|
|||
}
|
||||
|
||||
// cppverilator-suppress constParameter
|
||||
void FileLine::v3errorEnd(std::ostringstream& sstr, const string& extra) {
|
||||
void FileLine::v3errorEnd(std::ostringstream& sstr, const string& extra)
|
||||
VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
std::ostringstream nsstr;
|
||||
if (lastLineno()) nsstr << this;
|
||||
nsstr << sstr.str();
|
||||
|
|
@ -390,29 +391,33 @@ void FileLine::v3errorEnd(std::ostringstream& sstr, const string& extra) {
|
|||
lstr << std::setw(ascii().length()) << " "
|
||||
<< ": " << extra;
|
||||
}
|
||||
m_waive = V3Config::waive(this, V3Error::errorCode(), sstr.str());
|
||||
if (warnIsOff(V3Error::errorCode()) || m_waive) {
|
||||
V3Error::suppressThisWarning();
|
||||
} else if (!V3Error::errorContexted()) {
|
||||
m_waive = V3Config::waive(this, V3Error::s().errorCode(), sstr.str());
|
||||
if (warnIsOff(V3Error::s().errorCode()) || m_waive) {
|
||||
V3Error::s().suppressThisWarning();
|
||||
} else if (!V3Error::s().errorContexted()) {
|
||||
nsstr << warnContextPrimary();
|
||||
}
|
||||
if (!m_waive) V3Waiver::addEntry(V3Error::errorCode(), filename(), sstr.str());
|
||||
V3Error::v3errorEnd(nsstr, lstr.str());
|
||||
if (!m_waive) V3Waiver::addEntry(V3Error::s().errorCode(), filename(), sstr.str());
|
||||
V3Error::s().v3errorEnd(nsstr, lstr.str());
|
||||
}
|
||||
|
||||
string FileLine::warnMore() const {
|
||||
string FileLine::warnMore() const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
if (lastLineno()) {
|
||||
return V3Error::warnMore() + string(ascii().size(), ' ') + ": ";
|
||||
return V3Error::s().warnMore() + string(ascii().size(), ' ') + ": ";
|
||||
} else {
|
||||
return V3Error::warnMore();
|
||||
return V3Error::s().warnMore();
|
||||
}
|
||||
}
|
||||
string FileLine::warnOther() const VL_MT_SAFE {
|
||||
string FileLine::warnOther() const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
if (lastLineno()) {
|
||||
return V3Error::warnMore() + ascii() + ": ";
|
||||
return V3Error::s().warnMore() + ascii() + ": ";
|
||||
} else {
|
||||
return V3Error::warnMore();
|
||||
return V3Error::s().warnMore();
|
||||
}
|
||||
};
|
||||
string FileLine::warnOtherStandalone() const VL_EXCLUDES(V3Error::s().m_mutex) VL_MT_UNSAFE {
|
||||
const VerilatedLockGuard guard{V3Error::s().m_mutex};
|
||||
return warnOther();
|
||||
}
|
||||
|
||||
string FileLine::source() const VL_MT_SAFE {
|
||||
|
|
@ -435,8 +440,7 @@ string FileLine::prettySource() const VL_MT_SAFE {
|
|||
return VString::spaceUnprintable(out);
|
||||
}
|
||||
|
||||
string FileLine::warnContext(bool secondary) const VL_MT_SAFE {
|
||||
V3Error::errorContexted(true);
|
||||
string FileLine::warnContext() const {
|
||||
if (!v3Global.opt.context()) return "";
|
||||
string out;
|
||||
if (firstLineno() == lastLineno() && firstColumn()) {
|
||||
|
|
@ -457,16 +461,18 @@ string FileLine::warnContext(bool secondary) const VL_MT_SAFE {
|
|||
out += "\n";
|
||||
}
|
||||
}
|
||||
if (!secondary) { // Avoid printing long paths on informational part of error
|
||||
for (FileLine* parentFl = parent(); parentFl; parentFl = parentFl->parent()) {
|
||||
if (parentFl->filenameIsGlobal()) break;
|
||||
out += parentFl->warnOther() + "... note: In file included from "
|
||||
+ parentFl->filebasename() + "\n";
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
string FileLine::warnContextParent() const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
string out;
|
||||
for (FileLine* parentFl = parent(); parentFl; parentFl = parentFl->parent()) {
|
||||
if (parentFl->filenameIsGlobal()) break;
|
||||
out += parentFl->warnOther() + "... note: In file included from "
|
||||
+ parentFl->filebasename() + "\n";
|
||||
}
|
||||
return out;
|
||||
}
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
std::unordered_set<FileLine*> fileLineLeakChecks;
|
||||
|
||||
|
|
|
|||
|
|
@ -261,6 +261,11 @@ public:
|
|||
|
||||
// Turn on/off warning messages on this line.
|
||||
void warnOn(V3ErrorCode code, bool flag) {
|
||||
if (code == V3ErrorCode::WIDTH) {
|
||||
warnOn(V3ErrorCode::WIDTHTRUNC, flag);
|
||||
warnOn(V3ErrorCode::WIDTHEXPAND, flag);
|
||||
warnOn(V3ErrorCode::WIDTHXZEXPAND, flag);
|
||||
}
|
||||
m_msgEnIdx = singleton().msgEnSetBit(m_msgEnIdx, code, flag);
|
||||
}
|
||||
void warnOff(V3ErrorCode code, bool flag) { warnOn(code, !flag); }
|
||||
|
|
@ -309,24 +314,30 @@ public:
|
|||
void modifyWarnOff(V3ErrorCode code, bool flag) { warnOff(code, flag); }
|
||||
|
||||
// OPERATORS
|
||||
void v3errorEnd(std::ostringstream& str, const string& extra = "");
|
||||
void v3errorEndFatal(std::ostringstream& str) VL_ATTR_NORETURN {
|
||||
void v3errorEnd(std::ostringstream& str, const string& extra = "")
|
||||
VL_REQUIRES(V3Error::s().m_mutex);
|
||||
void v3errorEndFatal(std::ostringstream& str) VL_ATTR_NORETURN
|
||||
VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
v3errorEnd(str);
|
||||
assert(0); // LCOV_EXCL_LINE
|
||||
VL_UNREACHABLE;
|
||||
}
|
||||
/// When building an error, prefix for printing continuation lines
|
||||
/// e.g. information referring to the same FileLine as before
|
||||
string warnMore() const;
|
||||
string warnMore() const VL_REQUIRES(V3Error::s().m_mutex);
|
||||
/// When building an error, prefix for printing secondary information
|
||||
/// from a different FileLine than the original error
|
||||
string warnOther() const VL_MT_SAFE;
|
||||
string warnOther() const VL_REQUIRES(V3Error::s().m_mutex);
|
||||
string warnOtherStandalone() const VL_EXCLUDES(V3Error::s().m_mutex) VL_MT_UNSAFE;
|
||||
/// When building an error, current location in include etc
|
||||
/// If not used in a given error, automatically pasted at end of error
|
||||
string warnContextPrimary() const VL_MT_SAFE { return warnContext(false); }
|
||||
string warnContextPrimary() const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
V3Error::s().errorContexted(true);
|
||||
return warnContext() + warnContextParent();
|
||||
}
|
||||
/// When building an error, additional location for additional references
|
||||
/// Simplified information vs warnContextPrimary() to make dump clearer
|
||||
string warnContextSecondary() const VL_MT_SAFE { return warnContext(true); }
|
||||
string warnContextSecondary() const { return warnContext(); }
|
||||
bool operator==(const FileLine& rhs) const {
|
||||
return (m_firstLineno == rhs.m_firstLineno && m_firstColumn == rhs.m_firstColumn
|
||||
&& m_lastLineno == rhs.m_lastLineno && m_lastColumn == rhs.m_lastColumn
|
||||
|
|
@ -349,7 +360,8 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
string warnContext(bool secondary) const VL_MT_SAFE;
|
||||
string warnContext() const;
|
||||
string warnContextParent() const VL_REQUIRES(V3Error::s().m_mutex);
|
||||
const MsgEnBitSet& msgEn() const VL_MT_SAFE { return singleton().msgEn(m_msgEnIdx); }
|
||||
};
|
||||
std::ostream& operator<<(std::ostream& os, FileLine* fileline);
|
||||
|
|
|
|||
|
|
@ -55,21 +55,17 @@ class ForceConvertVisitor final : public VNVisitor {
|
|||
// TYPES
|
||||
struct ForceComponentsVar {
|
||||
AstVar* const m_rdVarp; // New variable to replace read references with
|
||||
AstVar* const m_enVarp; // Force enabled signal
|
||||
AstVar* const m_valVarp; // Forced value
|
||||
AstVar* const m_phVarp; // Placeholder variable for release (never read)
|
||||
AstVar* const m_enVarp; // Force enabled signal
|
||||
explicit ForceComponentsVar(AstVar* varp)
|
||||
: m_rdVarp{new AstVar{varp->fileline(), VVarType::WIRE, varp->name() + "__VforceRd",
|
||||
varp->dtypep()}}
|
||||
, m_enVarp{new AstVar{varp->fileline(), VVarType::VAR, varp->name() + "__VforceEn",
|
||||
varp->dtypep()}}
|
||||
, m_valVarp{new AstVar{varp->fileline(), VVarType::VAR, varp->name() + "__VforceVal",
|
||||
varp->dtypep()}}
|
||||
, m_phVarp{new AstVar{varp->fileline(), VVarType::VAR, varp->name() + "__VforcePh",
|
||||
varp->dtypep()}} {
|
||||
, m_enVarp{new AstVar{varp->fileline(), VVarType::VAR, varp->name() + "__VforceEn",
|
||||
(isRangedDType(varp) ? varp->dtypep() : varp->findBitDType())}} {
|
||||
m_rdVarp->addNext(m_enVarp);
|
||||
m_rdVarp->addNext(m_valVarp);
|
||||
m_rdVarp->addNext(m_phVarp);
|
||||
varp->addNextHere(m_rdVarp);
|
||||
|
||||
if (varp->isPrimaryIO()) {
|
||||
|
|
@ -87,15 +83,12 @@ class ForceConvertVisitor final : public VNVisitor {
|
|||
AstVarScope* const m_rdVscp; // New variable to replace read references with
|
||||
AstVarScope* const m_enVscp; // Force enabled signal
|
||||
AstVarScope* const m_valVscp; // Forced value
|
||||
AstVarScope* const m_phVscp; // Placeholder variable for release (never read)
|
||||
explicit ForceComponentsVarScope(AstVarScope* vscp, ForceComponentsVar& fcv)
|
||||
: m_rdVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_rdVarp}}
|
||||
, m_enVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_enVarp}}
|
||||
, m_valVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_valVarp}}
|
||||
, m_phVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_phVarp}} {
|
||||
, m_valVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_valVarp}} {
|
||||
m_rdVscp->addNext(m_enVscp);
|
||||
m_rdVscp->addNext(m_valVscp);
|
||||
m_rdVscp->addNext(m_phVscp);
|
||||
vscp->addNextHere(m_rdVscp);
|
||||
|
||||
FileLine* const flp = vscp->fileline();
|
||||
|
|
@ -118,12 +111,20 @@ class ForceConvertVisitor final : public VNVisitor {
|
|||
AstVarRef* const lhsp = new AstVarRef{flp, m_rdVscp, VAccess::WRITE};
|
||||
AstVarRef* const origp = new AstVarRef{flp, vscp, VAccess::READ};
|
||||
origp->user2(1); // Don't replace this read ref with the read signal
|
||||
AstOr* const rhsp = new AstOr{
|
||||
flp,
|
||||
new AstAnd{flp, new AstVarRef{flp, m_enVscp, VAccess::READ},
|
||||
new AstVarRef{flp, m_valVscp, VAccess::READ}},
|
||||
new AstAnd{flp, new AstNot{flp, new AstVarRef{flp, m_enVscp, VAccess::READ}},
|
||||
origp}};
|
||||
AstNodeExpr* rhsp;
|
||||
if (isRangedDType(vscp)) {
|
||||
rhsp = new AstOr{
|
||||
flp,
|
||||
new AstAnd{flp, new AstVarRef{flp, m_enVscp, VAccess::READ},
|
||||
new AstVarRef{flp, m_valVscp, VAccess::READ}},
|
||||
new AstAnd{flp,
|
||||
new AstNot{flp, new AstVarRef{flp, m_enVscp, VAccess::READ}},
|
||||
origp}};
|
||||
} else {
|
||||
rhsp = new AstCond{flp, new AstVarRef{flp, m_enVscp, VAccess::READ},
|
||||
new AstVarRef{flp, m_valVscp, VAccess::READ}, origp};
|
||||
}
|
||||
|
||||
AstActive* const activep
|
||||
= new AstActive{flp, "force-comb",
|
||||
new AstSenTree{flp, new AstSenItem{flp, AstSenItem::Combo{}}}};
|
||||
|
|
@ -144,6 +145,12 @@ class ForceConvertVisitor final : public VNVisitor {
|
|||
AstUser1Allocator<AstVarScope, ForceComponentsVarScope> m_forceComponentsVarScope;
|
||||
|
||||
// METHODS
|
||||
static bool isRangedDType(AstNode* nodep) {
|
||||
// If ranged we need a multibit enable to support bit-by-bit part-select forces,
|
||||
// otherwise forcing a real or other opaque dtype and need a single bit enable.
|
||||
const AstBasicDType* const basicp = nodep->dtypep()->skipRefp()->basicp();
|
||||
return basicp && basicp->isRanged();
|
||||
}
|
||||
const ForceComponentsVarScope& getForceComponents(AstVarScope* vscp) {
|
||||
AstVar* const varp = vscp->varp();
|
||||
return m_forceComponentsVarScope(vscp, vscp, m_forceComponentsVar(varp, varp));
|
||||
|
|
@ -178,7 +185,7 @@ class ForceConvertVisitor final : public VNVisitor {
|
|||
AstNodeExpr* const rhsp = nodep->rhsp(); // The value we are forcing it to
|
||||
|
||||
// Set corresponding enable signals to ones
|
||||
V3Number ones{lhsp, lhsp->width()};
|
||||
V3Number ones{lhsp, isRangedDType(lhsp) ? lhsp->width() : 1};
|
||||
ones.setAllBits1();
|
||||
AstAssign* const setEnp
|
||||
= new AstAssign{flp, lhsp->cloneTree(false), new AstConst{rhsp->fileline(), ones}};
|
||||
|
|
@ -213,7 +220,7 @@ class ForceConvertVisitor final : public VNVisitor {
|
|||
AstNodeExpr* const lhsp = nodep->lhsp(); // The LValue we are releasing
|
||||
|
||||
// Set corresponding enable signals to zero
|
||||
V3Number zero{lhsp, lhsp->width()};
|
||||
V3Number zero{lhsp, isRangedDType(lhsp) ? lhsp->width() : 1};
|
||||
zero.setAllBits0();
|
||||
AstAssign* const resetEnp
|
||||
= new AstAssign{flp, lhsp->cloneTree(false), new AstConst{lhsp->fileline(), zero}};
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <cstddef>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,14 @@ void V3Global::readFiles() {
|
|||
V3ParseSym parseSyms{v3Global.rootp()}; // Symbol table must be common across all parsing
|
||||
|
||||
V3Parse parser(v3Global.rootp(), &filter, &parseSyms);
|
||||
|
||||
// Parse the std package
|
||||
if (v3Global.opt.std()) {
|
||||
parser.parseFile(new FileLine{V3Options::getStdPackagePath()},
|
||||
V3Options::getStdPackagePath(), false,
|
||||
"Cannot find verilated_std.sv containing built-in std:: definitions:");
|
||||
}
|
||||
|
||||
// Read top module
|
||||
const V3StringList& vFiles = v3Global.opt.vFiles();
|
||||
for (const string& filename : vFiles) {
|
||||
|
|
@ -60,13 +68,6 @@ void V3Global::readFiles() {
|
|||
"Cannot find file containing module: ");
|
||||
}
|
||||
|
||||
if (usesStdPackage()) {
|
||||
// Parse the std package
|
||||
parser.parseFile(new FileLine{FileLine::commandLineFilename()},
|
||||
V3Options::getStdPackagePath(), false,
|
||||
"Cannot find verilated_std.sv containing built-in std:: definitions:");
|
||||
}
|
||||
|
||||
// Read libraries
|
||||
// To be compatible with other simulators,
|
||||
// this needs to be done after the top file is read
|
||||
|
|
@ -75,6 +76,15 @@ void V3Global::readFiles() {
|
|||
parser.parseFile(new FileLine{FileLine::commandLineFilename()}, filename, true,
|
||||
"Cannot find file containing library module: ");
|
||||
}
|
||||
|
||||
// Delete the std package if unused
|
||||
if (!usesStdPackage()) {
|
||||
if (AstNodeModule* stdp = v3Global.rootp()->stdPackagep()) {
|
||||
v3Global.rootp()->stdPackagep(nullptr);
|
||||
VL_DO_DANGLING(stdp->unlinkFrBack()->deleteTree(), stdp);
|
||||
}
|
||||
}
|
||||
|
||||
// v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("parse.tree"));
|
||||
V3Error::abortIfErrors();
|
||||
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ V3GraphEdge* V3GraphVertex::findConnectingEdgep(GraphWay way, const V3GraphVerte
|
|||
}
|
||||
|
||||
// cppcheck-has-bug-suppress constParameter
|
||||
void V3GraphVertex::v3errorEnd(std::ostringstream& str) const {
|
||||
void V3GraphVertex::v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
std::ostringstream nsstr;
|
||||
nsstr << str.str();
|
||||
if (debug()) {
|
||||
|
|
@ -139,10 +139,11 @@ void V3GraphVertex::v3errorEnd(std::ostringstream& str) const {
|
|||
if (FileLine* const flp = fileline()) {
|
||||
flp->v3errorEnd(nsstr);
|
||||
} else {
|
||||
V3Error::v3errorEnd(nsstr);
|
||||
V3Error::s().v3errorEnd(nsstr);
|
||||
}
|
||||
}
|
||||
void V3GraphVertex::v3errorEndFatal(std::ostringstream& str) const {
|
||||
void V3GraphVertex::v3errorEndFatal(std::ostringstream& str) const
|
||||
VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
v3errorEnd(str);
|
||||
assert(0); // LCOV_EXCL_LINE
|
||||
VL_UNREACHABLE;
|
||||
|
|
|
|||
|
|
@ -248,8 +248,8 @@ public:
|
|||
V3GraphEdge* beginp(GraphWay way) const { return way.forward() ? outBeginp() : inBeginp(); }
|
||||
// METHODS
|
||||
/// Error reporting
|
||||
void v3errorEnd(std::ostringstream& str) const;
|
||||
void v3errorEndFatal(std::ostringstream& str) const;
|
||||
void v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex);
|
||||
void v3errorEndFatal(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex);
|
||||
/// Edges are routed around this vertex to point from "from" directly to "to"
|
||||
void rerouteEdges(V3Graph* graphp);
|
||||
/// Find the edge connecting ap and bp, where bp is wayward from ap.
|
||||
|
|
|
|||
|
|
@ -343,7 +343,7 @@ private:
|
|||
// Arrayed instants: one bit for each of the instants (each
|
||||
// assign is 1 modwidth wide)
|
||||
if (m_cellRangep->littleEndian()) {
|
||||
nodep->exprp()->v3warn(LITENDIAN, "Little endian instance range connecting to "
|
||||
nodep->exprp()->v3warn(LITENDIAN, "Big endian instance range connecting to "
|
||||
"vector: left < right of instance range: ["
|
||||
<< m_cellRangep->leftConst() << ":"
|
||||
<< m_cellRangep->rightConst() << "]");
|
||||
|
|
|
|||
|
|
@ -352,8 +352,10 @@ private:
|
|||
}
|
||||
// Convert .* to list of pins
|
||||
bool pinStar = false;
|
||||
bool pinDotName = false;
|
||||
for (AstPin *nextp, *pinp = nodep->pinsp(); pinp; pinp = nextp) {
|
||||
nextp = VN_AS(pinp->nextp(), Pin);
|
||||
if (pinp->svDotName()) pinDotName = true;
|
||||
if (pinp->dotStar()) {
|
||||
if (pinStar) pinp->v3error("Duplicate .* in an instance (IEEE 1800-2017 23.3.2)");
|
||||
pinStar = true;
|
||||
|
|
@ -374,8 +376,8 @@ private:
|
|||
// Note what pins exist
|
||||
std::unordered_set<string> ports; // Symbol table of all connected port names
|
||||
for (AstPin* pinp = nodep->pinsp(); pinp; pinp = VN_AS(pinp->nextp(), Pin)) {
|
||||
if (pinStar && pinp->name().substr(0, 11) == "__pinNumber") {
|
||||
pinp->v3error("Connect by position is illegal in .* connected instances"
|
||||
if ((pinStar || pinDotName) && pinp->name().substr(0, 11) == "__pinNumber") {
|
||||
pinp->v3error("Mixing positional and .*/named instantiation connection"
|
||||
" (IEEE 1800-2017 23.3.2)");
|
||||
}
|
||||
if (!pinp->exprp()) {
|
||||
|
|
@ -404,6 +406,7 @@ private:
|
|||
nodep->fileline(), 0, portp->name(),
|
||||
new AstParseRef{nodep->fileline(), VParseRefExp::PX_TEXT,
|
||||
portp->name(), nullptr, nullptr}};
|
||||
newp->svDotName(true);
|
||||
newp->svImplicit(true);
|
||||
nodep->addPinsp(newp);
|
||||
} else { // warn on the CELL that needs it, not the port
|
||||
|
|
|
|||
|
|
@ -273,14 +273,16 @@ public:
|
|||
UINFO(4, "Var2 " << fnodep << endl);
|
||||
if (nodep->type() == fnodep->type()) {
|
||||
nodep->v3error("Duplicate declaration of "
|
||||
<< nodeTextType(fnodep) << ": " << nodep->prettyNameQ() << '\n'
|
||||
<< nodeTextType(fnodep) << ": " << AstNode::prettyNameQ(name)
|
||||
<< '\n'
|
||||
<< nodep->warnContextPrimary() << '\n'
|
||||
<< fnodep->warnOther() << "... Location of original declaration\n"
|
||||
<< fnodep->warnContextSecondary());
|
||||
} else {
|
||||
nodep->v3error("Unsupported in C: "
|
||||
<< ucfirst(nodeTextType(nodep)) << " has the same name as "
|
||||
<< nodeTextType(fnodep) << ": " << nodep->prettyNameQ() << '\n'
|
||||
<< nodeTextType(fnodep) << ": " << AstNode::prettyNameQ(name)
|
||||
<< '\n'
|
||||
<< nodep->warnContextPrimary() << '\n'
|
||||
<< fnodep->warnOther() << "... Location of original declaration\n"
|
||||
<< fnodep->warnContextSecondary());
|
||||
|
|
@ -1066,6 +1068,12 @@ class LinkDotFindVisitor final : public VNVisitor {
|
|||
VL_RESTORER(m_curSymp);
|
||||
VSymEnt* upSymp = m_curSymp;
|
||||
{
|
||||
if (VN_IS(m_curSymp->nodep(), Class)
|
||||
&& VN_AS(m_curSymp->nodep(), Class)->isInterfaceClass() && !nodep->pureVirtual()
|
||||
&& !nodep->isConstructor()) {
|
||||
nodep->v3error("Interface class functions must be pure virtual"
|
||||
<< " (IEEE 1800-2017 8.26): " << nodep->prettyNameQ());
|
||||
}
|
||||
// Change to appropriate package if extern declaration (vs definition)
|
||||
if (nodep->classOrPackagep()) {
|
||||
AstClassOrPackageRef* const cpackagerefp
|
||||
|
|
@ -1135,11 +1143,23 @@ class LinkDotFindVisitor final : public VNVisitor {
|
|||
iterate(nodep->sensesp());
|
||||
iterateAndNextNull(nodep->itemsp());
|
||||
// If the block has no name, one cannot reference the clockvars
|
||||
if (nodep->name().empty()) return;
|
||||
VL_RESTORER(m_curSymp);
|
||||
m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_classOrPackagep);
|
||||
m_curSymp->fallbackp(nullptr);
|
||||
iterateAndNextNull(nodep->itemsp());
|
||||
VSymEnt* itSymp = nullptr;
|
||||
if (nodep->isGlobal() //
|
||||
&& m_statep->forPrimary()) { // else flattening may see two globals
|
||||
m_statep->checkDuplicate(m_curSymp, nodep, "__024global_clock");
|
||||
itSymp
|
||||
= m_statep->insertBlock(m_curSymp, "__024global_clock", nodep, m_classOrPackagep);
|
||||
itSymp->fallbackp(nullptr);
|
||||
}
|
||||
if (!nodep->name().empty()) {
|
||||
itSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_classOrPackagep);
|
||||
itSymp->fallbackp(nullptr);
|
||||
}
|
||||
if (itSymp) {
|
||||
VL_RESTORER(m_curSymp);
|
||||
m_curSymp = itSymp;
|
||||
iterateAndNextNull(nodep->itemsp());
|
||||
}
|
||||
}
|
||||
void visit(AstClockingItem* nodep) override {
|
||||
if (nodep->varp()) {
|
||||
|
|
@ -1176,8 +1196,10 @@ class LinkDotFindVisitor final : public VNVisitor {
|
|||
// Var: Remember its name for later resolution
|
||||
UASSERT_OBJ(m_curSymp && m_modSymp, nodep, "Var not under module?");
|
||||
iterateChildren(nodep);
|
||||
if (nodep->isFuncLocal() && nodep->lifetime().isStatic()) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: 'static' function/task variables");
|
||||
if (VN_IS(m_curSymp->nodep(), Class)
|
||||
&& VN_AS(m_curSymp->nodep(), Class)->isInterfaceClass() && !nodep->isParam()) {
|
||||
nodep->v3error("Interface class cannot contain non-parameter members"
|
||||
<< " (IEEE 1800-2017 8.26): " << nodep->prettyNameQ());
|
||||
}
|
||||
if (!m_statep->forScopeCreation()) {
|
||||
// Find under either a task or the module's vars
|
||||
|
|
@ -1373,8 +1395,11 @@ class LinkDotFindVisitor final : public VNVisitor {
|
|||
UINFO(4, " Link: " << nodep << endl);
|
||||
VSymEnt* const srcp = m_statep->getNodeSym(nodep->packagep());
|
||||
if (nodep->name() == "*") {
|
||||
if (m_curSymp == m_statep->dunitEntp()) {
|
||||
nodep->v3warn(IMPORTSTAR, "Import::* in $unit scope may pollute global namespace");
|
||||
if (nodep->packagep() != v3Global.rootp()->stdPackagep()) {
|
||||
if (m_curSymp == m_statep->dunitEntp()) {
|
||||
nodep->v3warn(IMPORTSTAR,
|
||||
"Import::* in $unit scope may pollute global namespace");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
VSymEnt* const impp = srcp->findIdFlat(nodep->name());
|
||||
|
|
@ -1686,7 +1711,8 @@ private:
|
|||
}
|
||||
void visit(AstTypedefFwd* nodep) override {
|
||||
VSymEnt* const foundp = m_statep->getNodeSym(nodep)->findIdFallback(nodep->name());
|
||||
if (!foundp && v3Global.opt.pedantic()) {
|
||||
if (!foundp && v3Global.opt.pedantic()
|
||||
&& nodep->name() != "process") { // Process is dangling as isn't implemented yet
|
||||
// We only check it was ever really defined in pedantic mode, as it
|
||||
// might have been in a header file referring to a module we never
|
||||
// needed so there are false positives
|
||||
|
|
@ -1974,10 +2000,13 @@ private:
|
|||
// Cleared on global
|
||||
// *::user1p() -> See LinkDotState
|
||||
// *::user2p() -> See LinkDotState
|
||||
// *::user3() // bool. Processed
|
||||
// *::user3() // bool. Processed
|
||||
// *::user4() -> See LinkDotState
|
||||
// Cleared on Cell
|
||||
// AstVar::user5() // bool. True if pin used in this cell
|
||||
// AstVar::user5() // bool. True if pin used in this cell
|
||||
// AstClass::user5() // bool. True if class has a parameter
|
||||
// as a (possibly indirect) base class.
|
||||
// Used only in LDS_PRIMARY pass
|
||||
const VNUser3InUse m_inuser3;
|
||||
const VNUser5InUse m_inuser5;
|
||||
|
||||
|
|
@ -2000,12 +2029,14 @@ private:
|
|||
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
||||
int m_modportNum = 0; // Uniqueify modport numbers
|
||||
bool m_inSens = false; // True if in senitem
|
||||
std::set<std::string> m_ifClassImpNames; // Names imported from interface class
|
||||
|
||||
struct DotStates {
|
||||
DotPosition m_dotPos; // Scope part of dotted resolution
|
||||
VSymEnt* m_dotSymp; // SymEnt for dotted AstParse lookup
|
||||
const AstDot* m_dotp; // Current dot
|
||||
bool m_unresolved; // Unresolved, needs help from V3Param
|
||||
bool m_unresolvedCell; // Unresolved cell, needs help from V3Param
|
||||
bool m_unresolvedClass; // Unresolved class reference, needs help from V3Param
|
||||
AstNode* m_unlinkedScopep; // Unresolved scope, needs corresponding VarXRef
|
||||
bool m_dotErr; // Error found in dotted resolution, ignore upwards
|
||||
string m_dotText; // String of dotted names found in below parseref
|
||||
|
|
@ -2017,7 +2048,8 @@ private:
|
|||
m_dotp = nullptr;
|
||||
m_dotErr = false;
|
||||
m_dotText = "";
|
||||
m_unresolved = false;
|
||||
m_unresolvedCell = false;
|
||||
m_unresolvedClass = false;
|
||||
m_unlinkedScopep = nullptr;
|
||||
}
|
||||
string ascii() const {
|
||||
|
|
@ -2026,7 +2058,8 @@ private:
|
|||
sstr << "ds=" << names[m_dotPos];
|
||||
sstr << " dse" << cvtToHex(m_dotSymp);
|
||||
sstr << " txt=" << m_dotText;
|
||||
sstr << " unr=" << m_unresolved;
|
||||
sstr << " unrCell=" << m_unresolvedCell;
|
||||
sstr << " unrClass=" << m_unresolvedClass;
|
||||
return sstr.str();
|
||||
}
|
||||
} m_ds; // State to preserve across recursions
|
||||
|
|
@ -2153,6 +2186,47 @@ private:
|
|||
} while (classSymp && !VN_IS(classSymp->nodep(), Class));
|
||||
return classSymp;
|
||||
}
|
||||
void importImplementsClass(AstClass* implementsClassp, VSymEnt* interfaceSymp,
|
||||
AstClass* interfaceClassp) {
|
||||
UINFO(8, "importImplementsClass to " << implementsClassp << " from " << interfaceClassp
|
||||
<< endl);
|
||||
for (VSymEnt::const_iterator it = interfaceSymp->begin(); it != interfaceSymp->end();
|
||||
++it) {
|
||||
if (AstNode* interfaceSubp = it->second->nodep()) {
|
||||
UINFO(8, " SymFunc " << interfaceSubp << endl);
|
||||
if (VN_IS(interfaceSubp, NodeFTask)) {
|
||||
const VSymEnt* const foundp = m_curSymp->findIdFlat(interfaceSubp->name());
|
||||
bool existsInChild = foundp && !foundp->imported();
|
||||
if (!existsInChild && !implementsClassp->isInterfaceClass()) {
|
||||
implementsClassp->v3error(
|
||||
"Class " << implementsClassp->prettyNameQ() << " implements "
|
||||
<< interfaceClassp->prettyNameQ()
|
||||
<< " but is missing implementation for "
|
||||
<< interfaceSubp->prettyNameQ() << " (IEEE 1800-2017 8.26)\n"
|
||||
<< implementsClassp->warnContextPrimary() << '\n'
|
||||
<< interfaceSubp->warnOther()
|
||||
<< "... Location of interface class's function\n"
|
||||
<< interfaceSubp->warnContextSecondary());
|
||||
}
|
||||
if (!existsInChild
|
||||
&& m_ifClassImpNames.find(interfaceSubp->name())
|
||||
!= m_ifClassImpNames.end()) {
|
||||
implementsClassp->v3error(
|
||||
"Class " << implementsClassp->prettyNameQ() << " implements "
|
||||
<< interfaceClassp->prettyNameQ()
|
||||
<< " but missing inheritance conflict resolution for "
|
||||
<< interfaceSubp->prettyNameQ()
|
||||
<< " (IEEE 1800-2017 8.26.6.2)\n"
|
||||
<< implementsClassp->warnContextPrimary() << '\n'
|
||||
<< interfaceSubp->warnOther()
|
||||
<< "... Location of interface class's function\n"
|
||||
<< interfaceSubp->warnContextSecondary());
|
||||
}
|
||||
m_ifClassImpNames.emplace(interfaceSubp->name());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// VISITs
|
||||
void visit(AstNetlist* nodep) override {
|
||||
|
|
@ -2340,14 +2414,12 @@ private:
|
|||
}
|
||||
if (m_statep->forPrimary() && isParamedClassRef(nodep->lhsp())) {
|
||||
// Dots of paramed classes will be linked after deparameterization
|
||||
m_ds.m_dotPos = DP_NONE;
|
||||
return;
|
||||
m_ds.m_unresolvedClass = true;
|
||||
}
|
||||
if (m_ds.m_unresolved
|
||||
if (m_ds.m_unresolvedCell
|
||||
&& (VN_IS(nodep->lhsp(), CellRef) || VN_IS(nodep->lhsp(), CellArrayRef))) {
|
||||
m_ds.m_unlinkedScopep = nodep->lhsp();
|
||||
}
|
||||
if (VN_IS(nodep->lhsp(), LambdaArgRef)) m_ds.m_unlinkedScopep = nodep->lhsp();
|
||||
if (!m_ds.m_dotErr) { // Once something wrong, give up
|
||||
// Top 'final' dot RHS is final RHS, else it's a
|
||||
// DOT(DOT(x,*here*),real-rhs) which we consider a RHS
|
||||
|
|
@ -2355,32 +2427,35 @@ private:
|
|||
iterateAndNextNull(nodep->rhsp());
|
||||
// if (debug() >= 9) nodep->dumpTree("- dot-rho: ");
|
||||
}
|
||||
if (start) {
|
||||
AstNode* newp;
|
||||
if (m_ds.m_dotErr) {
|
||||
newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};
|
||||
} else {
|
||||
// RHS is what we're left with
|
||||
newp = nodep->rhsp()->unlinkFrBack();
|
||||
if (!m_ds.m_unresolvedClass) {
|
||||
if (start) {
|
||||
AstNode* newp;
|
||||
if (m_ds.m_dotErr) {
|
||||
newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};
|
||||
} else {
|
||||
newp = nodep->rhsp()->unlinkFrBack();
|
||||
}
|
||||
if (debug() >= 9) newp->dumpTree("- dot-out: ");
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
} else { // Dot midpoint
|
||||
AstNodeExpr* newp = nodep->rhsp()->unlinkFrBack();
|
||||
if (m_ds.m_unresolvedCell) {
|
||||
AstCellRef* const crp = new AstCellRef{
|
||||
nodep->fileline(), nodep->name(), nodep->lhsp()->unlinkFrBack(), newp};
|
||||
newp = crp;
|
||||
}
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
if (debug() >= 9) newp->dumpTree("- dot-out: ");
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
} else { // Dot midpoint
|
||||
AstNodeExpr* newp = nodep->rhsp()->unlinkFrBack();
|
||||
if (m_ds.m_unresolved) {
|
||||
AstCellRef* const crp = new AstCellRef{nodep->fileline(), nodep->name(),
|
||||
nodep->lhsp()->unlinkFrBack(), newp};
|
||||
newp = crp;
|
||||
}
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
}
|
||||
if (start) {
|
||||
m_ds = lastStates;
|
||||
} else {
|
||||
const bool unresolvedClass = m_ds.m_unresolvedClass;
|
||||
m_ds.m_dotp = lastStates.m_dotp;
|
||||
m_ds.m_unresolvedClass |= unresolvedClass;
|
||||
}
|
||||
}
|
||||
void visit(AstSenItem* nodep) override {
|
||||
|
|
@ -2391,6 +2466,7 @@ private:
|
|||
void visit(AstParseRef* nodep) override {
|
||||
if (nodep->user3SetOnce()) return;
|
||||
UINFO(9, " linkPARSEREF " << m_ds.ascii() << " n=" << nodep << endl);
|
||||
if (m_ds.m_unresolvedClass) return;
|
||||
// m_curSymp is symbol table of outer expression
|
||||
// m_ds.m_dotSymp is symbol table relative to "."'s above now
|
||||
UASSERT_OBJ(m_ds.m_dotSymp, nodep, "nullptr lookup symbol table");
|
||||
|
|
@ -2431,7 +2507,7 @@ private:
|
|||
// If not, treat it as normal member select
|
||||
iterateChildren(nodep);
|
||||
const auto newp = new AstLambdaArgRef{
|
||||
nodep->fileline(), m_ds.m_unlinkedScopep->name() + "__DOT__index", true};
|
||||
nodep->fileline(), m_ds.m_dotp->lhsp()->name() + "__DOT__index", true};
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
|
|
@ -2566,7 +2642,7 @@ private:
|
|||
varp->attrSplitVar(false);
|
||||
}
|
||||
m_ds.m_dotText = "";
|
||||
if (m_ds.m_unresolved && m_ds.m_unlinkedScopep) {
|
||||
if (m_ds.m_unresolvedCell && m_ds.m_unlinkedScopep) {
|
||||
const string dotted = refp->dotted();
|
||||
const size_t pos = dotted.find("__BRA__??__KET__");
|
||||
// Arrays of interfaces all have the same parameters
|
||||
|
|
@ -2578,7 +2654,7 @@ private:
|
|||
newp = new AstUnlinkedRef{nodep->fileline(), refp, refp->name(),
|
||||
m_ds.m_unlinkedScopep->unlinkFrBack()};
|
||||
m_ds.m_unlinkedScopep = nullptr;
|
||||
m_ds.m_unresolved = false;
|
||||
m_ds.m_unresolvedCell = false;
|
||||
}
|
||||
} else {
|
||||
newp = refp;
|
||||
|
|
@ -2668,12 +2744,18 @@ private:
|
|||
} else if (VN_IS(foundp->nodep(), Clocking)) {
|
||||
m_ds.m_dotSymp = foundp;
|
||||
ok = m_ds.m_dotPos == DP_SCOPE;
|
||||
} else if (VN_IS(foundp->nodep(), Property)) {
|
||||
AstFuncRef* const propRefp
|
||||
= new AstFuncRef{nodep->fileline(), nodep->name(), nullptr};
|
||||
nodep->replaceWith(propRefp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
ok = m_ds.m_dotPos == DP_NONE;
|
||||
} else if (const AstNodeFTask* const ftaskp = VN_CAST(foundp->nodep(), NodeFTask)) {
|
||||
if (!ftaskp->isFunction()) {
|
||||
ok = m_ds.m_dotPos == DP_NONE;
|
||||
if (ok) {
|
||||
// The condition is true for tasks, properties and void functions.
|
||||
// In these cases, the parentheses may be skipped.
|
||||
AstFuncRef* const funcRefp
|
||||
= new AstFuncRef{nodep->fileline(), nodep->name(), nullptr};
|
||||
nodep->replaceWith(funcRefp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
if (!ok) {
|
||||
|
|
@ -2741,8 +2823,15 @@ private:
|
|||
UINFO(4, "(Backto) Link ClassOrPackageRef: " << nodep << endl);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
if (m_statep->forPrimary() && VN_IS(nodep->classOrPackagep(), Class) && !nodep->paramsp()
|
||||
&& nodep->classOrPackagep()->hasGParam()
|
||||
// Don't warn on typedefs, which are hard to know if there's a param somewhere buried
|
||||
&& VN_IS(nodep->classOrPackageNodep(), Class)) {
|
||||
nodep->v3error("Reference to parameterized class without #() (IEEE 1800-2017 8.25.1)\n"
|
||||
<< nodep->warnMore() << "... Suggest use '"
|
||||
<< nodep->classOrPackageNodep()->prettyName() << "#()'");
|
||||
}
|
||||
}
|
||||
|
||||
void visit(AstVarRef* nodep) override {
|
||||
// VarRef: Resolve its reference
|
||||
// ParseRefs are used the first pass (forPrimary) so we shouldn't get can't find
|
||||
|
|
@ -2876,12 +2965,32 @@ private:
|
|||
if (nodep->user3SetOnce()) return;
|
||||
UINFO(8, " " << nodep << endl);
|
||||
UINFO(8, " " << m_ds.ascii() << endl);
|
||||
if (m_ds.m_dotp && m_ds.m_dotPos == DP_PACKAGE) {
|
||||
{
|
||||
// Visit arguments at the beginning.
|
||||
// They may be visitted even if the current node can't be linked now.
|
||||
VL_RESTORER(m_ds);
|
||||
m_ds.init(m_curSymp);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
if (m_ds.m_unresolvedClass) {
|
||||
// Unable to link before V3Param
|
||||
return;
|
||||
} else if (m_ds.m_unresolvedCell && m_ds.m_dotPos == DP_FINAL && m_ds.m_unlinkedScopep) {
|
||||
AstNodeFTaskRef* const newftaskp = nodep->cloneTree(false);
|
||||
newftaskp->dotted(m_ds.m_dotText);
|
||||
AstNode* const newp = new AstUnlinkedRef{nodep->fileline(), newftaskp, nodep->name(),
|
||||
m_ds.m_unlinkedScopep->unlinkFrBack()};
|
||||
m_ds.m_unlinkedScopep = nullptr;
|
||||
m_ds.m_unresolvedCell = false;
|
||||
nodep->replaceWith(newp);
|
||||
return;
|
||||
} else if (m_ds.m_dotp && m_ds.m_dotPos == DP_PACKAGE) {
|
||||
UASSERT_OBJ(VN_IS(m_ds.m_dotp->lhsp(), ClassOrPackageRef), m_ds.m_dotp->lhsp(),
|
||||
"Bad package link");
|
||||
AstClassOrPackageRef* const cpackagerefp
|
||||
= VN_AS(m_ds.m_dotp->lhsp(), ClassOrPackageRef);
|
||||
if (cpackagerefp->name() == "process" || cpackagerefp->name() == "local") {
|
||||
if (cpackagerefp->name() == "local") {
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: " << AstNode::prettyNameQ(cpackagerefp->name()));
|
||||
}
|
||||
|
|
@ -2890,19 +2999,7 @@ private:
|
|||
m_ds.m_dotPos = DP_SCOPE;
|
||||
m_ds.m_dotp = nullptr;
|
||||
} else if (m_ds.m_dotp && m_ds.m_dotPos == DP_FINAL) {
|
||||
if (m_ds.m_unresolved && m_ds.m_unlinkedScopep) {
|
||||
AstNodeFTaskRef* const newftaskp = nodep->cloneTree(false);
|
||||
newftaskp->dotted(m_ds.m_dotText);
|
||||
AstNode* const newp
|
||||
= new AstUnlinkedRef{nodep->fileline(), newftaskp, nodep->name(),
|
||||
m_ds.m_unlinkedScopep->unlinkFrBack()};
|
||||
m_ds.m_unlinkedScopep = nullptr;
|
||||
m_ds.m_unresolved = false;
|
||||
nodep->replaceWith(newp);
|
||||
return;
|
||||
} else {
|
||||
nodep->dotted(m_ds.m_dotText); // Maybe ""
|
||||
}
|
||||
nodep->dotted(m_ds.m_dotText); // Maybe ""
|
||||
} else if (m_ds.m_dotp && m_ds.m_dotPos == DP_MEMBER) {
|
||||
// Found a Var, everything following is method call.
|
||||
// {scope}.{var}.HERE {method} ( ARGS )
|
||||
|
|
@ -3033,11 +3130,6 @@ private:
|
|||
}
|
||||
taskFuncSwapCheck(nodep);
|
||||
}
|
||||
{
|
||||
VL_RESTORER(m_ds);
|
||||
m_ds.init(m_curSymp);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstSelBit* nodep) override {
|
||||
if (nodep->user3SetOnce()) return;
|
||||
|
|
@ -3046,7 +3138,7 @@ private:
|
|||
== DP_SCOPE) { // Already under dot, so this is {modulepart} DOT {modulepart}
|
||||
UINFO(9, " deferring until after a V3Param pass: " << nodep << endl);
|
||||
m_ds.m_dotText += "__BRA__??__KET__";
|
||||
m_ds.m_unresolved = true;
|
||||
m_ds.m_unresolvedCell = true;
|
||||
// And pass up m_ds.m_dotText
|
||||
}
|
||||
// Pass dot state down to fromp()
|
||||
|
|
@ -3059,7 +3151,7 @@ private:
|
|||
iterateAndNextNull(nodep->attrp());
|
||||
}
|
||||
}
|
||||
if (m_ds.m_unresolved && m_ds.m_dotPos == DP_SCOPE) {
|
||||
if (m_ds.m_unresolvedCell && m_ds.m_dotPos == DP_SCOPE) {
|
||||
AstNodeExpr* const exprp = nodep->bitp()->unlinkFrBack();
|
||||
AstCellArrayRef* const newp
|
||||
= new AstCellArrayRef{nodep->fileline(), nodep->fromp()->name(), exprp};
|
||||
|
|
@ -3152,20 +3244,31 @@ private:
|
|||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstClass* nodep) override {
|
||||
nodep->user3SetOnce();
|
||||
UINFO(5, " " << nodep << endl);
|
||||
checkNoDot(nodep);
|
||||
VL_RESTORER(m_curSymp);
|
||||
VL_RESTORER(m_modSymp);
|
||||
VL_RESTORER(m_ifClassImpNames);
|
||||
{
|
||||
m_ds.init(m_curSymp);
|
||||
// Until overridden by a SCOPE
|
||||
m_ds.m_dotSymp = m_curSymp = m_modSymp = m_statep->getNodeSym(nodep);
|
||||
m_modp = nodep;
|
||||
int next = 0;
|
||||
for (AstNode* itemp = nodep->extendsp(); itemp; itemp = itemp->nextp()) {
|
||||
if (AstClassExtends* const cextp = VN_CAST(itemp, ClassExtends)) {
|
||||
// Replace abstract reference with hard pointer
|
||||
// Will need later resolution when deal with parameters
|
||||
if (++next == 2 && !nodep->isInterfaceClass() && !cextp->isImplements()) {
|
||||
cextp->v3error("Multiple inheritance illegal on non-interface classes"
|
||||
" (IEEE 1800-2017 8.13)");
|
||||
}
|
||||
if (cextp->childDTypep() || cextp->dtypep()) continue; // Already converted
|
||||
if (VN_IS(cextp->classOrPkgsp(), Dot)) {
|
||||
itemp->v3warn(E_UNSUPPORTED, "Unsupported: Hierarchical class references");
|
||||
continue;
|
||||
}
|
||||
AstClassOrPackageRef* const cpackagerefp
|
||||
= VN_CAST(cextp->classOrPkgsp(), ClassOrPackageRef);
|
||||
if (VL_UNCOVERABLE(!cpackagerefp)) {
|
||||
|
|
@ -3173,36 +3276,87 @@ private:
|
|||
cextp->v3error("Attempting to extend using non-class"); // LCOV_EXCL_LINE
|
||||
} else {
|
||||
VSymEnt* const foundp = m_curSymp->findIdFallback(cpackagerefp->name());
|
||||
bool ok = false;
|
||||
if (foundp) {
|
||||
if (AstClass* const classp = VN_CAST(foundp->nodep(), Class)) {
|
||||
AstClassRefDType* classRefDtypep = nullptr;
|
||||
AstClass* classp = VN_CAST(foundp->nodep(), Class);
|
||||
if (classp) {
|
||||
if (classp != nodep) {
|
||||
// Case with recursive inheritance is handled later in this
|
||||
// function
|
||||
iterate(classp);
|
||||
}
|
||||
if (classp->user5()) {
|
||||
// Has a parameter as its base class
|
||||
nodep->user5(true);
|
||||
return;
|
||||
}
|
||||
AstPin* paramsp = cpackagerefp->paramsp();
|
||||
if (paramsp) paramsp = paramsp->cloneTree(true);
|
||||
classRefDtypep
|
||||
= new AstClassRefDType{nodep->fileline(), classp, paramsp};
|
||||
} else if (AstParamTypeDType* const paramp
|
||||
= VN_CAST(foundp->nodep(), ParamTypeDType)) {
|
||||
if (m_statep->forPrimary()) {
|
||||
// Extending has to be handled after V3Param.cpp, but the type
|
||||
// reference has to be visited
|
||||
iterate(paramp);
|
||||
nodep->user5(true);
|
||||
return;
|
||||
} else {
|
||||
AstNodeDType* const paramTypep = paramp->getChildDTypep();
|
||||
classRefDtypep
|
||||
= VN_CAST(paramTypep->cloneTree(false), ClassRefDType);
|
||||
if (!classRefDtypep) {
|
||||
paramTypep->v3error(
|
||||
"Attempting to extend using non-class");
|
||||
} else {
|
||||
classp = classRefDtypep->classp();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cextp->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: " << foundp->nodep()->prettyTypeName()
|
||||
<< " in AstClassExtends");
|
||||
}
|
||||
|
||||
if (classp) {
|
||||
UINFO(8, "Import to " << nodep << " from export class " << classp
|
||||
<< endl);
|
||||
if (classp == nodep) {
|
||||
cextp->v3error("Attempting to extend class "
|
||||
<< nodep->prettyNameQ() << " from itself");
|
||||
} else if (cextp->isImplements() && !classp->isInterfaceClass()) {
|
||||
cextp->v3error(
|
||||
"Attempting to implement from non-interface class "
|
||||
<< classp->prettyNameQ() << '\n'
|
||||
<< "... Suggest use 'extends'");
|
||||
} else if (!cextp->isImplements() && !nodep->isInterfaceClass()
|
||||
&& classp->isInterfaceClass()) {
|
||||
cextp->v3error("Attempting to extend from interface class "
|
||||
<< classp->prettyNameQ() << '\n'
|
||||
<< "... Suggest use 'implements'");
|
||||
} else {
|
||||
AstPin* paramsp = cpackagerefp->paramsp();
|
||||
if (paramsp) paramsp = paramsp->cloneTree(true);
|
||||
const auto newp
|
||||
= new AstClassRefDType{nodep->fileline(), classp, paramsp};
|
||||
cextp->childDTypep(newp);
|
||||
cextp->childDTypep(classRefDtypep);
|
||||
classp->isExtended(true);
|
||||
nodep->isExtended(true);
|
||||
VSymEnt* const srcp = m_statep->getNodeSym(classp);
|
||||
m_curSymp->importFromClass(m_statep->symsp(), srcp);
|
||||
if (classp->isInterfaceClass()) {
|
||||
importImplementsClass(nodep, srcp, classp);
|
||||
}
|
||||
if (!cextp->isImplements()) {
|
||||
m_curSymp->importFromClass(m_statep->symsp(), srcp);
|
||||
}
|
||||
VL_DO_DANGLING(cpackagerefp->unlinkFrBack()->deleteTree(),
|
||||
cpackagerefp);
|
||||
}
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
if (!ok) {
|
||||
} else {
|
||||
const string suggest = m_statep->suggestSymFallback(
|
||||
m_curSymp, cpackagerefp->name(), LinkNodeMatcherClass{});
|
||||
cpackagerefp->v3error(
|
||||
"Class to extend not found: "
|
||||
<< cpackagerefp->prettyNameQ() << '\n'
|
||||
"Class for '"
|
||||
<< cextp->verilogKwd() // extends/implements
|
||||
<< "' not found: " << cpackagerefp->prettyNameQ() << '\n'
|
||||
<< (suggest.empty() ? "" : cpackagerefp->warnMore() + suggest));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -201,6 +201,15 @@ private:
|
|||
void prepost_stmt_visit(AstNodeTriop* nodep) {
|
||||
iterateChildren(nodep);
|
||||
|
||||
// Currently we can't reference the target, so we just copy the AST both for read and
|
||||
// write, but doing so would double any side-effects, so as a safety measure all
|
||||
// statements which could have side-effects are banned at the moment.
|
||||
if (!nodep->rhsp()->isTreePureRecurse()) {
|
||||
nodep->rhsp()->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Inc/Dec of expression with side-effects");
|
||||
return;
|
||||
}
|
||||
|
||||
AstConst* const constp = VN_AS(nodep->lhsp(), Const);
|
||||
UASSERT_OBJ(nodep, constp, "Expecting CONST");
|
||||
AstConst* const newconstp = constp->cloneTree(true);
|
||||
|
|
@ -222,6 +231,15 @@ private:
|
|||
void prepost_expr_visit(AstNodeTriop* nodep) {
|
||||
iterateChildren(nodep);
|
||||
|
||||
// Currently we can't reference the target, so we just copy the AST both for read and
|
||||
// write, but doing so would double any side-effects, so as a safety measure all
|
||||
// statements which could have side-effects are banned at the moment.
|
||||
if (!nodep->rhsp()->isTreePureRecurse()) {
|
||||
nodep->rhsp()->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Inc/Dec of expression with side-effects");
|
||||
return;
|
||||
}
|
||||
|
||||
const AstNodeVarRef* varrefp = nullptr;
|
||||
if (m_unsupportedHere || !(varrefp = VN_CAST(nodep->rhsp(), VarRef))) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Incrementation in this context.");
|
||||
|
|
|
|||
|
|
@ -59,15 +59,23 @@ void V3LinkLevel::modSortByLevel() {
|
|||
if (tops.size() >= 2) {
|
||||
const AstNode* const secp = tops[1]; // Complain about second one, as first often intended
|
||||
if (!secp->fileline()->warnIsOff(V3ErrorCode::MULTITOP)) {
|
||||
auto warnTopModules = [](std::string warnMore, ModVec tops)
|
||||
VL_REQUIRES(V3Error::s().m_mutex) -> std::string {
|
||||
std::stringstream ss;
|
||||
for (AstNode* alsop : tops) {
|
||||
ss << warnMore << "... Top module " << alsop->prettyNameQ() << endl
|
||||
<< alsop->warnContextSecondary();
|
||||
}
|
||||
return ss.str();
|
||||
};
|
||||
|
||||
secp->v3warn(MULTITOP, "Multiple top level modules\n"
|
||||
<< secp->warnMore()
|
||||
<< "... Suggest see manual; fix the duplicates, or use "
|
||||
"--top-module to select top."
|
||||
<< V3Error::warnContextNone());
|
||||
for (AstNode* alsop : tops) {
|
||||
std::cerr << secp->warnMore() << "... Top module " << alsop->prettyNameQ() << endl
|
||||
<< alsop->warnContextSecondary();
|
||||
}
|
||||
<< V3Error::s().warnContextNone()
|
||||
<< V3Error::warnAdditionalInfo()
|
||||
<< warnTopModules(secp->warnMore(), tops));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -148,8 +148,8 @@ private:
|
|||
if (classPkgRefp && VN_IS(classPkgRefp->classOrPackageNodep(), Class)) {
|
||||
// Class methods are automatic by default
|
||||
m_lifetime = VLifetime::AUTOMATIC;
|
||||
} else if (nodep->dpiImport()) {
|
||||
// DPI-imported function don't have lifetime specifiers
|
||||
} else if (nodep->dpiImport() || VN_IS(nodep, Property)) {
|
||||
// DPI-imported functions and properties don't have lifetime specifiers
|
||||
m_lifetime = VLifetime::NONE;
|
||||
}
|
||||
if (m_lifetime.isStatic() && hasStaticDeclAssignments(nodep)) {
|
||||
|
|
@ -222,13 +222,10 @@ private:
|
|||
|
||||
void visit(AstVar* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
if (nodep->lifetime().isNone()) {
|
||||
if (m_ftaskp) {
|
||||
nodep->lifetime(VLifetime::AUTOMATIC);
|
||||
} else {
|
||||
nodep->lifetime(m_lifetime);
|
||||
}
|
||||
if (nodep->lifetime().isNone() && nodep->varType() != VVarType::PORT) {
|
||||
nodep->lifetime(m_lifetime);
|
||||
}
|
||||
if (nodep->isGParam() && m_modp) m_modp->hasGParam(true);
|
||||
if (nodep->isParam() && !nodep->valuep()
|
||||
&& nodep->fileline()->language() < V3LangCode::L1800_2009) {
|
||||
nodep->v3error("Parameter requires default value, or use IEEE 1800-2009 or later.");
|
||||
|
|
|
|||
|
|
@ -145,34 +145,13 @@ private:
|
|||
void visit(AstNodePreSel* nodep) override {
|
||||
if (!nodep->attrp()) {
|
||||
iterateChildren(nodep);
|
||||
// Constification may change the fromp() to a constant, which will lose the
|
||||
// variable we're extracting from (to determine MSB/LSB/endianness/etc.)
|
||||
// So we replicate it in another node
|
||||
// Note that V3Param knows not to replace AstVarRef's under AstAttrOf's
|
||||
AstNode* const basefromp = AstArraySel::baseFromp(nodep, false);
|
||||
if (AstNodeVarRef* const varrefp
|
||||
= VN_CAST(basefromp, NodeVarRef)) { // Maybe varxref - so need to clone
|
||||
nodep->attrp(new AstAttrOf{nodep->fileline(), VAttrType::VAR_BASE,
|
||||
varrefp->cloneTree(false)});
|
||||
} else if (AstUnlinkedRef* const uvxrp
|
||||
= VN_CAST(basefromp, UnlinkedRef)) { // Maybe unlinked - so need to clone
|
||||
nodep->attrp(new AstAttrOf{nodep->fileline(), VAttrType::VAR_BASE,
|
||||
uvxrp->cloneTree(false)});
|
||||
} else if (auto* const fromp = VN_CAST(basefromp, LambdaArgRef)) {
|
||||
nodep->attrp(new AstAttrOf{nodep->fileline(), VAttrType::VAR_BASE,
|
||||
fromp->cloneTree(false)});
|
||||
} else if (AstMemberSel* const fromp = VN_CAST(basefromp, MemberSel)) {
|
||||
nodep->attrp(new AstAttrOf{nodep->fileline(), VAttrType::MEMBER_BASE,
|
||||
fromp->cloneTree(false)});
|
||||
} else if (AstEnumItemRef* const fromp = VN_CAST(basefromp, EnumItemRef)) {
|
||||
nodep->attrp(new AstAttrOf{nodep->fileline(), VAttrType::ENUM_BASE,
|
||||
fromp->cloneTree(false)});
|
||||
} else if (VN_IS(basefromp, Replicate)) {
|
||||
if (VN_IS(basefromp, Replicate)) {
|
||||
// From {...}[...] syntax in IEEE 2017
|
||||
if (basefromp) UINFO(1, " Related node: " << basefromp << endl);
|
||||
} else {
|
||||
if (basefromp) UINFO(1, " Related node: " << basefromp << endl);
|
||||
nodep->v3fatalSrc("Illegal bit select; no signal/member being extracted from");
|
||||
nodep->attrp(new AstAttrOf{nodep->fileline(), VAttrType::VAR_BASE,
|
||||
basefromp->cloneTree(false)});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -233,12 +212,12 @@ private:
|
|||
inPct = true;
|
||||
inIgnore = false;
|
||||
fmt = ch;
|
||||
} else if (inPct && (isdigit(ch) || ch == '.' || ch == '-')) {
|
||||
} else if (inPct && (std::isdigit(ch) || ch == '.' || ch == '-')) {
|
||||
fmt += ch;
|
||||
} else if (inPct) {
|
||||
inPct = false;
|
||||
fmt += ch;
|
||||
switch (tolower(ch)) {
|
||||
switch (std::tolower(ch)) {
|
||||
case '%': // %% - just output a %
|
||||
break;
|
||||
case '*':
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ constexpr int MAX_SPRINTF_DOUBLE_SIZE
|
|||
//======================================================================
|
||||
// Errors
|
||||
|
||||
void V3Number::v3errorEnd(const std::ostringstream& str) const VL_MT_SAFE {
|
||||
void V3Number::v3errorEnd(const std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
std::ostringstream nsstr;
|
||||
nsstr << str.str();
|
||||
if (m_nodep) {
|
||||
|
|
@ -84,11 +84,12 @@ void V3Number::v3errorEnd(const std::ostringstream& str) const VL_MT_SAFE {
|
|||
} else if (m_fileline) {
|
||||
m_fileline->v3errorEnd(nsstr);
|
||||
} else {
|
||||
V3Error::v3errorEnd(nsstr);
|
||||
V3Error::s().v3errorEnd(nsstr);
|
||||
}
|
||||
}
|
||||
|
||||
void V3Number::v3errorEndFatal(const std::ostringstream& str) const VL_MT_SAFE {
|
||||
void V3Number::v3errorEndFatal(const std::ostringstream& str) const
|
||||
VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
v3errorEnd(str);
|
||||
assert(0); // LCOV_EXCL_LINE
|
||||
VL_UNREACHABLE;
|
||||
|
|
@ -138,6 +139,7 @@ void V3Number::create(const char* sourcep) {
|
|||
}
|
||||
}
|
||||
|
||||
bool userSized = false;
|
||||
bool unbased = false;
|
||||
char base = '\0';
|
||||
if (value_startp != sourcep) { // Has a '
|
||||
|
|
@ -151,7 +153,7 @@ void V3Number::create(const char* sourcep) {
|
|||
if (*cp != '_') widthn += *cp;
|
||||
}
|
||||
while (*cp == '_') cp++;
|
||||
if (*cp && tolower(*cp) == 's') {
|
||||
if (*cp && std::tolower(*cp) == 's') {
|
||||
cp++;
|
||||
isSigned(true);
|
||||
}
|
||||
|
|
@ -161,6 +163,7 @@ void V3Number::create(const char* sourcep) {
|
|||
}
|
||||
value_startp = cp;
|
||||
|
||||
userSized = widthn.length() != 0;
|
||||
if (std::atoi(widthn.c_str())) {
|
||||
if (std::atoi(widthn.c_str()) < 0
|
||||
|| std::atoi(widthn.c_str()) > v3Global.opt.maxNumWidth()) {
|
||||
|
|
@ -188,11 +191,11 @@ void V3Number::create(const char* sourcep) {
|
|||
width(1, false); // So we extend it
|
||||
setBit(0, 1);
|
||||
m_data.m_autoExtend = true;
|
||||
} else if (tolower(base) == 'z') {
|
||||
} else if (std::tolower(base) == 'z') {
|
||||
width(1, false); // So we extend it
|
||||
setBit(0, 'z');
|
||||
m_data.m_autoExtend = true;
|
||||
} else if (tolower(base) == 'x') {
|
||||
} else if (std::tolower(base) == 'x') {
|
||||
width(1, false); // So we extend it
|
||||
setBit(0, 'x');
|
||||
m_data.m_autoExtend = true;
|
||||
|
|
@ -204,13 +207,17 @@ void V3Number::create(const char* sourcep) {
|
|||
}
|
||||
|
||||
// Ignore leading blanks
|
||||
while (*value_startp == '_' || isspace(*value_startp)) value_startp++;
|
||||
while (*value_startp == '_' || std::isspace(*value_startp)) ++value_startp;
|
||||
if (!*value_startp && !m_data.m_autoExtend) {
|
||||
v3error("Number is missing value digits: " << sourcep);
|
||||
}
|
||||
if (userSized && m_data.m_autoExtend) {
|
||||
v3error("Syntax error: size cannot be provided with '0/'1/'x/'z: "
|
||||
<< sourcep << " (IEEE 1800-2017 5.7.1)");
|
||||
}
|
||||
|
||||
int obit = 0; // Start at LSB
|
||||
if (tolower(base) == 'd') {
|
||||
if (std::tolower(base) == 'd') {
|
||||
// Ignore leading zeros so we don't issue too many digit errors when lots of leading 0's
|
||||
while (*value_startp == '_' || *value_startp == '0') value_startp++;
|
||||
// Convert decimal number to hex
|
||||
|
|
@ -220,7 +227,7 @@ void V3Number::create(const char* sourcep) {
|
|||
int got_z = 0;
|
||||
int got_01 = 0;
|
||||
for (const char* cp = value_startp; *cp; cp++) {
|
||||
switch (tolower(*cp)) {
|
||||
switch (std::tolower(*cp)) {
|
||||
case '0': // FALLTHRU
|
||||
case '1': // FALLTHRU
|
||||
case '2': // FALLTHRU
|
||||
|
|
@ -291,9 +298,9 @@ void V3Number::create(const char* sourcep) {
|
|||
v3error("Too many digits for " << width() << " bit number: " << sourcep);
|
||||
break;
|
||||
}
|
||||
switch (tolower(base)) {
|
||||
switch (std::tolower(base)) {
|
||||
case 'b': {
|
||||
switch (tolower(*cp)) {
|
||||
switch (std::tolower(*cp)) {
|
||||
case '0': setBit(obit++, 0); break;
|
||||
case '1': setBit(obit++, 1); break;
|
||||
case 'z':
|
||||
|
|
@ -307,7 +314,7 @@ void V3Number::create(const char* sourcep) {
|
|||
|
||||
case 'o':
|
||||
case 'c': {
|
||||
switch (tolower(*cp)) {
|
||||
switch (std::tolower(*cp)) {
|
||||
// clang-format off
|
||||
case '0': setBit(obit++, 0); setBit(obit++, 0); setBit(obit++, 0); break;
|
||||
case '1': setBit(obit++, 1); setBit(obit++, 0); setBit(obit++, 0); break;
|
||||
|
|
@ -328,7 +335,7 @@ void V3Number::create(const char* sourcep) {
|
|||
}
|
||||
|
||||
case 'h': {
|
||||
switch (tolower(*cp)) {
|
||||
switch (std::tolower(*cp)) {
|
||||
// clang-format off
|
||||
case '0': setBit(obit++,0); setBit(obit++,0); setBit(obit++,0); setBit(obit++,0); break;
|
||||
case '1': setBit(obit++,1); setBit(obit++,0); setBit(obit++,0); setBit(obit++,0); break;
|
||||
|
|
@ -568,7 +575,7 @@ string V3Number::ascii(bool prefixed, bool cleanVerilog) const {
|
|||
|
||||
bool V3Number::displayedFmtLegal(char format, bool isScan) {
|
||||
// Is this a valid format letter?
|
||||
switch (tolower(format)) {
|
||||
switch (std::tolower(format)) {
|
||||
case 'b': return true;
|
||||
case 'c': return true;
|
||||
case 'd': return true; // Unsigned decimal
|
||||
|
|
@ -612,11 +619,11 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const {
|
|||
++pos;
|
||||
}
|
||||
string fmtsize;
|
||||
for (; pos != vformat.cend() && (isdigit(pos[0]) || pos[0] == '.'); ++pos) {
|
||||
for (; pos != vformat.cend() && (std::isdigit(pos[0]) || pos[0] == '.'); ++pos) {
|
||||
fmtsize += pos[0];
|
||||
}
|
||||
string str;
|
||||
const char code = tolower(pos[0]);
|
||||
const char code = std::tolower(pos[0]);
|
||||
switch (code) {
|
||||
case 'b': // FALLTHRU
|
||||
case 'o': // FALLTHRU
|
||||
|
|
@ -703,7 +710,8 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const {
|
|||
return str;
|
||||
} // case b/d/x/o
|
||||
case 'c': {
|
||||
if (width() > 8) fl->v3warn(WIDTH, "$display-like format of %c format of > 8 bit value");
|
||||
if (width() > 8)
|
||||
fl->v3warn(WIDTHTRUNC, "$display-like format of %c format of > 8 bit value");
|
||||
const unsigned int v = bitsValue(0, 8);
|
||||
char strc[2];
|
||||
strc[0] = v & 0xff;
|
||||
|
|
@ -1427,15 +1435,19 @@ V3Number& V3Number::opRepl(const V3Number& lhs,
|
|||
// i op repl, L(i)*value(rhs) bit return
|
||||
NUM_ASSERT_OP_ARGS1(lhs);
|
||||
NUM_ASSERT_LOGIC_ARGS1(lhs);
|
||||
setZero();
|
||||
if (rhsval > 8192) {
|
||||
if (rhsval > (1UL << 24)) {
|
||||
v3error("More than a 16 Mbit replication, perhaps the replication factor"
|
||||
" was two's-complement negative: "
|
||||
<< rhsval);
|
||||
} else if (rhsval > 8192) {
|
||||
v3warn(WIDTHCONCAT, "More than a 8k bit replication is probably wrong: " << rhsval);
|
||||
}
|
||||
setZero();
|
||||
int obit = 0;
|
||||
for (unsigned times = 0; times < rhsval; times++) {
|
||||
for (int bit = 0; bit < lhs.width(); bit++) {
|
||||
for (unsigned times = 0; times < rhsval; ++times) {
|
||||
for (int bit = 0; bit < lhs.width(); ++bit) {
|
||||
setBit(obit, lhs.bitIs(bit));
|
||||
obit++;
|
||||
++obit;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
|
|
@ -2194,13 +2206,13 @@ V3Number& V3Number::opAssignNonXZ(const V3Number& lhs, bool ignoreXZ) {
|
|||
m_data.m_isNull = true;
|
||||
} else if (isString()) {
|
||||
if (VL_UNLIKELY(!lhs.isString())) {
|
||||
// Non-compatible types, erase value.
|
||||
m_data.str() = "";
|
||||
// Numbers can still be strings
|
||||
m_data.str() = lhs.toString();
|
||||
} else {
|
||||
m_data.str() = lhs.m_data.str();
|
||||
}
|
||||
} else if (VL_UNLIKELY(lhs.isString())) {
|
||||
// Non-compatible types, erase value.
|
||||
// Non-compatible types, see also opAToN()
|
||||
setZero();
|
||||
} else {
|
||||
// Also handles double as is just bits
|
||||
|
|
@ -2212,6 +2224,20 @@ V3Number& V3Number::opAssignNonXZ(const V3Number& lhs, bool ignoreXZ) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
V3Number& V3Number::opNToI(const V3Number& lhs) {
|
||||
// String to packed number
|
||||
NUM_ASSERT_OP_ARGS1(lhs);
|
||||
NUM_ASSERT_STRING_ARGS1(lhs);
|
||||
setZero();
|
||||
const string& str = lhs.toString();
|
||||
for (size_t n = 0; n < str.length(); ++n) {
|
||||
const char c = str[str.length() - 1 - n];
|
||||
for (size_t cbit = 0; cbit < 8; ++cbit)
|
||||
setBit(n * 8 + cbit, VL_BITISSET_I(c, cbit) ? 1 : 0);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
V3Number& V3Number::opExtendS(const V3Number& lhs, uint32_t lbits) {
|
||||
// Note may be a width change during the sign extension
|
||||
NUM_ASSERT_OP_ARGS1(lhs);
|
||||
|
|
@ -2243,7 +2269,8 @@ void V3Number::opCleanThis(bool warnOnTruncation) {
|
|||
const uint32_t newValueXMsb = v.m_valueX & hiWordMask();
|
||||
if (warnOnTruncation && (newValueMsb != v.m_value || newValueXMsb != v.m_valueX)) {
|
||||
// Displaying in decimal avoids hiWordMask truncation
|
||||
v3warn(WIDTH, "Value too large for " << width() << " bit number: " << displayed("%d"));
|
||||
v3warn(WIDTHTRUNC,
|
||||
"Value too large for " << width() << " bit number: " << displayed("%d"));
|
||||
}
|
||||
m_data.num()[words() - 1] = {newValueMsb, newValueXMsb};
|
||||
}
|
||||
|
|
@ -2459,15 +2486,13 @@ V3Number& V3Number::opReplN(const V3Number& lhs, uint32_t rhsval) {
|
|||
V3Number& V3Number::opToLowerN(const V3Number& lhs) {
|
||||
NUM_ASSERT_OP_ARGS1(lhs);
|
||||
NUM_ASSERT_STRING_ARGS1(lhs);
|
||||
std::string out = lhs.toString();
|
||||
for (auto& cr : out) cr = tolower(cr);
|
||||
std::string out = VString::downcase(lhs.toString());
|
||||
return setString(out);
|
||||
}
|
||||
V3Number& V3Number::opToUpperN(const V3Number& lhs) {
|
||||
NUM_ASSERT_OP_ARGS1(lhs);
|
||||
NUM_ASSERT_STRING_ARGS1(lhs);
|
||||
std::string out = lhs.toString();
|
||||
for (auto& cr : out) cr = toupper(cr);
|
||||
std::string out = VString::upcase(lhs.toString());
|
||||
return setString(out);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -566,8 +566,9 @@ private:
|
|||
}
|
||||
|
||||
public:
|
||||
void v3errorEnd(const std::ostringstream& sstr) const;
|
||||
void v3errorEndFatal(const std::ostringstream& sstr) const VL_ATTR_NORETURN;
|
||||
void v3errorEnd(const std::ostringstream& sstr) const VL_REQUIRES(V3Error::s().m_mutex);
|
||||
void v3errorEndFatal(const std::ostringstream& sstr) const VL_ATTR_NORETURN
|
||||
VL_REQUIRES(V3Error::s().m_mutex);
|
||||
void width(int width, bool sized = true) {
|
||||
m_data.m_sized = sized;
|
||||
m_data.resize(width);
|
||||
|
|
@ -761,6 +762,7 @@ public:
|
|||
|
||||
// "N" - string operations
|
||||
V3Number& opAtoN(const V3Number& lhs, int base);
|
||||
V3Number& opNToI(const V3Number& lhs);
|
||||
V3Number& opPutcN(const V3Number& lhs, const V3Number& rhs, const V3Number& ths);
|
||||
V3Number& opGetcN(const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opSubstrN(const V3Number& lhs, const V3Number& rhs, const V3Number& ths);
|
||||
|
|
|
|||
|
|
@ -237,20 +237,20 @@ void VTimescale::parseSlashed(FileLine* fl, const char* textp, VTimescale& unitr
|
|||
precr = VTimescale::NONE;
|
||||
|
||||
const char* cp = textp;
|
||||
for (; isspace(*cp); ++cp) {}
|
||||
for (; std::isspace(*cp); ++cp) {}
|
||||
const char* const unitp = cp;
|
||||
for (; *cp && *cp != '/'; ++cp) {}
|
||||
const string unitStr(unitp, cp - unitp);
|
||||
for (; isspace(*cp); ++cp) {}
|
||||
for (; std::isspace(*cp); ++cp) {}
|
||||
string precStr;
|
||||
if (*cp == '/') {
|
||||
++cp;
|
||||
for (; isspace(*cp); ++cp) {}
|
||||
for (; std::isspace(*cp); ++cp) {}
|
||||
const char* const precp = cp;
|
||||
for (; *cp && *cp != '/'; ++cp) {}
|
||||
precStr = string(precp, cp - precp);
|
||||
}
|
||||
for (; isspace(*cp); ++cp) {}
|
||||
for (; std::isspace(*cp); ++cp) {}
|
||||
if (*cp) {
|
||||
fl->v3error("`timescale syntax error: '" << textp << "'");
|
||||
return;
|
||||
|
|
@ -489,8 +489,10 @@ string V3Options::fileExists(const string& filename) {
|
|||
std::set<string>* setp = &(diriter->second);
|
||||
|
||||
#ifdef _MSC_VER
|
||||
for (const auto& dirEntry : std::filesystem::directory_iterator(dir.c_str()))
|
||||
setp->insert(dirEntry.path().filename().string());
|
||||
try {
|
||||
for (const auto& dirEntry : std::filesystem::directory_iterator(dir.c_str()))
|
||||
setp->insert(dirEntry.path().filename().string());
|
||||
} catch (std::filesystem::filesystem_error const& ex) { return ""; }
|
||||
#else
|
||||
if (DIR* const dirp = opendir(dir.c_str())) {
|
||||
while (struct dirent* direntp = readdir(dirp)) setp->insert(direntp->d_name);
|
||||
|
|
@ -570,26 +572,28 @@ string V3Options::filePath(FileLine* fl, const string& modname, const string& la
|
|||
void V3Options::filePathLookedMsg(FileLine* fl, const string& modname) {
|
||||
static bool shown_notfound_msg = false;
|
||||
if (modname.find("__Vhsh") != string::npos) {
|
||||
std::cerr << V3Error::warnMore() << "... Unsupported: Name is longer than 127 characters;"
|
||||
std::cerr << V3Error::warnMoreStandalone()
|
||||
<< "... Unsupported: Name is longer than 127 characters;"
|
||||
<< " automatic file lookup not supported.\n";
|
||||
std::cerr << V3Error::warnMore() << "... Suggest putting filename with this module/package"
|
||||
std::cerr << V3Error::warnMoreStandalone()
|
||||
<< "... Suggest putting filename with this module/package"
|
||||
<< " onto command line instead.\n";
|
||||
} else if (!shown_notfound_msg) {
|
||||
shown_notfound_msg = true;
|
||||
if (m_impp->m_incDirUsers.empty()) {
|
||||
fl->v3error("This may be because there's no search path specified with -I<dir>.");
|
||||
}
|
||||
std::cerr << V3Error::warnMore() << "... Looked in:" << endl;
|
||||
std::cerr << V3Error::warnMoreStandalone() << "... Looked in:" << endl;
|
||||
for (const string& dir : m_impp->m_incDirUsers) {
|
||||
for (const string& ext : m_impp->m_libExtVs) {
|
||||
const string fn = V3Os::filenameFromDirBase(dir, modname + ext);
|
||||
std::cerr << V3Error::warnMore() << " " << fn << endl;
|
||||
std::cerr << V3Error::warnMoreStandalone() << " " << fn << endl;
|
||||
}
|
||||
}
|
||||
for (const string& dir : m_impp->m_incDirFallbacks) {
|
||||
for (const string& ext : m_impp->m_libExtVs) {
|
||||
const string fn = V3Os::filenameFromDirBase(dir, modname + ext);
|
||||
std::cerr << V3Error::warnMore() << " " << fn << endl;
|
||||
std::cerr << V3Error::warnMoreStandalone() << " " << fn << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -602,7 +606,9 @@ void V3Options::filePathLookedMsg(FileLine* fl, const string& modname) {
|
|||
V3LangCode V3Options::fileLanguage(const string& filename) {
|
||||
string ext = V3Os::filenameNonDir(filename);
|
||||
string::size_type pos;
|
||||
if ((pos = ext.rfind('.')) != string::npos) {
|
||||
if (filename == V3Options::getStdPackagePath()) {
|
||||
return V3LangCode::mostRecent();
|
||||
} else if ((pos = ext.rfind('.')) != string::npos) {
|
||||
ext.erase(0, pos + 1);
|
||||
const auto it = m_impp->m_langExts.find(ext);
|
||||
if (it != m_impp->m_langExts.end()) return it->second;
|
||||
|
|
@ -770,15 +776,11 @@ void V3Options::notify() {
|
|||
FileLine* const cmdfl = new FileLine{FileLine::commandLineFilename()};
|
||||
|
||||
if (!outFormatOk() && v3Global.opt.main()) ccSet(); // --main implies --cc if not provided
|
||||
if (!outFormatOk() && !cdc() && !dpiHdrOnly() && !lintOnly() && !preprocOnly() && !xmlOnly()) {
|
||||
v3fatal("verilator: Need --binary, --cc, --sc, --cdc, --dpi-hdr-only, --lint-only, "
|
||||
if (!outFormatOk() && !dpiHdrOnly() && !lintOnly() && !preprocOnly() && !xmlOnly()) {
|
||||
v3fatal("verilator: Need --binary, --cc, --sc, --dpi-hdr-only, --lint-only, "
|
||||
"--xml-only or --E option");
|
||||
}
|
||||
|
||||
if (cdc()) {
|
||||
cmdfl->v3warn(DEPRECATED, "Option --cdc is deprecated and is planned for removal");
|
||||
}
|
||||
|
||||
if (m_build && (m_gmake || m_cmake)) {
|
||||
cmdfl->v3error("--make cannot be used together with --build. Suggest see manual");
|
||||
}
|
||||
|
|
@ -820,16 +822,14 @@ void V3Options::notify() {
|
|||
// Default some options if not turned on or off
|
||||
if (v3Global.opt.skipIdentical().isDefault()) {
|
||||
v3Global.opt.m_skipIdentical.setTrueOrFalse( //
|
||||
!v3Global.opt.cdc() //
|
||||
&& !v3Global.opt.dpiHdrOnly() //
|
||||
!v3Global.opt.dpiHdrOnly() //
|
||||
&& !v3Global.opt.lintOnly() //
|
||||
&& !v3Global.opt.preprocOnly() //
|
||||
&& !v3Global.opt.xmlOnly());
|
||||
}
|
||||
if (v3Global.opt.makeDepend().isDefault()) {
|
||||
v3Global.opt.m_makeDepend.setTrueOrFalse( //
|
||||
!v3Global.opt.cdc() //
|
||||
&& !v3Global.opt.dpiHdrOnly() //
|
||||
!v3Global.opt.dpiHdrOnly() //
|
||||
&& !v3Global.opt.lintOnly() //
|
||||
&& !v3Global.opt.preprocOnly() //
|
||||
&& !v3Global.opt.xmlOnly());
|
||||
|
|
@ -1079,7 +1079,6 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
|
||||
DECL_OPTION("-CFLAGS", CbVal, callStrSetter(&V3Options::addCFlags));
|
||||
DECL_OPTION("-cc", CbCall, [this]() { ccSet(); });
|
||||
DECL_OPTION("-cdc", OnOff, &m_cdc);
|
||||
DECL_OPTION("-clk", CbVal, callStrSetter(&V3Options::addClocker));
|
||||
DECL_OPTION("-no-clk", CbVal, callStrSetter(&V3Options::addNoClocker));
|
||||
DECL_OPTION("-comp-limit-blocks", Set, &m_compLimitBlocks).undocumented();
|
||||
|
|
@ -1145,7 +1144,10 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
DECL_OPTION("-dumpi-", CbPartialMatchVal, [this](const char* optp, const char* valp) {
|
||||
m_dumpLevel[optp] = std::atoi(valp);
|
||||
});
|
||||
DECL_OPTION("-E", Set, &m_preprocOnly);
|
||||
DECL_OPTION("-E", CbOnOff, [this](bool flag) {
|
||||
if (flag) m_std = false;
|
||||
m_preprocOnly = flag;
|
||||
});
|
||||
DECL_OPTION("-error-limit", CbVal, static_cast<void (*)(int)>(&V3Error::errorLimit));
|
||||
DECL_OPTION("-exe", OnOff, &m_exe);
|
||||
DECL_OPTION("-expand-limit", CbVal,
|
||||
|
|
@ -1286,8 +1288,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
fl->v3warn(DEPRECATED, "Option -O<letter> is deprecated. "
|
||||
"Use -f<optimization> or -fno-<optimization> instead.");
|
||||
for (const char* cp = optp; *cp; ++cp) {
|
||||
const bool flag = isupper(*cp);
|
||||
switch (tolower(*cp)) {
|
||||
const bool flag = std::isupper(*cp);
|
||||
switch (std::tolower(*cp)) {
|
||||
case '0': optimize(0); break;
|
||||
case '1': optimize(1); break;
|
||||
case '2': optimize(2); break;
|
||||
|
|
@ -1405,6 +1407,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
m_statsVars = flag;
|
||||
m_stats |= flag;
|
||||
});
|
||||
DECL_OPTION("-std", OnOff, &m_std);
|
||||
DECL_OPTION("-structs-packed", OnOff, &m_structsPacked);
|
||||
DECL_OPTION("-sv", CbCall, [this]() { m_defaultLanguage = V3LangCode::L1800_2017; });
|
||||
|
||||
|
|
@ -1505,6 +1508,17 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
DECL_OPTION("-v", CbVal, [this, &optdir](const char* valp) {
|
||||
V3Options::addLibraryFile(parseFileArg(optdir, valp));
|
||||
});
|
||||
DECL_OPTION("-verilate-jobs", CbVal, [this, fl](const char* valp) {
|
||||
int val = std::atoi(valp);
|
||||
if (val < 0) {
|
||||
fl->v3error("--verilate-jobs requires a non-negative integer, but '"
|
||||
<< valp << "' was passed");
|
||||
val = 1;
|
||||
} else if (val == 0) {
|
||||
val = std::thread::hardware_concurrency();
|
||||
}
|
||||
m_verilateJobs = val;
|
||||
});
|
||||
DECL_OPTION("-verilate", OnOff, &m_verilate);
|
||||
DECL_OPTION("-version", CbCall, [this]() {
|
||||
showVersion(false);
|
||||
|
|
@ -1512,7 +1526,10 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
});
|
||||
DECL_OPTION("-vpi", OnOff, &m_vpi);
|
||||
|
||||
DECL_OPTION("-Wpedantic", OnOff, &m_pedantic);
|
||||
DECL_OPTION("-Wpedantic", CbCall, [this]() {
|
||||
m_pedantic = true;
|
||||
V3Error::pretendError(V3ErrorCode::ASSIGNIN, false);
|
||||
});
|
||||
DECL_OPTION("-Wall", CbCall, []() {
|
||||
FileLine::globalWarnLintOff(false);
|
||||
FileLine::globalWarnStyleOff(false);
|
||||
|
|
@ -1553,6 +1570,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
});
|
||||
DECL_OPTION("-Wno-style", CbCall, []() { FileLine::globalWarnStyleOff(true); });
|
||||
DECL_OPTION("-Wno-UNUSED", CbCall, []() { FileLine::globalWarnUnusedOff(true); });
|
||||
DECL_OPTION("-Wno-WIDTH", CbCall, []() { FileLine::globalWarnOff(V3ErrorCode::WIDTH, true); });
|
||||
DECL_OPTION("-Wwarn-", CbPartialMatch, [this, fl, &parser](const char* optp) {
|
||||
const V3ErrorCode code{optp};
|
||||
if (code == V3ErrorCode::EC_ERROR) {
|
||||
|
|
@ -1574,6 +1592,10 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
V3Error::pretendError(V3ErrorCode::UNUSEDSIGNAL, false);
|
||||
V3Error::pretendError(V3ErrorCode::UNUSEDPARAM, false);
|
||||
});
|
||||
DECL_OPTION("-Wwarn-WIDTH", CbCall, []() {
|
||||
FileLine::globalWarnOff(V3ErrorCode::WIDTH, false);
|
||||
V3Error::pretendError(V3ErrorCode::WIDTH, false);
|
||||
});
|
||||
DECL_OPTION("-waiver-output", Set, &m_waiverOutput);
|
||||
|
||||
DECL_OPTION("-x-assign", CbVal, [this, fl](const char* valp) {
|
||||
|
|
@ -1622,12 +1644,13 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
|| !std::strcmp(argv[i], "--j")) { // Allow gnu -- switches
|
||||
++i;
|
||||
int val = 0;
|
||||
if (i < argc && isdigit(argv[i][0])) {
|
||||
val = atoi(argv[i]); // Can't be negative due to isdigit above
|
||||
if (i < argc && std::isdigit(argv[i][0])) {
|
||||
val = std::atoi(argv[i]); // Can't be negative due to isdigit above
|
||||
if (val == 0) val = std::thread::hardware_concurrency();
|
||||
++i;
|
||||
}
|
||||
if (m_buildJobs == -1) m_buildJobs = val;
|
||||
if (m_verilateJobs == -1) m_verilateJobs = val;
|
||||
} else if (argv[i][0] == '-' || argv[i][0] == '+') {
|
||||
const char* argvNoDashp = (argv[i][1] == '-') ? (argv[i] + 2) : (argv[i] + 1);
|
||||
if (const int consumed = parser.parse(i, argc, argv)) {
|
||||
|
|
@ -1660,6 +1683,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
}
|
||||
}
|
||||
if (m_buildJobs == -1) m_buildJobs = 1;
|
||||
if (m_verilateJobs == -1) m_verilateJobs = 1;
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
|
@ -1690,7 +1714,8 @@ void V3Options::parseOptsFile(FileLine* fl, const string& filename, bool rel) {
|
|||
++pos;
|
||||
}
|
||||
} else if (*pos == '/' && *(pos + 1) == '/'
|
||||
&& (pos == line.begin() || isspace(lastch))) { // But allow /file//path
|
||||
&& (pos == line.begin()
|
||||
|| std::isspace(lastch))) { // But allow /file//path
|
||||
break; // Ignore to EOL
|
||||
} else if (*pos == '#' && space_begin) { // Only # at [spaced] begin of line
|
||||
break; // Ignore to EOL
|
||||
|
|
@ -1700,7 +1725,7 @@ void V3Options::parseOptsFile(FileLine* fl, const string& filename, bool rel) {
|
|||
// cppcheck-suppress StlMissingComparison
|
||||
++pos;
|
||||
} else {
|
||||
if (!isspace(*pos)) space_begin = false;
|
||||
if (!std::isspace(*pos)) space_begin = false;
|
||||
oline += *pos;
|
||||
}
|
||||
}
|
||||
|
|
@ -1733,7 +1758,7 @@ void V3Options::parseOptsFile(FileLine* fl, const string& filename, bool rel) {
|
|||
char curr_char = whole_file[pos];
|
||||
switch (st) {
|
||||
case ST_IN_OPTION: // Get all chars up to a white space or a "="
|
||||
if (isspace(curr_char)) { // End of option
|
||||
if (std::isspace(curr_char)) { // End of option
|
||||
if (!arg.empty()) { // End of word
|
||||
args.push_back(arg);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -224,7 +224,6 @@ private:
|
|||
bool m_bboxSys = false; // main switch: --bbox-sys
|
||||
bool m_bboxUnsup = false; // main switch: --bbox-unsup
|
||||
bool m_build = false; // main switch: --build
|
||||
bool m_cdc = false; // main switch: --cdc
|
||||
bool m_cmake = false; // main switch: --make cmake
|
||||
bool m_context = true; // main switch: --Wcontext
|
||||
bool m_coverageLine = false; // main switch: --coverage-block
|
||||
|
|
@ -267,6 +266,7 @@ private:
|
|||
bool m_relativeIncludes = false; // main switch: --relative-includes
|
||||
bool m_reportUnoptflat = false; // main switch: --report-unoptflat
|
||||
bool m_savable = false; // main switch: --savable
|
||||
bool m_std = true; // main switch: --std
|
||||
bool m_structsPacked = false; // main switch: --structs-packed
|
||||
bool m_systemC = false; // main switch: --sc: System C instead of simple C++
|
||||
bool m_stats = false; // main switch: --stats
|
||||
|
|
@ -317,6 +317,7 @@ private:
|
|||
int m_traceThreads = 0; // main switch: --trace-threads
|
||||
int m_unrollCount = 64; // main switch: --unroll-count
|
||||
int m_unrollStmts = 30000; // main switch: --unroll-stmts
|
||||
int m_verilateJobs = -1; // main switch: --verilate-jobs
|
||||
|
||||
int m_compLimitBlocks = 0; // compiler selection; number of nested blocks
|
||||
int m_compLimitMembers = 64; // compiler selection; number of members in struct before make anon array
|
||||
|
|
@ -430,6 +431,7 @@ public:
|
|||
bool savable() const VL_MT_SAFE { return m_savable; }
|
||||
bool stats() const { return m_stats; }
|
||||
bool statsVars() const { return m_statsVars; }
|
||||
bool std() const { return m_std; }
|
||||
bool structsPacked() const { return m_structsPacked; }
|
||||
bool assertOn() const { return m_assert; } // assertOn as __FILE__ may be defined
|
||||
bool autoflush() const { return m_autoflush; }
|
||||
|
|
@ -438,7 +440,6 @@ public:
|
|||
bool build() const { return m_build; }
|
||||
string buildDepBin() const { return m_buildDepBin; }
|
||||
void buildDepBin(const string& flag) { m_buildDepBin = flag; }
|
||||
bool cdc() const { return m_cdc; }
|
||||
bool cmake() const { return m_cmake; }
|
||||
bool context() const VL_MT_SAFE { return m_context; }
|
||||
bool coverage() const VL_MT_SAFE {
|
||||
|
|
@ -544,6 +545,7 @@ public:
|
|||
}
|
||||
int unrollCount() const { return m_unrollCount; }
|
||||
int unrollStmts() const { return m_unrollStmts; }
|
||||
int verilateJobs() const { return m_verilateJobs; }
|
||||
|
||||
int compLimitBlocks() const { return m_compLimitBlocks; }
|
||||
int compLimitMembers() const { return m_compLimitMembers; }
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ string V3Os::filenameSubstitute(const string& filename) {
|
|||
string::size_type endpos = pos + 1;
|
||||
while (((endpos + 1) < filename.length())
|
||||
&& (((brackets == NONE)
|
||||
&& (isalnum(filename[endpos + 1]) || filename[endpos + 1] == '_'))
|
||||
&& (std::isalnum(filename[endpos + 1]) || filename[endpos + 1] == '_'))
|
||||
|| ((brackets == CURLY) && (filename[endpos + 1] != '}'))
|
||||
|| ((brackets == PAREN) && (filename[endpos + 1] != ')'))))
|
||||
++endpos;
|
||||
|
|
|
|||
|
|
@ -547,7 +547,10 @@ class ParamProcessor final {
|
|||
AstClass* const newClassp) {
|
||||
if (AstClassRefDType* const classRefp = VN_CAST(nodep, ClassRefDType)) {
|
||||
if (classRefp->classp() == oldClassp) classRefp->classp(newClassp);
|
||||
} else if (AstClassOrPackageRef* const classRefp = VN_CAST(nodep, ClassOrPackageRef)) {
|
||||
if (classRefp->classOrPackagep() == oldClassp) classRefp->classOrPackagep(newClassp);
|
||||
}
|
||||
|
||||
if (nodep->op1p()) replaceRefsRecurse(nodep->op1p(), oldClassp, newClassp);
|
||||
if (nodep->op2p()) replaceRefsRecurse(nodep->op2p(), oldClassp, newClassp);
|
||||
if (nodep->op3p()) replaceRefsRecurse(nodep->op3p(), oldClassp, newClassp);
|
||||
|
|
@ -820,12 +823,18 @@ class ParamProcessor final {
|
|||
}
|
||||
|
||||
for (auto* stmtp = srcModpr->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
if (auto* dtypep = VN_CAST(stmtp, ParamTypeDType)) {
|
||||
if (AstParamTypeDType* dtypep = VN_CAST(stmtp, ParamTypeDType)) {
|
||||
if (VN_IS(dtypep->subDTypep(), VoidDType)) {
|
||||
nodep->v3error("Missing type parameter: " << dtypep->prettyNameQ());
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
}
|
||||
}
|
||||
if (AstVar* const varp = VN_CAST(stmtp, Var)) {
|
||||
if (VN_IS(srcModpr, Class) && varp->isParam() && !varp->valuep()) {
|
||||
nodep->v3error("Class parameter without initial value is never given value"
|
||||
<< " (IEEE 1800-2017 6.20.1): " << varp->prettyNameQ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the parameters from the cell; they're not relevant any longer.
|
||||
|
|
@ -911,6 +920,8 @@ class ParamVisitor final : public VNVisitor {
|
|||
bool m_iterateModule = false; // Iterating module body
|
||||
string m_generateHierName; // Generate portion of hierarchy name
|
||||
string m_unlinkedTxt; // Text for AstUnlinkedRef
|
||||
AstNodeModule* m_modp; // Module iterating
|
||||
std::vector<AstDot*> m_dots; // Dot references to process
|
||||
std::multimap<bool, AstNode*> m_cellps; // Cells left to process (in current module)
|
||||
std::multimap<int, AstNodeModule*> m_workQueue; // Modules left to process
|
||||
|
||||
|
|
@ -940,7 +951,11 @@ class ParamVisitor final : public VNVisitor {
|
|||
if (modp->someInstanceName().empty()) modp->someInstanceName(modp->origName());
|
||||
|
||||
// Iterate the body
|
||||
iterateChildren(modp);
|
||||
{
|
||||
VL_RESTORER(m_modp);
|
||||
m_modp = modp;
|
||||
iterateChildren(modp);
|
||||
}
|
||||
|
||||
// Process interface cells, then non-interface cells, which may reference an interface
|
||||
// cell.
|
||||
|
|
@ -1007,6 +1022,21 @@ class ParamVisitor final : public VNVisitor {
|
|||
m_cellps.emplace(!isIface, nodep);
|
||||
}
|
||||
|
||||
// RHSs of AstDots need a relink when LHS is a parametrized class reference
|
||||
void relinkDots() {
|
||||
for (AstDot* const dotp : m_dots) {
|
||||
const AstClassOrPackageRef* const classRefp = VN_AS(dotp->lhsp(), ClassOrPackageRef);
|
||||
const AstClass* const lhsClassp = VN_AS(classRefp->classOrPackageNodep(), Class);
|
||||
AstClassOrPackageRef* const rhsp = VN_AS(dotp->rhsp(), ClassOrPackageRef);
|
||||
for (auto* itemp = lhsClassp->membersp(); itemp; itemp = itemp->nextp()) {
|
||||
if (itemp->name() == rhsp->name()) {
|
||||
rhsp->classOrPackageNodep(itemp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
if (nodep->recursiveClone()) nodep->dead(true); // Fake, made for recursive elimination
|
||||
|
|
@ -1037,7 +1067,7 @@ class ParamVisitor final : public VNVisitor {
|
|||
if (nodep->user5SetOnce()) return; // Process once
|
||||
iterateChildren(nodep);
|
||||
if (nodep->isParam()) {
|
||||
if (!nodep->valuep()) {
|
||||
if (!nodep->valuep() && !VN_IS(m_modp, Class)) {
|
||||
nodep->v3error("Parameter without initial value is never given value"
|
||||
<< " (IEEE 1800-2017 6.20.1): " << nodep->prettyNameQ());
|
||||
} else {
|
||||
|
|
@ -1113,6 +1143,25 @@ class ParamVisitor final : public VNVisitor {
|
|||
nodep->varp(nullptr); // Needs relink, as may remove pointed-to var
|
||||
}
|
||||
|
||||
void visit(AstDot* nodep) override {
|
||||
iterate(nodep->lhsp());
|
||||
// Check if it is a reference to a field of a parameterized class.
|
||||
// If so, the RHS should be updated, when the LHS is replaced
|
||||
// by a class with actual parameter values.
|
||||
const AstClass* lhsClassp = nullptr;
|
||||
const AstClassOrPackageRef* const classRefp = VN_CAST(nodep->lhsp(), ClassOrPackageRef);
|
||||
if (classRefp) lhsClassp = VN_CAST(classRefp->classOrPackageNodep(), Class);
|
||||
AstNode* rhsDefp = nullptr;
|
||||
AstClassOrPackageRef* const rhsp = VN_CAST(nodep->rhsp(), ClassOrPackageRef);
|
||||
if (rhsp) rhsDefp = rhsp->classOrPackageNodep();
|
||||
if (lhsClassp && rhsDefp) {
|
||||
m_dots.push_back(nodep);
|
||||
// No need to iterate into rhsp, because there should be nothing to do
|
||||
} else {
|
||||
iterate(nodep->rhsp());
|
||||
}
|
||||
}
|
||||
|
||||
void visit(AstUnlinkedRef* nodep) override {
|
||||
AstVarXRef* const varxrefp = VN_CAST(nodep->refp(), VarXRef);
|
||||
AstNodeFTaskRef* const taskrefp = VN_CAST(nodep->refp(), NodeFTaskRef);
|
||||
|
|
@ -1215,6 +1264,7 @@ class ParamVisitor final : public VNVisitor {
|
|||
}
|
||||
void visit(AstGenCase* nodep) override {
|
||||
UINFO(9, " GENCASE " << nodep << endl);
|
||||
bool hit = false;
|
||||
AstNode* keepp = nullptr;
|
||||
iterateAndNextNull(nodep->exprp());
|
||||
V3Case::caseLint(nodep);
|
||||
|
|
@ -1240,7 +1290,10 @@ class ParamVisitor final : public VNVisitor {
|
|||
if (const AstConst* const ccondp = VN_CAST(ep, Const)) {
|
||||
V3Number match{nodep, 1};
|
||||
match.opEq(ccondp->num(), exprp->num());
|
||||
if (!keepp && match.isNeqZero()) keepp = itemp->stmtsp();
|
||||
if (!hit && match.isNeqZero()) {
|
||||
hit = true;
|
||||
keepp = itemp->stmtsp();
|
||||
}
|
||||
} else {
|
||||
itemp->v3error("Generate Case item does not evaluate to constant");
|
||||
}
|
||||
|
|
@ -1251,7 +1304,10 @@ class ParamVisitor final : public VNVisitor {
|
|||
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), CaseItem)) {
|
||||
if (itemp->isDefault()) {
|
||||
if (!keepp) keepp = itemp->stmtsp();
|
||||
if (!hit) {
|
||||
hit = true;
|
||||
keepp = itemp->stmtsp();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Replace
|
||||
|
|
@ -1273,6 +1329,8 @@ public:
|
|||
// Relies on modules already being in top-down-order
|
||||
iterate(netlistp);
|
||||
|
||||
relinkDots();
|
||||
|
||||
// Re-sort module list to be in topological order and fix-up incorrect levels. We need to
|
||||
// do this globally at the end due to the presence of recursive modules, which might be
|
||||
// expanded in orders that reuse earlier specializations later at a lower level.
|
||||
|
|
|
|||
|
|
@ -240,6 +240,7 @@ AstVar* V3ParseGrammar::createVariable(FileLine* fileline, const string& name,
|
|||
} else {
|
||||
nodep->trace(allTracingOn(nodep->fileline()));
|
||||
}
|
||||
if (nodep->varType().isVPIAccessible()) { nodep->addAttrsp(GRAMMARP->cloneScopedSigAttr()); }
|
||||
|
||||
// Remember the last variable created, so we can attach attributes to it in later parsing
|
||||
GRAMMARP->m_varAttrp = nodep;
|
||||
|
|
@ -256,7 +257,7 @@ string V3ParseGrammar::deQuote(FileLine* fileline, string text) {
|
|||
int octal_digits = 0;
|
||||
for (string::const_iterator cp = text.begin(); cp != text.end(); ++cp) {
|
||||
if (quoted) {
|
||||
if (isdigit(*cp)) {
|
||||
if (std::isdigit(*cp)) {
|
||||
octal_val = octal_val * 8 + (*cp - '0');
|
||||
if (++octal_digits == 3) {
|
||||
octal_digits = 0;
|
||||
|
|
@ -285,13 +286,13 @@ string V3ParseGrammar::deQuote(FileLine* fileline, string text) {
|
|||
newtext += '\t';
|
||||
} else if (*cp == 'v') {
|
||||
newtext += '\v'; // SystemVerilog 3.1
|
||||
} else if (*cp == 'x' && isxdigit(cp[1])
|
||||
&& isxdigit(cp[2])) { // SystemVerilog 3.1
|
||||
#define vl_decodexdigit(c) ((isdigit(c) ? ((c) - '0') : (tolower((c)) - 'a' + 10)))
|
||||
} else if (*cp == 'x' && std::isxdigit(cp[1])
|
||||
&& std::isxdigit(cp[2])) { // SystemVerilog 3.1
|
||||
#define vl_decodexdigit(c) ((std::isdigit(c) ? ((c) - '0') : (std::tolower((c)) - 'a' + 10)))
|
||||
newtext
|
||||
+= static_cast<char>(16 * vl_decodexdigit(cp[1]) + vl_decodexdigit(cp[2]));
|
||||
cp += 2;
|
||||
} else if (isalnum(*cp)) {
|
||||
} else if (std::isalnum(*cp)) {
|
||||
fileline->v3error("Unknown escape sequence: \\" << *cp);
|
||||
break;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -134,10 +134,10 @@ void V3ParseImp::lexVerilatorCmtLintRestore(FileLine* fl) {
|
|||
|
||||
void V3ParseImp::lexVerilatorCmtLint(FileLine* fl, const char* textp, bool warnOff) {
|
||||
const char* sp = textp;
|
||||
while (*sp && !isspace(*sp)) ++sp;
|
||||
while (*sp && isspace(*sp)) ++sp;
|
||||
while (*sp && !isspace(*sp)) ++sp;
|
||||
while (*sp && isspace(*sp)) ++sp;
|
||||
while (*sp && !std::isspace(*sp)) ++sp;
|
||||
while (*sp && std::isspace(*sp)) ++sp;
|
||||
while (*sp && !std::isspace(*sp)) ++sp;
|
||||
while (*sp && std::isspace(*sp)) ++sp;
|
||||
string msg = sp;
|
||||
string::size_type pos;
|
||||
if ((pos = msg.find('*')) != string::npos) msg.erase(pos);
|
||||
|
|
@ -155,9 +155,9 @@ void V3ParseImp::lexVerilatorCmtBad(FileLine* fl, const char* textp) {
|
|||
if (cmtparse.substr(0, std::strlen("/*verilator")) == "/*verilator") {
|
||||
cmtparse.replace(0, std::strlen("/*verilator"), "");
|
||||
}
|
||||
while (isspace(cmtparse[0])) cmtparse.replace(0, 1, "");
|
||||
while (std::isspace(cmtparse[0])) cmtparse.replace(0, 1, "");
|
||||
string cmtname;
|
||||
for (int i = 0; isalnum(cmtparse[i]); i++) { cmtname += cmtparse[i]; }
|
||||
for (int i = 0; std::isalnum(cmtparse[i]); i++) cmtname += cmtparse[i];
|
||||
if (!v3Global.opt.isFuture(cmtname)) {
|
||||
fl->v3error("Unknown verilator comment: '" << textp << "'");
|
||||
}
|
||||
|
|
@ -191,7 +191,7 @@ double V3ParseImp::lexParseTimenum(const char* textp) {
|
|||
char* const strgp = new char[length + 1];
|
||||
char* dp = strgp;
|
||||
const char* sp = textp;
|
||||
for (; isdigit(*sp) || *sp == '_' || *sp == '.'; ++sp) {
|
||||
for (; std::isdigit(*sp) || *sp == '_' || *sp == '.'; ++sp) {
|
||||
if (*sp != '_') *dp++ = *sp;
|
||||
}
|
||||
*dp++ = '\0';
|
||||
|
|
@ -256,7 +256,7 @@ void V3ParseImp::preprocDumps(std::ostream& os) {
|
|||
if (noblanks) {
|
||||
bool blank = true;
|
||||
for (string::iterator its = buf.begin(); its != buf.end(); ++its) {
|
||||
if (!isspace(*its) && *its != '\n') {
|
||||
if (!std::isspace(*its) && *its != '\n') {
|
||||
blank = false;
|
||||
break;
|
||||
}
|
||||
|
|
@ -374,6 +374,32 @@ size_t V3ParseImp::tokenPipeScanParam(size_t depth) {
|
|||
return depth;
|
||||
}
|
||||
|
||||
size_t V3ParseImp::tokenPipeScanType(size_t depth) {
|
||||
// Search around IEEE type_reference to see if is expression
|
||||
// Return location of following token, or input if not found
|
||||
// yTYPE__ETC '(' ... ')' ['==' '===' '!=' '!===']
|
||||
if (tokenPeekp(depth)->token != '(') return depth;
|
||||
depth += 1; // Past the (
|
||||
int parens = 1; // Count first (
|
||||
while (true) {
|
||||
const int tok = tokenPeekp(depth)->token;
|
||||
if (tok == 0) {
|
||||
UINFO(9, "tokenPipeScanType hit EOF; probably syntax error to come");
|
||||
break;
|
||||
} else if (tok == '(') {
|
||||
++parens;
|
||||
} else if (tok == ')') {
|
||||
--parens;
|
||||
if (parens == 0) {
|
||||
++depth;
|
||||
break;
|
||||
}
|
||||
}
|
||||
++depth;
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
void V3ParseImp::tokenPipeline() {
|
||||
// called from bison's "yylex", has a "this"
|
||||
if (m_tokensAhead.empty()) tokenPull(); // corrupts yylval
|
||||
|
|
@ -388,6 +414,7 @@ void V3ParseImp::tokenPipeline() {
|
|||
|| token == yLOCAL__LEX //
|
||||
|| token == yNEW__LEX //
|
||||
|| token == ySTATIC__LEX //
|
||||
|| token == yTYPE__LEX //
|
||||
|| token == yVIRTUAL__LEX //
|
||||
|| token == yWITH__LEX //
|
||||
|| token == yaID__LEX //
|
||||
|
|
@ -443,6 +470,18 @@ void V3ParseImp::tokenPipeline() {
|
|||
} else {
|
||||
token = ySTATIC__ETC;
|
||||
}
|
||||
} else if (token == yTYPE__LEX) {
|
||||
VL_RESTORER(yylval); // Remember value, as about to read ahead
|
||||
const size_t depth = tokenPipeScanType(0);
|
||||
const int postToken = tokenPeekp(depth)->token;
|
||||
if ( // v-- token v-- postToken
|
||||
// yTYPE__EQ '(' .... ')' EQ_OPERATOR yTYPE_ETC '(' ... ')'
|
||||
postToken == yP_EQUAL || postToken == yP_NOTEQUAL || postToken == yP_CASEEQUAL
|
||||
|| postToken == yP_CASENOTEQUAL) {
|
||||
token = yTYPE__EQ;
|
||||
} else {
|
||||
token = yTYPE__ETC;
|
||||
}
|
||||
} else if (token == yVIRTUAL__LEX) {
|
||||
if (nexttok == yCLASS) {
|
||||
token = yVIRTUAL__CLASS;
|
||||
|
|
@ -496,7 +535,7 @@ void V3ParseImp::tokenPipelineSym() {
|
|||
if (const VSymEnt* const look_underp = V3ParseImp::parsep()->symp()->nextId()) {
|
||||
UINFO(7, " tokenPipelineSym: next id lookup forced under " << look_underp << endl);
|
||||
// if (debug() >= 7) V3ParseImp::parsep()->symp()->dumpSelf(cout, " -symtree: ");
|
||||
foundp = look_underp->findIdFallback(*(yylval.strp));
|
||||
foundp = look_underp->findIdFlat(*(yylval.strp));
|
||||
// "consume" it. Must set again if want another token under temp scope
|
||||
V3ParseImp::parsep()->symp()->nextId(nullptr);
|
||||
} else {
|
||||
|
|
@ -507,6 +546,19 @@ void V3ParseImp::tokenPipelineSym() {
|
|||
// " -findtree: ", true);
|
||||
foundp = V3ParseImp::parsep()->symp()->symCurrentp()->findIdFallback(*(yylval.strp));
|
||||
}
|
||||
if (!foundp && !m_afterColonColon) { // Check if the symbol can be found in std
|
||||
AstPackage* const stdpkgp = v3Global.rootp()->stdPackagep();
|
||||
if (stdpkgp) {
|
||||
VSymEnt* const stdsymp = stdpkgp->user4u().toSymEnt();
|
||||
foundp = stdsymp->findIdFallback(*(yylval.strp));
|
||||
}
|
||||
if (foundp && !v3Global.usesStdPackage()) {
|
||||
AstPackageImport* const impp
|
||||
= new AstPackageImport(stdpkgp->fileline(), stdpkgp, "*");
|
||||
unitPackage(stdpkgp->fileline())->addStmtsp(impp);
|
||||
v3Global.setUsesStdPackage();
|
||||
}
|
||||
}
|
||||
if (foundp) {
|
||||
AstNode* const scp = foundp->nodep();
|
||||
yylval.scp = scp;
|
||||
|
|
@ -523,20 +575,13 @@ void V3ParseImp::tokenPipelineSym() {
|
|||
} else {
|
||||
token = yaID__ETC;
|
||||
}
|
||||
} else if (!m_afterColonColon && *(yylval.strp) == "std") {
|
||||
v3Global.setUsesStdPackage();
|
||||
}
|
||||
} else if ((token == yaID__LEX || token == yaID__CC)
|
||||
&& (*(yylval.strp) == "mailbox" // IEEE-standard class
|
||||
|| *(yylval.strp) == "process" // IEEE-standard class
|
||||
|| *(yylval.strp) == "semaphore")) { // IEEE-standard class
|
||||
v3Global.setUsesStdPackage();
|
||||
yylval.scp = nullptr;
|
||||
if (token == yaID__LEX) token = yaID__aTYPE;
|
||||
} else { // Not found
|
||||
yylval.scp = nullptr;
|
||||
if (token == yaID__CC) {
|
||||
if (!m_afterColonColon && *(yylval.strp) == "std") {
|
||||
v3Global.setUsesStdPackage();
|
||||
} else if (!v3Global.opt.bboxUnsup()) {
|
||||
if (!v3Global.opt.bboxUnsup()) {
|
||||
// IEEE does require this, but we may relax this as UVM breaks it, so allow
|
||||
// bbox for today
|
||||
// We'll get a parser error eventually but might not be obvious
|
||||
|
|
|
|||
|
|
@ -303,6 +303,7 @@ private:
|
|||
void tokenPipeline(); // Internal; called from tokenToBison
|
||||
void tokenPipelineSym();
|
||||
size_t tokenPipeScanParam(size_t depth);
|
||||
size_t tokenPipeScanType(size_t depth);
|
||||
const V3ParseBisonYYSType* tokenPeekp(size_t depth);
|
||||
void preprocDumps(std::ostream& os);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -185,7 +185,9 @@ public:
|
|||
// For getline()
|
||||
string m_lineChars; ///< Characters left for next line
|
||||
|
||||
void v3errorEnd(std::ostringstream& str) { fileline()->v3errorEnd(str); }
|
||||
void v3errorEnd(std::ostringstream& str) VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
fileline()->v3errorEnd(str);
|
||||
}
|
||||
|
||||
static const char* tokenName(int tok);
|
||||
void debugToken(int tok, const char* cmtp);
|
||||
|
|
@ -387,14 +389,14 @@ string V3PreProcImp::commentCleanup(const string& text) {
|
|||
while ((pos = cmd.find('\"')) != string::npos) cmd.replace(pos, 1, " ");
|
||||
while ((pos = cmd.find('\t')) != string::npos) cmd.replace(pos, 1, " ");
|
||||
while ((pos = cmd.find(" ")) != string::npos) cmd.replace(pos, 2, " ");
|
||||
while (!cmd.empty() && isspace(cmd[cmd.size() - 1])) cmd.erase(cmd.size() - 1);
|
||||
while (!cmd.empty() && std::isspace(cmd[cmd.size() - 1])) cmd.erase(cmd.size() - 1);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
bool V3PreProcImp::commentTokenMatch(string& cmdr, const char* strg) {
|
||||
int len = std::strlen(strg);
|
||||
if (VString::startsWith(cmdr, strg) && (cmdr[len] == '\0' || isspace(cmdr[len]))) {
|
||||
if (isspace(cmdr[len])) len++;
|
||||
if (VString::startsWith(cmdr, strg) && (cmdr[len] == '\0' || std::isspace(cmdr[len]))) {
|
||||
if (std::isspace(cmdr[len])) len++;
|
||||
cmdr = cmdr.substr(len);
|
||||
return true;
|
||||
} else {
|
||||
|
|
@ -417,7 +419,7 @@ void V3PreProcImp::comment(const string& text) {
|
|||
return;
|
||||
}
|
||||
|
||||
while (isspace(*cp)) cp++;
|
||||
while (std::isspace(*cp)) ++cp;
|
||||
|
||||
bool synth = false;
|
||||
bool vlcomment = false;
|
||||
|
|
@ -450,7 +452,7 @@ void V3PreProcImp::comment(const string& text) {
|
|||
|
||||
if (!vlcomment && !synth) return; // Short-circuit
|
||||
|
||||
while (isspace(*cp)) cp++;
|
||||
while (std::isspace(*cp)) ++cp;
|
||||
string cmd = commentCleanup(string(cp));
|
||||
// cmd now is comment without extra spaces and "verilator" prefix
|
||||
|
||||
|
|
@ -472,12 +474,16 @@ void V3PreProcImp::comment(const string& text) {
|
|||
// else ignore the comment we don't recognize
|
||||
} // else no assertions
|
||||
} else if (vlcomment) {
|
||||
string::size_type pos;
|
||||
if ((pos = cmd.find("public_flat_rw")) != string::npos) {
|
||||
if (VString::startsWith(cmd, "public_flat_rw")) {
|
||||
// "/*verilator public_flat_rw @(foo) */" -> "/*verilator public_flat_rw*/ @(foo)"
|
||||
cmd = cmd.substr(pos + std::strlen("public_flat_rw"));
|
||||
while (isspace(cmd[0])) cmd = cmd.substr(1);
|
||||
if (!printed) insertUnreadback("/*verilator public_flat_rw*/ " + cmd + " /**/");
|
||||
string::size_type endOfCmd = std::strlen("public_flat_rw");
|
||||
while (VString::isWordChar(cmd[endOfCmd])) ++endOfCmd;
|
||||
string baseCmd = cmd.substr(0, endOfCmd);
|
||||
string arg = cmd.substr(endOfCmd);
|
||||
while (std::isspace(arg[0])) arg = arg.substr(1);
|
||||
if (arg.size() && baseCmd == "public_flat_rw_on")
|
||||
baseCmd += "_sns"; // different cmd for applying sensitivity
|
||||
if (!printed) insertUnreadback("/*verilator " + baseCmd + "*/ " + arg + " /**/");
|
||||
} else {
|
||||
if (!printed) insertUnreadback("/*verilator " + cmd + "*/");
|
||||
}
|
||||
|
|
@ -554,16 +560,16 @@ string V3PreProcImp::trimWhitespace(const string& strg, bool trailing) {
|
|||
// Remove leading whitespace
|
||||
string out = strg;
|
||||
string::size_type leadspace = 0;
|
||||
while (out.length() > leadspace && isspace(out[leadspace])) leadspace++;
|
||||
while (out.length() > leadspace && std::isspace(out[leadspace])) ++leadspace;
|
||||
if (leadspace) out.erase(0, leadspace);
|
||||
// Remove trailing whitespace
|
||||
if (trailing) {
|
||||
string::size_type trailspace = 0;
|
||||
while (out.length() > trailspace && isspace(out[out.length() - 1 - trailspace]))
|
||||
trailspace++;
|
||||
while (out.length() > trailspace && std::isspace(out[out.length() - 1 - trailspace]))
|
||||
++trailspace;
|
||||
// Don't remove \{space_or_newline}
|
||||
if (trailspace && out.length() > trailspace && out[out.length() - 1 - trailspace] == '\\')
|
||||
trailspace--;
|
||||
--trailspace;
|
||||
if (trailspace) out.erase(out.length() - trailspace, trailspace);
|
||||
}
|
||||
return out;
|
||||
|
|
@ -675,13 +681,13 @@ string V3PreProcImp::defineSubst(VDefineRef* refp) {
|
|||
// UINFO(4, "CH "<<*cp<<" an "<<argName<<endl);
|
||||
if (!quote && *cp == '\\') {
|
||||
backslashesc = true;
|
||||
} else if (isspace(*cp)) {
|
||||
} else if (std::isspace(*cp)) {
|
||||
backslashesc = false;
|
||||
}
|
||||
// We don't check for quotes; some simulators expand even inside quotes
|
||||
if (isalpha(*cp) || *cp == '_'
|
||||
if (std::isalpha(*cp) || *cp == '_'
|
||||
|| *cp == '$' // Won't replace system functions, since no $ in argValueByName
|
||||
|| (argName != "" && (isdigit(*cp) || *cp == '$'))) {
|
||||
|| (argName != "" && (std::isdigit(*cp) || *cp == '$'))) {
|
||||
argName += *cp;
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,13 +99,13 @@ class Graph final : public V3Graph {
|
|||
// TODO: 'typeName' is an internal thing. This should be more human readable.
|
||||
if (LogicVertex* const lvtxp = dynamic_cast<LogicVertex*>(vtxp)) {
|
||||
AstNode* const logicp = lvtxp->logicp();
|
||||
std::cerr << logicp->fileline()->warnOther()
|
||||
std::cerr << logicp->fileline()->warnOtherStandalone()
|
||||
<< " Example path: " << logicp->typeName() << endl;
|
||||
} else {
|
||||
VarVertex* const vvtxp = dynamic_cast<VarVertex*>(vtxp);
|
||||
UASSERT(vvtxp, "Cannot be anything else");
|
||||
AstVarScope* const vscp = vvtxp->vscp();
|
||||
std::cerr << vscp->fileline()->warnOther()
|
||||
std::cerr << vscp->fileline()->warnOtherStandalone()
|
||||
<< " Example path: " << vscp->prettyName() << endl;
|
||||
}
|
||||
}
|
||||
|
|
@ -289,7 +289,7 @@ void reportLoopVars(Graph* graphp, VarVertex* vvtxp) {
|
|||
if (i == candidates.size()) break;
|
||||
const Candidate& candidate = candidates[i];
|
||||
AstVar* const varp = candidate.first->varp();
|
||||
std::cerr << V3Error::warnMore() << " " << varp->fileline() << " "
|
||||
std::cerr << V3Error::warnMoreStandalone() << " " << varp->fileline() << " "
|
||||
<< varp->prettyName() << ", width " << std::dec << varp->width()
|
||||
<< ", circular fanout " << candidate.second;
|
||||
if (V3SplitVar::canSplitVar(varp)) {
|
||||
|
|
@ -301,19 +301,19 @@ void reportLoopVars(Graph* graphp, VarVertex* vvtxp) {
|
|||
};
|
||||
|
||||
// Widest variables
|
||||
std::cerr << V3Error::warnMore() << "... Widest variables candidate to splitting:\n";
|
||||
std::cerr << V3Error::warnMoreStandalone() << "... Widest variables candidate to splitting:\n";
|
||||
reportFirst10([](const Candidate& a, const Candidate& b) {
|
||||
return a.first->varp()->width() > b.first->varp()->width();
|
||||
});
|
||||
|
||||
// Highest fanout
|
||||
std::cerr << V3Error::warnMore() << "... Candidates with the highest fanout:\n";
|
||||
std::cerr << V3Error::warnMoreStandalone() << "... Candidates with the highest fanout:\n";
|
||||
reportFirst10([](const Candidate& a, const Candidate& b) { //
|
||||
return a.second > b.second;
|
||||
});
|
||||
|
||||
if (splittable) {
|
||||
std::cerr << V3Error::warnMore()
|
||||
std::cerr << V3Error::warnMoreStandalone()
|
||||
<< "... Suggest add /*verilator split_var*/ to appropriate variables above."
|
||||
<< std::endl;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ private:
|
|||
if (AstRefDType* const refdtypep = VN_CAST(dtypep, RefDType)) { //
|
||||
dtypep = refdtypep->skipRefp();
|
||||
}
|
||||
if (AstStructDType* const stp = VN_CAST(dtypep, StructDType)) {
|
||||
if (AstNodeUOrStructDType* const stp = VN_CAST(dtypep, NodeUOrStructDType)) {
|
||||
if (stp->packed()) {
|
||||
std::ostringstream out;
|
||||
out << "'{";
|
||||
|
|
@ -418,7 +418,7 @@ private:
|
|||
if (!VN_IS(nodep->varp()->dtypeSkipRefp(), BasicDType)
|
||||
&& !VN_IS(nodep->varp()->dtypeSkipRefp(), PackArrayDType)
|
||||
&& !VN_IS(nodep->varp()->dtypeSkipRefp(), UnpackArrayDType)
|
||||
&& !VN_IS(nodep->varp()->dtypeSkipRefp(), StructDType))
|
||||
&& !VN_IS(nodep->varp()->dtypeSkipRefp(), NodeUOrStructDType))
|
||||
clearOptimizable(nodep, "Array references/not basic");
|
||||
if (nodep->access().isWriteOrRW()) {
|
||||
if (m_inDlyAssign) {
|
||||
|
|
@ -442,7 +442,8 @@ private:
|
|||
clearOptimizable(nodep, "Var write & read");
|
||||
}
|
||||
vscp->user1(vscp->user1() | VU_RV);
|
||||
const bool isConst = nodep->varp()->isParam() && nodep->varp()->valuep();
|
||||
const bool isConst = (nodep->varp()->isConst() || nodep->varp()->isParam())
|
||||
&& nodep->varp()->valuep();
|
||||
AstNodeExpr* const valuep
|
||||
= isConst ? fetchValueNull(nodep->varp()->valuep()) : nullptr;
|
||||
// Propagate PARAM constants for constant function analysis
|
||||
|
|
@ -503,6 +504,15 @@ private:
|
|||
checkNodeInfo(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstInitialStatic* nodep) override {
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (!m_params) {
|
||||
badNodeType(nodep);
|
||||
return;
|
||||
}
|
||||
checkNodeInfo(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNodeIf* nodep) override {
|
||||
if (jumpingOver(nodep)) return;
|
||||
UINFO(5, " IF " << nodep << endl);
|
||||
|
|
@ -1074,15 +1084,22 @@ private:
|
|||
const string format = nodep->text();
|
||||
auto pos = format.cbegin();
|
||||
bool inPct = false;
|
||||
string width;
|
||||
for (; pos != format.cend(); ++pos) {
|
||||
if (!inPct && pos[0] == '%') {
|
||||
inPct = true;
|
||||
width = "";
|
||||
} else if (!inPct) { // Normal text
|
||||
result += *pos;
|
||||
} else { // Format character
|
||||
if (std::isdigit(pos[0])) {
|
||||
width += pos[0];
|
||||
continue;
|
||||
}
|
||||
|
||||
inPct = false;
|
||||
|
||||
if (V3Number::displayedFmtLegal(tolower(pos[0]), false)) {
|
||||
if (V3Number::displayedFmtLegal(std::tolower(pos[0]), false)) {
|
||||
AstNode* const argp = nextArgp;
|
||||
nextArgp = nextArgp->nextp();
|
||||
AstConst* const constp = fetchConstNull(argp);
|
||||
|
|
@ -1091,10 +1108,10 @@ private:
|
|||
nodep, "Argument for $display like statement is not constant");
|
||||
break;
|
||||
}
|
||||
const string pformat = std::string{"%"} + pos[0];
|
||||
const string pformat = std::string{"%"} + width + pos[0];
|
||||
result += constp->num().displayed(nodep, pformat);
|
||||
} else {
|
||||
switch (tolower(pos[0])) {
|
||||
switch (std::tolower(pos[0])) {
|
||||
case '%': result += "%"; break;
|
||||
case 'm':
|
||||
// This happens prior to AstScope so we don't
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ class SliceVisitor final : public VNVisitor {
|
|||
// STATE
|
||||
AstNode* m_assignp = nullptr; // Assignment we are under
|
||||
bool m_assignError = false; // True if the current assign already has an error
|
||||
bool m_okInitArray = false; // Allow InitArray children
|
||||
|
||||
// METHODS
|
||||
AstNodeExpr* cloneAndSel(AstNode* nodep, int elements, int offset) {
|
||||
|
|
@ -163,8 +164,14 @@ class SliceVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
void visit(AstConsPackUOrStruct* nodep) override {
|
||||
VL_RESTORER(m_okInitArray);
|
||||
m_okInitArray = true;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstInitArray* nodep) override {
|
||||
UASSERT_OBJ(!m_assignp, nodep, "Array initialization should have been removed earlier");
|
||||
UASSERT_OBJ(!m_assignp || m_okInitArray, nodep,
|
||||
"Array initialization should have been removed earlier");
|
||||
}
|
||||
|
||||
void expandBiOp(AstNodeBiop* nodep) {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ class StatsReport final {
|
|||
os << " " << V3Options::version() << '\n';
|
||||
os << " Arguments: " << v3Global.opt.allArgsString() << '\n';
|
||||
os << " Build jobs: " << v3Global.opt.buildJobs() << '\n';
|
||||
os << " Verilate jobs: " << v3Global.opt.verilateJobs() << '\n';
|
||||
os << '\n';
|
||||
}
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue