Merge from master for release.

This commit is contained in:
Wilson Snyder 2023-03-04 11:29:11 -05:00
commit aaaf8e75af
570 changed files with 10839 additions and 4215 deletions

View File

@ -15,7 +15,7 @@
cmake_minimum_required(VERSION 3.15)
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
project(Verilator
VERSION 5.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
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,12 +5,12 @@
# General Public License Version 3 or the Perl Artistic License Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
# When releasing, also update header of Changes file
# When releasing, also update header of Changes file,
# and 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])

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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{"&gt;"};
} else if (c == '&') {
out += std::string{"&amp;"};
} 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");

View File

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

View File

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

View File

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

View File

@ -19,7 +19,7 @@
#include "verilatedos.h"
#include <stddef.h>
#include <cstddef>
#include <tuple>
#include <type_traits>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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