Merge from master for release.

This commit is contained in:
Wilson Snyder 2025-02-24 18:32:52 -05:00
commit ac3f30ed6c
252 changed files with 10648 additions and 1820 deletions

View File

@ -9,7 +9,7 @@ assignees: ''
Thanks for taking the time to report this.
Can you attach an example that shows the issue? (Must be openly licensed, ideally in test_regress format.)
Can you attach an example that shows the issue? (Must be openly licensed, completely self-contaned so can directly run what you provide. Ideally use test_regress format, see https://veripool.org/guide/latest/contributing.html?highlight=test_regress#reporting-bugs
What 'verilator' command line do we use to run your example?

View File

@ -16,14 +16,14 @@ cmake_minimum_required(VERSION 3.15)
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
project(
Verilator
VERSION 5.032
VERSION 5.034
HOMEPAGE_URL https://verilator.org
LANGUAGES CXX
)
# Set default build type to Release if not specified
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type" FORCE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type" FORCE)
endif()
option(

59
Changes
View File

@ -8,6 +8,65 @@ The changes in each Verilator version are described below. The
contributors that suggested a given feature are shown in []. Thanks!
Verilator 5.034 2025-02-24
==========================
**Important:**
* This is likely the last version to support Ubuntu 20.04, which is at end-of-life.
* Add expression coverage (#4677) (#5719). [Todd Strader]
**Other:**
* Add `COVERIGN` warning, as a more specific UNSUPPORTED error.
* Add `--public-ignore` to ignore public metacomments (#5716). [Andrew Nolte]
* Add `--preproc-token-limit` (#5768). [Krzysztof Bieganski, Antmicro Ltd.]
* Add lint error on importing package within a class (#5634) (#5679). [Nick Brereton]
* Add `--preproc-resolve` for modules in preprocessor output (#5789). [Kamil Rakoczy, Antmicro Ltd.]
* Support multidimensional array access via VPI (#2812) (#5573). [Krzysztof Starecki]
* Support nested classes (#4178) (#5778). [Kamil Rakoczy, Antmicro Ltd.]
* Support VPI array accessors (#5612). [Moubarak Jeje]
* Support unpacked structure constrained randomization (#5657) (#5759). [Yilou Wang]
* Support generated classes (#5665). [Shou-Li Hsu]
* Support constraints on associative array user-defined keys (#5671) (#5729). [Yilou Wang]
* Support `+incdir` with multiple directories.
* Support integer atom type ports in `--hierarchical` (#5748). [Bartłomiej Chmiel, Antmicro Ltd.]
* Support constrained random for arrays in structs (#5765) (#5802). [Yilou Wang]
* Support selects on arbitrary string expressions (#5773). [Krzysztof Bieganski, Antmicro Ltd.]
* Support side effects of form 'variable[index_function()]++'.
* Improve hierarchical DPI wrapper scheduling performance (#2583) (#5734). [Bartłomiej Chmiel, Antmicro Ltd.]
* Improve VPI write errors (#5712). [Andrew Nolte]
* Improve `resetall support (#5728) (#5730). [Ethan Sifferman]
* Optimize labels as final `if` block statements (#5744).
* Optimize empty function definition bodies (#5750).
* Optimize splitting trigger computation and dump (#5798). [Geza Lore]
* Fix error message when call task as a function (#3089). [Matthew Ballance]
* Fix force VPI public signal visibility (#5225). [Frédéric Requin]
* Fix VPI iteration over hierarchy (#5314) (#5731). [Natan Kreimer]
* Fix constrained random for > 64-bit associative arrays (#5670) (#5682). [Yilou Wang]
* Fix mis-optimizing away `$urandom` (#5703). [Parker Schless]
* Fix packages with `--public-depth 1` (#5708). [Andrew Nolte]
* Fix V3Simulate constant reuse (#5709). [Geza Lore]
* Fix man pages what-is section (#5710). [Ahmed El-Mahmoudy]
* Fix pattern assignment to real inside struct (#5713).
* Fix %p format output for real inside struct (#5713).
* Fix segfault when only enum value referenced in package (#5714). [Dan Katz]
* Fix `BLKSEQ` on suspendable processes (#5722). [Krzysztof Bieganski, Antmicro Ltd.]
* Fix vpiDefName issues with non-inlined scopes and dpi conflicts (#5732). [Andrew Nolte]
* Fix inlined expression with assignment under LHS of NBA (#5736) (#5740). [Geza Lore]
* Fix duplicate-named class variable equivalence (#5737).
* Fix `-j` ignored after `-f` (#5749). [Luca Colagrande]
* Fix handling forced assigns in V3Life (#5757). [Bartłomiej Chmiel, Antmicro Ltd.]
* Fix VFileContent reference count (#5769) (#5771). [Dave Sargeant]
* Fix ignoring joins in stringify in preprocessor (#5777). [Krzysztof Bieganski, Antmicro Ltd.]
* Fix unpacked split_var (#5782) (#5785). [Yutetsu TAKATSUKASA]
* Fix time import error on time parameters (#5786). [Luca Colagrande]
* Fix dpi context functions (#5788). [Ryszard Rozak, Antmicro Ltd.]
* Fix `$monitor` with dotted references (#5794). [Ahmed Elzeftawi]
* Fix parsing input wire with default and range (#5800). [RJ Cunningham]
* Fix matching language extension options including dots.
Verilator 5.032 2025-01-01
==========================

View File

@ -192,7 +192,7 @@ examples: all_nomsg
# Docs
.PHONY: docs
docs: info
docs: info $(VL_INST_MAN_FILES)
info: $(INFOS)
@ -201,7 +201,8 @@ verilator.1: ${srcdir}/bin/verilator
verilator_coverage.1: ${srcdir}/bin/verilator_coverage
pod2man $< $@
%.1: ${srcdir}/bin/%
help2man --no-info --no-discard-stderr --version-string=- $< -o $@
help2man --no-info --no-discard-stderr --version-string=- \
-n "$(shell $< --help | head -n 3 | tail -n 1)" $< -o $@
.PHONY: verilator.html
verilator.html:

View File

@ -269,8 +269,7 @@ __END__
=head1 NAME
Verilator - Translate and simulate SystemVerilog code using C++/SystemC
Verilator - Lint, compile and simulate SystemVerilog code using C++/SystemC
=head1 SYNOPSIS
@ -329,6 +328,8 @@ detailed descriptions of these arguments.
--compiler-include Include additional header in the precompiled one
--converge-limit <loops> Tune convergence settle time
--coverage Enable all coverage
--coverage-expr Enable expression coverage
--coverage-expr-max <value> Maximum permutations allowed for an expression
--coverage-line Enable line coverage
--coverage-max-width <width> Maximum array depth for coverage
--coverage-toggle Enable toggle coverage
@ -375,8 +376,8 @@ detailed descriptions of these arguments.
--gdb Run Verilator under GDB interactively
--gdbbt Run Verilator under GDB for backtrace
--generate-key Create random key for --protect-key
--getenv <var> Get environment variable with defaults
--get-supported <feature> Get if feature is supported
--getenv <var> Get environment variable with defaults
--help Show this help
--hierarchical Enable hierarchical Verilation
--hierarchical-params-file <name> Internal option that specifies parameters file for hier blocks
@ -386,6 +387,11 @@ detailed descriptions of these arguments.
--inline-mult <value> Tune module inlining
--instr-count-dpi <value> Assumed dynamic instruction count of DPI imports
-j <jobs> Parallelism for --build-jobs/--verilate-jobs
--no-json-edit-nums Don't dump editNum in .tree.json files
--no-json-ids Don't use short identifiers instead of adresses/paths in .tree.json
--json-only Create JSON parser output (.tree.json and .meta.json)
--json-only-output .tree.json output filename
--json-only-meta-output .tree.meta.json output filename
--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
@ -422,6 +428,8 @@ detailed descriptions of these arguments.
--pipe-filter <command> Filter all input through a script
--pp-comments Show preprocessor comments with -E
--prefix <topname> Name of top-level class
--preproc-resolve Include all found modules in the output with -E
--preproc-token-limit Maximum tokens on a line allowed by preprocessor
--private Debugging; see docs
--prof-c Compile C++ code with profiling
--prof-cfuncs Name functions for profiling
@ -432,8 +440,9 @@ detailed descriptions of these arguments.
--protect-lib <name> Create a DPI protected library
--public Mark signals as public; see docs
--public-depth <level> Mark public to specified module depth
--public-params Mark all parameters as public_flat
--public-flat-rw Mark all variables, etc as public_flat_rw
--public-ignore Ignore all public comment markings
--public-params Mark all parameters as public_flat
-pvalue+<name>=<value> Overwrite toplevel parameter
--quiet Minimize additional printing
--quiet-exit Don't print the command on failure
@ -506,11 +515,6 @@ detailed descriptions of these arguments.
--x-assign <mode> Assign non-initial Xs to this value
--x-initial <mode> Assign initial Xs to this value
--x-initial-edge Enable initial X->0 and X->1 edge triggers
--no-json-edit-nums Don't dump editNum in .tree.json files
--no-json-ids Don't use short identifiers instead of adresses/paths in .tree.json
--json-only Create JSON parser output (.tree.json and .meta.json)
--json-only-output .tree.json output filename
--json-only-meta-output .tree.meta.json output filename
--xml-only Create XML parser output
--xml-output XML output filename
-y <dir> Directory to search for modules

View File

@ -521,7 +521,7 @@ def write_vcd(filename):
parser = argparse.ArgumentParser(
allow_abbrev=False,
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""Create Gantt chart of multi-threaded execution.
description="""Create Gantt chart of Verilator multi-threaded execution
Verilator_gantt creates a visual representation to help analyze Verilator
#xmultithreaded simulation performance, by showing when each macro-task

View File

@ -164,7 +164,7 @@ def profcfunc(filename):
parser = argparse.ArgumentParser(
allow_abbrev=False,
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""Read gprof report created with --prof-cfuncs.
description="""Read gprof report created with Verilator --prof-cfuncs
Verilator_profcfunc reads a profile report created by gprof. The names of
the functions are then transformed, assuming the user used Verilator's

View File

@ -10,7 +10,7 @@
# Then 'make maintainer-dist'
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[5.032 2025-01-01],
AC_INIT([Verilator],[5.034 2025-02-24],
[https://verilator.org],
[verilator],[https://verilator.org])
@ -478,6 +478,7 @@ m4_foreach([cflag],[
[-Wno-parentheses-equality],
[-Wno-shadow],
[-Wno-sign-compare],
[-Wno-subobject-linkage],
[-Wno-tautological-bitwise-compare],
[-Wno-tautological-compare],
[-Wno-uninitialized],
@ -538,7 +539,7 @@ _MY_LDLIBS_CHECK_IFELSE(
[if test "$CFG_WITH_TCMALLOC" != "no"; then
CFG_LIBS="$LTCMALLOC $CFG_LIBS";
# If using tcmalloc, add some extra options to make the compiler not assume
# it is using it's own versions of the standard library functions
# it is using its own versions of the standard library functions
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-malloc)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-calloc)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-realloc)

View File

@ -7,8 +7,8 @@ This contributing document contains some suggestions that may make
contributions flow more efficiently.
Did you find a bug?
-------------------
Did you find a Verilator bug?
-----------------------------
- Please **ensure the bug was not already reported** by searching
`Verilator Issues <https://verilator.org/issues>`__.
@ -28,8 +28,8 @@ Did you find a bug?
<https://github.com/verilator/verilator/blob/master/docs/internals.rst>`__.
Did you write a patch that fixes a bug?
---------------------------------------
Did you write a patch that fixes a Verilator bug?
-------------------------------------------------
- Please `Open a new Verilator issue <https://verilator.org/issues/new>`__
if there is not one already describing the bug.
@ -72,8 +72,8 @@ Did you write a patch that fixes a bug?
- Most important is we get your patch.
Do you have questions?
----------------------
Do you have questions on Verilator?
-----------------------------------
- Please see FAQ section and rest of the `Verilator
manual <https://verilator.org/verilator_doc.html>`__, or `Verilator

View File

@ -38,9 +38,11 @@ Daniel Bates
David Horton
David Ledger
David Metz
Dave Sargeant
David Stanford
David Turner
Dercury
Diego Roux
Don Williamson
Drew Ranck
Drew Taussig
@ -99,6 +101,7 @@ Jesse Taube
Jevin Sweval
Jiacheng Qian
Jiamin Zhu
Jitesh Nayak
Jinyan Xu
Jiuyang Liu
Joey Liu
@ -155,10 +158,13 @@ Miodrag Milanović
Mladen Slijepcevic
Morten Borup Petersen
Mostafa Gamal
Moubarak Jeje
Nandu Raj
Natan Kreimer
Nathan Graybeal
Nathan Kohagen
Nathan Myers
Nick Brereton
Nolan Poe
Oleh Maksymenko
Patrick Stewart
@ -210,6 +216,7 @@ Todd Strader
Tomasz Gorochowik
Topa Topino
Toru Niina
Trung Nguyen
Tudor Timi
Tymoteusz Blazejczyk
Udi Finkelstein

View File

@ -237,7 +237,17 @@ Summary:
.. option:: --coverage
Enables all forms of coverage, an alias for :vlopt:`--coverage-line`
:vlopt:`--coverage-toggle` :vlopt:`--coverage-user`.
:vlopt:`--coverage-toggle` :vlopt:`--coverage-expr` :vlopt:`--coverage-user`.
.. option:: --coverage-expr
Enables expression coverage analysis. See :ref:`Expression Coverage`.
.. option:: --coverage-expr-max <value>
Rarely needed. Specifies the maximum number of permutations able to be
covered for a given expression. Defaults to 32. Increasing may slow
coverage simulations and make analyzing the results unwieldy.
.. option:: --coverage-line
@ -480,8 +490,8 @@ Summary:
out. Beware of enabling debugging messages, as they will also go to
standard out. See :vlopt:`--no-std`, which is implied by this.
See also :vlopt:`--dump-defines`, :vlopt:`-P`, and
:vlopt:`--pp-comments` options.
See also :vlopt:`--dump-defines`, :vlopt:`-P`, :vlopt:`--pp-comments`
and :vlopt:`--preproc-resolve` options.
.. option:: --emit-accessors
@ -751,7 +761,10 @@ Summary:
.. option:: +incdir+<dir>
See :vlopt:`-y`.
See :vlopt:`-y`. Unlike with :vlopt:`-y`, multiple directories may be
specified separated with a `+` symbol; this is for Verilog-XL
compatibility and is not recommended usage as this is not supported by
some third-party tools.
.. option:: --inline-mult <value>
@ -779,6 +792,38 @@ Summary:
the number of threads in the current hardware. Otherwise, must be a
positive integer specifying the maximum number of parallel build jobs.
.. option:: --json-only
Create JSON output only, do not create any other output.
The JSON format is intended to be used to leverage Verilator's parser and
elaboration to feed to other downstream tools. For details on the format, see
the Verilator Internals manual. Be aware that the JSON
format is still evolving; there will be some changes in future versions.
This option disables some more aggressive transformations and dumps only
the final state of the AST. For more granular and unaltered dumps, meant
mainly for debugging see :vlopt:`--dump-tree-json`.
.. option:: --json-only-meta-output <filename>
Specifies the filename for the metadata output file (`.tree.meta.json`) of --json-only.
Using this option automatically sets :vlopt:`--json-only`.
.. option:: --json-only-output <filename>
Specifies the filename for the main output file (`.tree.json`) of --json-only.
Using this option automatically sets :vlopt:`--json-only`.
.. option:: --no-json-edit-nums
Don't dump edit number in .tree.json files. This may make the file more
run-to-run stable for easier comparison.
.. option:: --no-json-ids
Don't use short identifiers instead of addresses/paths in .tree.json.
.. option:: --l2-name <value>
Instead of using the module name when showing Verilog scope, use the
@ -1107,6 +1152,20 @@ Summary:
prepended to the name of the :vlopt:`--top` option, or V prepended to
the first Verilog filename passed on the command line.
.. option:: --preproc-resolve
With :vlopt:`-E`, resolve referenced instance modules, to include
preprocessed output of submodules. Used to convert a multi-file design
into a single output file.
See :vlopt:`-E`.
.. option:: --preproc-token-limit <value>
Rarely needed. Configure the limit of the number of tokens Verilator
can process on a single line to prevent infinite loops and other hangs.
Defaults to 40000 tokens.
.. option:: --private
Opposite of :vlopt:`--public`. This is the default; this option exists for
@ -1208,6 +1267,13 @@ Summary:
module specifically enabled it with
:option:`/*verilator&32;inline_module*/`.
.. option:: --public-depth <level>
Enables public as with :vlopt:`--public-flat-rw`, but only to the specified depth of modules.
It operates at the module maximum level, so if a module's cells are A.B.X and A.X, the
a --public-depth 3 must be used to make module X public, and both A.B.X and A.X will be public.
.. option:: --public-flat-rw
Declares all variables, ports, and wires public as if they had
@ -1219,11 +1285,12 @@ Summary:
marking only those signals that need public_flat_rw is typically
significantly better performing.
.. option:: --public-depth <level>
.. option:: --public-ignore
Enables public as with :vlopt:`--public-flat-rw`, but only to the specified depth of modules.
It operates at the module maximum level, so if a module's cells are A.B.X and A.X, the
a --public-depth 3 must be used to make module X public, and both A.B.X and A.X will be public.
Ignore all :code:`/*verilator public* */` metacomments. This is useful
for speed-optimizing VPI builds where VPI is not being used. This only
affects metacomments; options such as :vlopt:`--public`,
:vlopt:`--public-depth`, etc. work normally.
.. option:: --public-params
@ -1231,6 +1298,7 @@ Summary:
:code:`/*verilator public_flat_rd*/`
metacomments.
.. option:: -pvalue+<name>=<value>
Overwrites the given parameter(s) of the top-level module. See
@ -1881,38 +1949,6 @@ Summary:
iterations. This may be another indication of problems with the
modeled design that should be addressed.
.. option:: --json-only
Create JSON output only, do not create any other output.
The JSON format is intended to be used to leverage Verilator's parser and
elaboration to feed to other downstream tools. For details on the format, see
the Verilator Internals manual. Be aware that the JSON
format is still evolving; there will be some changes in future versions.
This option disables some more aggressive transformations and dumps only
the final state of the AST. For more granular and unaltered dumps, meant
mainly for debugging see :vlopt:`--dump-tree-json`.
.. option:: --json-only-meta-output <filename>
Specifies the filename for the metadata output file (`.tree.meta.json`) of --json-only.
Using this option automatically sets :vlopt:`--json-only`.
.. option:: --json-only-output <filename>
Specifies the filename for the main output file (`.tree.json`) of --json-only.
Using this option automatically sets :vlopt:`--json-only`.
.. option:: --no-json-edit-nums
Don't dump edit number in .tree.json files. This may make the file more
run-to-run stable for easier comparison.
.. option:: --no-json-ids
Don't use short identifiers instead of addresses/paths in .tree.json.
.. option:: --xml-only
Create XML output only, do not create any other output.
@ -2142,6 +2178,13 @@ The grammar of configuration commands is as follows:
:option:`/*verilator&32;public_flat*/`, etc., metacomments. See
also :ref:`VPI Example`.
.. option:: profile_data -hier-dpi "<function_name>" -cost <cost_value>
Internal profiling data inserted during :vlopt:`--hierarchical`; specifies
execution cost of a hierarchical DPI wrappers for modules with
:option:`/*verilator&32;hier_block*/` metacomment. See
:ref:`Hierarchical Verilation`.
.. option:: profile_data -mtask "<mtask_hash>" -cost <cost_value>
Feeds profile-guided optimization data into the Verilator algorithms in

View File

@ -58,7 +58,7 @@ 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.
Points are children of each line coverage- branches or toggle points.
Points are children of each line coverage- branches, expressions or toggle points.
When point counts are aggregated into a line, the minimum and maximum counts
are used to determine the status of the line (complete, partial, failing)
The count is equal to the maximum of the points.

View File

@ -270,6 +270,42 @@ A :option:`/*verilator&32;coverage_off*/`
signals that do not need toggle analysis, such as RAMs and register files.
.. _Expression Coverage:
Expression Coverage
-------------------
With :vlopt:`--coverage` or :vlopt:`--coverage-expr`, Verilator will
automatically add coverage analysis at each expression, indicating with a
truth table how every Boolean truth-table possibility in the expression
occurred.
Multi-bit expressions are ignored, but sub-expressions with are entirely
Boolean are analyzed. Expression coverage does not fully explore the truth
table of an expression, rather is looks at each term's contribution. E.g.
an AND operation will check coverage for TT, XF and FX.
Some expressions may produce too many cover points. Verilator limits the
maximum number of cover points per expression to 32, but this may be
controlled with :vlopt:`--coverage-expr-max`.
Below is an example showing expression coverage produced from `verilator_coverage`
as applied to the condition expression inside an if statement. Each line
shows the minimum number of terms and their values (e.g. `(t1==0 && t2==1)`) needed
to reach a result for the overall expression (e.g. `=> 1`). Each line also
shows the number of times this combination was hit. Note that individual lines
are not mutually exclusive.
.. code-block::
%000004 if ((~t1 && t2) || (~t3 && t4)) $write("");
-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t
-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t
-000004 point: comment=(t1==1 && t4==0) => 0 hier=top.t
-000002 point: comment=(t2==0 && t3==1) => 0 hier=top.t
-000003 point: comment=(t2==0 && t4==0) => 0 hier=top.t
-000002 point: comment=(t3==0 && t4==1) => 1 hier=top.t
.. _Suppressing Coverage:
Suppressing Coverage

View File

@ -44,7 +44,7 @@ Warnings may be disabled in multiple ways:
.. code-block:: sv
lint_off -rule UNSIGNED -file "*/example.v" -line 1
lint_off -rule UNSIGNED -file "*/example.v" -lines 1
Error And Warning Format
@ -445,6 +445,18 @@ List Of Warnings
correctly.
.. option:: COVERIGN
Warns that Verilator does not support certain forms of
:code:`covergroup`, :code:`coverpoint`, and coverage options, and the
construct was are ignored.
Disabling the :option:`UNSUPPORTED` error also disables this warning.
Ignoring this warning may make Verilator ignore lint checking on the
construct, and collect coverage data differently from other simulators.
.. option:: DECLFILENAME
.. TODO better example
@ -2009,12 +2021,12 @@ List Of Warnings
.. code-block:: sv
wire _unused_ok = &{1'b0,
wire _unused_ok = 1'b0 && &{1'b0,
sig_not_used_a,
sig_not_used_yet_b, // To be fixed
1'b0};
The reduction AND and constant zeros mean the net will always be zero,
The AND with constant zero mean the net will always be zero,
so won't use simulation runtime. The redundant leading and trailing
zeros avoid syntax errors if there are no signals between them. The
magic name "unused" (controlled by the :vlopt:`--unused-regexp` option)

View File

@ -104,6 +104,7 @@ Eiler
Eivind
El
Elbourne
Elzeftawi
Embecosm
Engstrom
Enzo
@ -362,6 +363,7 @@ Sanguinetti
Sanjay
Sasselli
Scharrer
Schless
Seitz
Shahid
Shahrestani
@ -901,6 +903,7 @@ predefines
prepareClone
prepend
prepended
preproc
preprocess
preprocessed
preprocessing

View File

@ -3910,7 +3910,7 @@ char *pnt = buf;
int len = 0;
/* zero is illegal for a value...it is assumed they start at one */
while (value && len <= 14)
while (value && len < 14)
{
value--;
++len;

View File

@ -1898,7 +1898,7 @@ std::string VL_TO_STRING(CData lhs) { return VL_SFORMATF_N_NX("'h%0x", 0, 8, lhs
std::string VL_TO_STRING(SData lhs) { return VL_SFORMATF_N_NX("'h%0x", 0, 16, lhs); }
std::string VL_TO_STRING(IData lhs) { return VL_SFORMATF_N_NX("'h%0x", 0, 32, lhs); }
std::string VL_TO_STRING(QData lhs) { return VL_SFORMATF_N_NX("'h%0x", 0, 64, lhs); }
std::string VL_TO_STRING(double lhs) { return VL_SFORMATF_N_NX("%d", 0, 64, lhs); }
std::string VL_TO_STRING(double lhs) { return VL_SFORMATF_N_NX("%g", 0, 64, lhs); }
std::string VL_TO_STRING_W(int words, const WDataInP obj) {
return VL_SFORMATF_N_NX("'h%0x", 0, words * VL_EDATASIZE, obj);
}
@ -3289,7 +3289,7 @@ uint32_t VerilatedVarProps::entSize() const VL_MT_SAFE {
case VLVT_UINT16: size = sizeof(SData); break;
case VLVT_UINT32: size = sizeof(IData); break;
case VLVT_UINT64: size = sizeof(QData); break;
case VLVT_WDATA: size = VL_WORDS_I(packed().elements()) * sizeof(IData); break;
case VLVT_WDATA: size = VL_WORDS_I(entBits()) * sizeof(IData); break;
default: size = 0; break; // LCOV_EXCL_LINE
}
return size;
@ -3308,7 +3308,7 @@ void* VerilatedVarProps::datapAdjustIndex(void* datap, int dim, int indx) const
uint8_t* bytep = reinterpret_cast<uint8_t*>(datap);
// If on index 1 of a 2 index array, then each index 1 is index2sz*entsz
size_t slicesz = entSize();
for (int d = dim + 1; d <= m_udims; ++d) slicesz *= elements(d);
for (int d = dim + 1; d <= udims(); ++d) slicesz *= elements(d);
bytep += indxAdj * slicesz;
return bytep;
}
@ -3369,32 +3369,30 @@ void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) VL_
}
void VerilatedScope::varInsert(int finalize, const char* namep, void* datap, bool isParam,
VerilatedVarType vltype, int vlflags, int dims, ...) VL_MT_UNSAFE {
VerilatedVarType vltype, int vlflags, int udims,
int pdims...) VL_MT_UNSAFE {
// Grab dimensions
// In the future we may just create a large table at emit time and
// statically construct from that.
if (!finalize) return;
if (!m_varsp) m_varsp = new VerilatedVarNameMap;
VerilatedVar var(namep, datap, vltype, static_cast<VerilatedVarFlags>(vlflags), dims, isParam);
VerilatedVar var(namep, datap, vltype, static_cast<VerilatedVarFlags>(vlflags), udims, pdims,
isParam);
va_list ap;
va_start(ap, dims);
for (int i = 0; i < dims; ++i) {
va_start(ap, pdims);
for (int i = 0; i < udims; ++i) {
const int msb = va_arg(ap, int);
const int lsb = va_arg(ap, int);
if (i == 0) {
var.m_packed.m_left = msb;
var.m_packed.m_right = lsb;
} else if (i >= 1 && i <= var.udims()) {
var.m_unpacked[i - 1].m_left = msb;
var.m_unpacked[i - 1].m_right = lsb;
} else {
// We could have a linked list of ranges, but really this whole thing needs
// to be generalized to support structs and unions, etc.
const std::string msg = "Unsupported multi-dimensional public varInsert: "s + namep;
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
}
var.m_unpacked[i].m_left = msb;
var.m_unpacked[i].m_right = lsb;
}
for (int i = 0; i < pdims; ++i) {
const int msb = va_arg(ap, int);
const int lsb = va_arg(ap, int);
var.m_packed[i].m_left = msb;
var.m_packed[i].m_right = lsb;
}
va_end(ap);

View File

@ -175,6 +175,9 @@ enum class VerilatedAssertDirectiveType : uint8_t {
using VerilatedAssertType_t = std::underlying_type<VerilatedAssertType>::type;
using VerilatedAssertDirectiveType_t = std::underlying_type<VerilatedAssertDirectiveType>::type;
// Type trait for custom struct
template <typename>
struct VlIsCustomStruct : public std::false_type {};
//=============================================================================
// Utility functions
@ -726,7 +729,7 @@ public: // But internals only - called from VerilatedModule's
const Type& type) VL_MT_UNSAFE;
void exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE;
void varInsert(int finalize, const char* namep, void* datap, bool isParam,
VerilatedVarType vltype, int vlflags, int dims, ...) VL_MT_UNSAFE;
VerilatedVarType vltype, int vlflags, int udims, int pdims, ...) VL_MT_UNSAFE;
// ACCESSORS
const char* name() const VL_MT_SAFE_POSTINIT { return m_namep; }
const char* identifier() const VL_MT_SAFE_POSTINIT { return m_identifierp; }

View File

@ -289,7 +289,7 @@ static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s,
}
case VLVT_WDATA: {
WDataOutP wdatap = (reinterpret_cast<WDataOutP>(datap));
for (int i = 0; i < VL_WORDS_I(varp->packed().elements()); ++i) d[i] = wdatap[i];
for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) d[i] = wdatap[i];
return;
}
default: // LCOV_EXCL_START // Errored earlier
@ -328,7 +328,7 @@ static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandl
}
case VLVT_WDATA: {
WDataOutP wdatap = (reinterpret_cast<WDataOutP>(datap));
for (int i = 0; i < VL_WORDS_I(varp->packed().elements()); ++i) {
for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) {
d[i].aval = wdatap[i];
d[i].bval = 0;
}
@ -354,7 +354,7 @@ static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecV
case VLVT_UINT64: *(reinterpret_cast<QData*>(datap)) = VL_SET_QII(s[1], s[0]); break;
case VLVT_WDATA: {
WDataOutP wdatap = (reinterpret_cast<WDataOutP>(datap));
for (int i = 0; i < VL_WORDS_I(varp->packed().elements()); ++i) wdatap[i] = s[i];
for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) wdatap[i] = s[i];
return;
}
default: // LCOV_EXCL_START // Errored earlier
@ -376,7 +376,7 @@ static void _vl_svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogic
case VLVT_UINT64: *(reinterpret_cast<QData*>(datap)) = VL_SET_QII(s[1].aval, s[0].aval); break;
case VLVT_WDATA: {
WDataOutP wdatap = (reinterpret_cast<WDataOutP>(datap));
for (int i = 0; i < VL_WORDS_I(varp->packed().elements()); ++i) wdatap[i] = s[i].aval;
for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) wdatap[i] = s[i].aval;
return;
}
default: // LCOV_EXCL_START // Errored earlier

View File

@ -282,6 +282,7 @@ std::string parseNestedSelect(const std::string& nested_select_expr,
indices.push_back(idx);
return name;
}
//======================================================================
// VlRandomizer:: Methods
@ -467,17 +468,27 @@ bool VlRandomizer::parseSolution(std::iostream& f) {
"hex_index contains invalid format");
continue;
}
const long long index = std::stoll(hex_index.substr(start + 2), nullptr, 16);
oss << "[" << index << "]";
std::string trimmed_hex = hex_index.substr(start + 2);
if (trimmed_hex.size() <= 8) { // Small numbers: <= 32 bits
// Convert to decimal and output directly
oss << "[" << std::to_string(std::stoll(trimmed_hex, nullptr, 16)) << "]";
} else { // Large numbers: > 32 bits
// Trim leading zeros and handle empty case
trimmed_hex.erase(0, trimmed_hex.find_first_not_of('0'));
oss << "[" << (trimmed_hex.empty() ? "0" : trimmed_hex) << "]";
}
}
const std::string indexed_name = oss.str();
const auto it = std::find_if(m_arr_vars.begin(), m_arr_vars.end(),
[&indexed_name](const auto& entry) {
return entry.second->m_name == indexed_name;
});
if (it != m_arr_vars.end()) {
const auto iti = std::find_if(m_arr_vars.begin(), m_arr_vars.end(),
[&indexed_name](const auto& entry) {
return entry.second->m_name == indexed_name;
});
if (iti != m_arr_vars.end()) {
std::ostringstream ss;
ss << "#x" << std::hex << std::setw(8) << std::setfill('0') << it->second->m_index;
ss << "#x" << std::hex << std::setw(8) << std::setfill('0')
<< iti->second->m_index;
idx = ss.str();
} else {
VL_FATAL_MT(__FILE__, __LINE__, "randomize",
@ -498,7 +509,7 @@ void VlRandomizer::clear() { m_constraints.clear(); }
#ifdef VL_DEBUG
void VlRandomizer::dump() const {
for (const auto& var : m_vars) {
VL_PRINTF("Variable (%d): %s\n", var.second->width(), var.second->name());
VL_PRINTF("Variable (%d): %s\n", var.second->width(), var.second->name().c_str());
}
for (const std::string& c : m_constraints) VL_PRINTF("Constraint: %s\n", c.c_str());
}

View File

@ -27,8 +27,10 @@
#include "verilated.h"
#include <iomanip>
#include <iostream>
#include <ostream>
#include <sstream>
//=============================================================================
// VlRandomExpr and subclasses represent expressions for the constraint solver.
@ -38,10 +40,10 @@ public:
m_name; // Name of the array variable, including index notation (e.g., arr[2][1])
void* const m_datap; // Reference to the array variable data
const int m_index; // Flattened (1D) index of the array element
const std::vector<size_t> m_indices; // Multi-dimensional indices of the array element
const std::vector<IData> m_indices; // Multi-dimensional indices of the array element
const std::vector<size_t> m_idxWidths; // Multi-dimensional indices' bit widths
ArrayInfo(const std::string& name, void* datap, int index, const std::vector<size_t>& indices,
ArrayInfo(const std::string& name, void* datap, int index, const std::vector<IData>& indices,
const std::vector<size_t>& idxWidths)
: m_name(name)
, m_datap(datap)
@ -52,21 +54,22 @@ public:
using ArrayInfoMap = std::map<std::string, std::shared_ptr<const ArrayInfo>>;
class VlRandomVar VL_NOT_FINAL {
const char* const m_name; // Variable name
std::string m_name; // Variable name
void* const m_datap; // Reference to variable data
const int m_width; // Variable width in bits
const int m_dimension; //Variable dimension, default is 0
const std::uint32_t m_randModeIdx; // rand_mode index
public:
VlRandomVar(const char* name, int width, void* datap, int dimension, std::uint32_t randModeIdx)
VlRandomVar(const std::string& name, int width, void* datap, int dimension,
std::uint32_t randModeIdx)
: m_name{name}
, m_datap{datap}
, m_width{width}
, m_dimension{dimension}
, m_randModeIdx{randModeIdx} {}
virtual ~VlRandomVar() = default;
const char* name() const { return m_name; }
std::string name() const { return m_name; }
int width() const { return m_width; }
int dimension() const { return m_dimension; }
virtual void* datap(int idx) const { return m_datap; }
@ -97,7 +100,7 @@ public:
template <typename T>
class VlRandomArrayVarTemplate final : public VlRandomVar {
public:
VlRandomArrayVarTemplate(const char* name, int width, void* datap, int dimension,
VlRandomArrayVarTemplate(const std::string& name, int width, void* datap, int dimension,
std::uint32_t randModeIdx)
: VlRandomVar{name, width, datap, dimension, randModeIdx} {}
void* datap(int idx) const override {
@ -110,16 +113,31 @@ public:
return nullptr;
}
}
void emitSelect(std::ostream& s, const std::vector<size_t>& indices,
void emitHexs(std::ostream& s, const std::vector<IData>& indices, const size_t bit_width,
size_t idx) const {
for (int j = bit_width - 4; j >= 0; j -= 4) {
s << "0123456789abcdef"[(indices[idx] >> j) & 0xf];
}
}
void emitSelect(std::ostream& s, const std::vector<IData>& indices,
const std::vector<size_t>& idxWidths) const {
for (size_t idx = 0; idx < indices.size(); ++idx) s << "(select ";
const size_t num_indices = idxWidths.size();
size_t wide_size = 0;
for (size_t idx = 0; idx < num_indices; ++idx) s << "(select ";
s << name();
for (size_t idx = 0; idx < indices.size(); ++idx) {
s << " #x";
for (size_t idx = 0; idx < num_indices; ++idx) {
const size_t bit_width = idxWidths[idx];
for (int j = bit_width - 4; j >= 0; j -= 4) {
s << "0123456789abcdef"[(indices[idx] >> j) & 0xf];
s << " #x";
const size_t emit_count = (bit_width > 32) ? (idxWidths[idx] / 32) : 1;
for (size_t i = 0; i < emit_count; ++i) {
emitHexs(s, indices, (bit_width > 32) ? 32 : bit_width, wide_size + i);
}
wide_size += (idxWidths[idx] > 32) ? (idxWidths[idx] / 32) : 1;
s << ")";
}
}
@ -129,7 +147,7 @@ public:
const std::string indexed_name = name() + std::to_string(i);
const auto it = m_arrVarsRefp->find(indexed_name);
if (it != m_arrVarsRefp->end()) {
const std::vector<size_t>& indices = it->second->m_indices;
const std::vector<IData>& indices = it->second->m_indices;
const std::vector<size_t>& idxWidths = it->second->m_idxWidths;
emitSelect(s, indices, idxWidths);
} else {
@ -165,7 +183,7 @@ public:
const std::string indexed_name = name() + std::to_string(j);
const auto it = m_arrVarsRefp->find(indexed_name);
if (it != m_arrVarsRefp->end()) {
const std::vector<size_t>& indices = it->second->m_indices;
const std::vector<IData>& indices = it->second->m_indices;
const std::vector<size_t>& idxWidths = it->second->m_idxWidths;
emitSelect(s, indices, idxWidths);
} else {
@ -176,7 +194,6 @@ public:
};
//=============================================================================
// VlRandomizer is the object holding constraints and variable references.
class VlRandomizer final {
// MEMBERS
std::vector<std::string> m_constraints; // Solver-dependent constraints
@ -200,48 +217,93 @@ public:
bool next(VlRNG& rngr);
template <typename T_Key>
typename std::enable_if<std::is_integral<T_Key>::value>::type
process_key(const T_Key& key, std::string& indexed_name, size_t& integral_index,
typename std::enable_if<std::is_integral<T_Key>::value && (sizeof(T_Key) <= 4)>::type
process_key(const T_Key& key, std::string& indexed_name, std::vector<size_t>& integral_index,
const std::string& base_name, size_t& idx_width) {
integral_index = static_cast<size_t>(key);
indexed_name = base_name + "[" + std::to_string(integral_index) + "]";
integral_index.push_back(static_cast<size_t>(key));
indexed_name
= base_name + "[" + std::to_string(integral_index[integral_index.size() - 1]) + "]";
idx_width = sizeof(T_Key) * 8;
}
template <typename T_Key>
typename std::enable_if<std::is_same<T_Key, std::string>::value>::type
process_key(const T_Key& key, std::string& indexed_name, size_t& integral_index,
typename std::enable_if<std::is_integral<T_Key>::value && (sizeof(T_Key) > 4)>::type
process_key(const T_Key& key, std::string& indexed_name, std::vector<size_t>& integral_index,
const std::string& base_name, size_t& idx_width) {
integral_index = string_to_integral(key);
indexed_name = base_name + "[" + std::to_string(integral_index) + "]";
idx_width = 64; // 64-bit mask
constexpr size_t segment_bits = 32;
constexpr T_Key mask = (static_cast<T_Key>(1) << segment_bits) - 1;
integral_index.push_back(static_cast<size_t>(key >> segment_bits));
integral_index.push_back(static_cast<size_t>(key & mask));
std::ostringstream hex_stream;
hex_stream << std::hex << key;
std::string index_string = hex_stream.str();
index_string.erase(0, index_string.find_first_not_of('0'));
index_string = index_string.empty() ? "0" : index_string;
indexed_name = base_name + "[" + index_string + "]";
idx_width = sizeof(T_Key) * 8;
}
template <typename T_Key>
typename std::enable_if<VlIsVlWide<T_Key>::value>::type
process_key(const T_Key& key, std::string& indexed_name, std::vector<size_t>& integral_index,
const std::string& base_name, size_t& idx_width) {
std::ostringstream hex_stream;
for (size_t i = key.size(); i > 0; --i) {
const size_t segment_value = key.at(i - 1);
hex_stream << std::hex << segment_value;
integral_index.push_back(segment_value);
}
std::string index_string = hex_stream.str();
index_string.erase(0, index_string.find_first_not_of('0'));
index_string = index_string.empty() ? "0" : index_string;
indexed_name = base_name + "[" + index_string + "]";
idx_width = key.size() * 32;
}
template <typename T_Key>
typename std::enable_if<std::is_same<T_Key, std::string>::value>::type
process_key(const T_Key& key, std::string& indexed_name, std::vector<size_t>& integral_index,
const std::string& base_name, size_t& idx_width) {
// Convert the input string to its ASCII hexadecimal representation
std::ostringstream oss;
for (unsigned char c : key) {
oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(c);
}
std::string hex_str = oss.str();
// Ensure the hex string is exactly 128 bits (32 hex characters)
hex_str = hex_str.size() > 32 ? hex_str.substr(0, 32)
: std::string(32 - hex_str.size(), '0') + hex_str;
// Split the hex string into 4 segments (32-bit per segment)
integral_index.clear();
for (size_t i = 0; i < hex_str.size(); i += 8) {
integral_index.push_back(std::stoul(hex_str.substr(i, 8), nullptr, 16));
}
indexed_name = base_name + "["
+ (hex_str.find_first_not_of('0') == std::string::npos
? "0"
: hex_str.substr(hex_str.find_first_not_of('0')))
+ "]";
idx_width = 128;
}
template <typename T_Key>
typename std::enable_if<!std::is_integral<T_Key>::value
&& !std::is_same<T_Key, std::string>::value>::type
process_key(const T_Key& key, std::string& indexed_name, size_t& integral_index,
&& !std::is_same<T_Key, std::string>::value
&& !VlIsVlWide<T_Key>::value>::type
process_key(const T_Key& key, std::string& indexed_name, std::vector<size_t>& integral_index,
const std::string& base_name, size_t& idx_width) {
VL_FATAL_MT(__FILE__, __LINE__, "randomize",
"Unsupported: Only integral and string index of associative array is "
"supported currently.");
}
uint64_t string_to_integral(const std::string& str) {
uint64_t result = 0;
for (char c : str) { result = (result << 8) | static_cast<uint64_t>(c); }
#ifdef VL_DEBUG
if (seen_values.count(result) > 0 && seen_values[result] != str)
VL_WARN_MT(__FILE__, __LINE__, "randomize",
"Conflict detected: Different strings mapped to the same 64-bit index.");
seen_values[result] = str;
#endif
return result;
}
template <typename T>
void write_var(T& var, int width, const char* name, int dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
typename std::enable_if<!VlIsCustomStruct<T>::value, void>::type
write_var(T& var, int width, const char* name, int dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
if (m_vars.find(name) != m_vars.end()) return;
// TODO: make_unique once VlRandomizer is per-instance not per-ref
m_vars[name]
@ -281,6 +343,21 @@ public:
record_arr_table(var, name, dimension, {}, {});
}
}
template <typename T, std::size_t... I>
void modifyMembers(T& obj, std::index_sequence<I...>, std::string baseName) {
// Use the indices to access each member via std::get
(void)std::initializer_list<int>{
(write_var(std::get<I>(obj.getMembers(obj)), obj.memberWidth()[I],
(baseName + "." + obj.memberNames()[I]).c_str(), obj.memberDimension()[I]),
0)...};
}
template <typename T>
typename std::enable_if<VlIsCustomStruct<T>::value, void>::type
write_var(T& var, int width, const char* name, int dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
modifyMembers(var, var.memberIndices(), name);
}
int idx;
std::string generateKey(const std::string& name, int idx) {
@ -295,15 +372,16 @@ public:
}
template <typename T>
void record_arr_table(T& var, const std::string name, int dimension,
std::vector<size_t> indices, std::vector<size_t> idxWidths) {
typename std::enable_if<!std::is_class<T>::value, void>::type
record_arr_table(T& var, const std::string name, int dimension, std::vector<IData> indices,
std::vector<size_t> idxWidths) {
const std::string key = generateKey(name, idx);
m_arr_vars[key] = std::make_shared<ArrayInfo>(name, &var, idx, indices, idxWidths);
++idx;
}
template <typename T>
void record_arr_table(VlQueue<T>& var, const std::string name, int dimension,
std::vector<size_t> indices, std::vector<size_t> idxWidths) {
std::vector<IData> indices, std::vector<size_t> idxWidths) {
if ((dimension > 0) && (var.size() != 0)) {
idxWidths.push_back(32);
for (size_t i = 0; i < var.size(); ++i) {
@ -316,7 +394,7 @@ public:
}
template <typename T, std::size_t N_Depth>
void record_arr_table(VlUnpacked<T, N_Depth>& var, const std::string name, int dimension,
std::vector<size_t> indices, std::vector<size_t> idxWidths) {
std::vector<IData> indices, std::vector<size_t> idxWidths) {
if ((dimension > 0) && (N_Depth != 0)) {
idxWidths.push_back(32);
for (size_t i = 0; i < N_Depth; ++i) {
@ -330,20 +408,27 @@ public:
}
template <typename T_Key, typename T_Value>
void record_arr_table(VlAssocArray<T_Key, T_Value>& var, const std::string name, int dimension,
std::vector<size_t> indices, std::vector<size_t> idxWidths) {
std::vector<IData> indices, std::vector<size_t> idxWidths) {
if ((dimension > 0) && (var.size() != 0)) {
for (auto it = var.begin(); it != var.end(); ++it) {
const T_Key& key = it->first;
const T_Value& value = it->second;
std::string indexed_name;
size_t integral_index;
size_t idx_width;
std::vector<size_t> integral_index;
size_t idx_width = 0;
process_key(key, indexed_name, integral_index, name, idx_width);
// Update indices and widths
idxWidths.push_back(idx_width);
indices.push_back(integral_index);
indices.insert(indices.end(), integral_index.begin(), integral_index.end());
record_arr_table(var.at(key), indexed_name, dimension - 1, indices, idxWidths);
// Cleanup indices and widths
idxWidths.pop_back();
indices.pop_back();
indices.resize(indices.size() - integral_index.size());
}
}
}

View File

@ -74,27 +74,41 @@ class VerilatedVarProps VL_NOT_FINAL {
const uint32_t m_magic; // Magic number
const VerilatedVarType m_vltype; // Data type
const VerilatedVarFlags m_vlflags; // Direction
const int m_pdims; // Packed dimensions, 0 = none
const int m_udims; // Unpacked dimensions, 0 = none
VerilatedRange m_packed; // Packed array range
std::vector<VerilatedRange> m_unpacked; // Unpacked array ranges
void initUnpacked(const int* ulims) {
for (int i = 0; i < m_udims; ++i) {
std::vector<VerilatedRange> m_packed; // Packed array ranges
VerilatedRange m_packedDpi; // Flattened packed array range
void initUnpacked(int udims, const int* ulims) {
for (int i = 0; i < udims; ++i) {
const int uleft = ulims ? ulims[2 * i + 0] : 0;
const int uright = ulims ? ulims[2 * i + 1] : 0;
m_unpacked.emplace_back(uleft, uright);
}
}
void initPacked(int pdims, const int* plims) {
int packedSize = 1;
for (int i = 0; i < pdims; ++i) {
const int pleft = plims ? plims[2 * i + 0] : 0;
const int pright = plims ? plims[2 * i + 1] : 0;
m_packed.emplace_back(pleft, pright);
packedSize *= abs(pleft - pright) + 1;
}
if (pdims == 1) {
// Preserve packed array range if the packed component is 1-D
m_packedDpi = m_packed.front();
} else {
m_packedDpi = VerilatedRange{packedSize - 1, 0};
}
}
// CONSTRUCTORS
protected:
friend class VerilatedScope;
VerilatedVarProps(VerilatedVarType vltype, VerilatedVarFlags vlflags, int pdims, int udims)
VerilatedVarProps(VerilatedVarType vltype, VerilatedVarFlags vlflags, int udims, int pdims)
: m_magic{MAGIC}
, m_vltype{vltype}
, m_vlflags{vlflags}
, m_pdims{pdims}
, m_udims{udims} {
initUnpacked(nullptr);
, m_vlflags{vlflags} {
// Only preallocate the ranges
initUnpacked(udims, nullptr);
initPacked(pdims, nullptr);
}
public:
@ -103,35 +117,29 @@ public:
VerilatedVarProps(VerilatedVarType vltype, int vlflags)
: m_magic{MAGIC}
, m_vltype{vltype}
, m_vlflags(VerilatedVarFlags(vlflags)) // Need () or GCC 4.8 false warning
, m_pdims{0}
, m_udims{0} {}
, m_vlflags(VerilatedVarFlags(vlflags)) {} // Need () or GCC 4.8 false warning
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Unpacked, int udims, const int* ulims)
: m_magic{MAGIC}
, m_vltype{vltype}
, m_vlflags(VerilatedVarFlags(vlflags)) // Need () or GCC 4.8 false warning
, m_pdims{0}
, m_udims{udims} {
initUnpacked(ulims);
, m_vlflags(VerilatedVarFlags(vlflags)) { // Need () or GCC 4.8 false warning
initUnpacked(udims, ulims);
}
// With packed
class Packed {};
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Packed, int pl, int pr)
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Packed, int pdims, const int* plims)
: m_magic{MAGIC}
, m_vltype{vltype}
, m_vlflags(VerilatedVarFlags(vlflags)) // Need () or GCC 4.8 false warning
, m_pdims{1}
, m_udims{0}
, m_packed{pl, pr} {}
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Packed, int pl, int pr, Unpacked,
int udims, const int* ulims)
, m_vlflags(VerilatedVarFlags(vlflags)) { // Need () or GCC 4.8 false warning
initPacked(pdims, plims);
}
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Unpacked, int udims, const int* ulims,
Packed, int pdims, const int* plims)
: m_magic{MAGIC}
, m_vltype{vltype}
, m_vlflags(VerilatedVarFlags(vlflags)) // Need () or GCC 4.8 false warning
, m_pdims{1}
, m_udims{udims}
, m_packed{pl, pr} {
initUnpacked(ulims);
, m_vlflags(VerilatedVarFlags(vlflags)) { // Need () or GCC 4.8 false warning
initUnpacked(udims, ulims);
initPacked(pdims, plims);
}
~VerilatedVarProps() = default;
@ -142,41 +150,55 @@ public:
return static_cast<VerilatedVarFlags>(static_cast<int>(m_vlflags) & VLVF_MASK_DIR);
}
uint32_t entSize() const VL_MT_SAFE;
uint32_t entBits() const VL_MT_SAFE {
uint32_t bits = 1;
for (auto it : m_packed) bits *= it.elements();
return bits;
}
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 VL_MT_SAFE { return m_udims; }
int dims() const { return m_pdims + m_udims; }
const VerilatedRange& packed() const VL_MT_SAFE { return m_packed; }
const VerilatedRange& unpacked() const { return m_unpacked[0]; }
// DPI accessors
int udims() const VL_MT_SAFE { return m_unpacked.size(); }
int pdims() const VL_MT_SAFE { return m_packed.size(); }
int dims() const VL_MT_SAFE { return pdims() + udims(); }
const std::vector<VerilatedRange>& packedRanges() const VL_MT_SAFE { return m_packed; }
const std::vector<VerilatedRange>& unpackedRanges() const VL_MT_SAFE { return m_unpacked; }
const VerilatedRange* range(int dim) const VL_MT_SAFE {
if (dim < udims())
return &m_unpacked[dim];
else if (dim < dims())
return &m_packed[dim - udims()];
else
return nullptr;
}
// DPI accessors (with packed dimensions flattened!)
int left(int dim) const VL_MT_SAFE {
return dim == 0 ? m_packed.left()
return dim == 0 ? m_packedDpi.left()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].left()
: 0;
}
int right(int dim) const VL_MT_SAFE {
return dim == 0 ? m_packed.right()
return dim == 0 ? m_packedDpi.right()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].right()
: 0;
}
int low(int dim) const VL_MT_SAFE {
return dim == 0 ? m_packed.low()
return dim == 0 ? m_packedDpi.low()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].low()
: 0;
}
int high(int dim) const VL_MT_SAFE {
return dim == 0 ? m_packed.high()
return dim == 0 ? m_packedDpi.high()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].high()
: 0;
}
int increment(int dim) const {
return dim == 0 ? m_packed.increment()
return dim == 0 ? m_packedDpi.increment()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].increment()
: 0;
}
int elements(int dim) const VL_MT_SAFE {
return dim == 0 ? m_packed.elements()
return dim == 0 ? m_packedDpi.elements()
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].elements()
: 0;
}
@ -208,8 +230,7 @@ public:
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 entBits() const { return m_propsp->entBits(); }
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); }
@ -236,8 +257,8 @@ protected:
friend class VerilatedScope;
// CONSTRUCTORS
VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
VerilatedVarFlags vlflags, int dims, bool isParam)
: VerilatedVarProps{vltype, vlflags, (dims > 0 ? 1 : 0), ((dims > 1) ? dims - 1 : 0)}
VerilatedVarFlags vlflags, int udims, int pdims, bool isParam)
: VerilatedVarProps{vltype, vlflags, udims, pdims}
, m_datap{datap}
, m_namep{namep}
, m_isParam{isParam} {}
@ -246,8 +267,6 @@ public:
~VerilatedVar() = default;
// ACCESSORS
void* datap() const { return m_datap; }
const VerilatedRange& range() const { return packed(); } // Deprecated
const VerilatedRange& array() const { return unpacked(); } // Deprecated
const char* name() const { return m_namep; }
bool isParam() const { return m_isParam; }
};

View File

@ -451,6 +451,7 @@ struct VlWide final {
// METHODS
const EData& at(size_t index) const { return m_storage[index]; }
EData& at(size_t index) { return m_storage[index]; }
size_t size() const { return N_Words; }
WData* data() { return &m_storage[0]; }
const WData* data() const { return &m_storage[0]; }
bool operator<(const VlWide<N_Words>& rhs) const {
@ -504,6 +505,12 @@ public:
VlQueue& operator=(VlQueue&&) = default;
bool operator==(const VlQueue& rhs) const { return m_deque == rhs.m_deque; }
bool operator!=(const VlQueue& rhs) const { return m_deque != rhs.m_deque; }
bool operator<(const VlQueue& rhs) const {
for (int index = 0; index < m_deque.size(); ++index) {
if (m_deque[index] < rhs.m_deque[index]) return true;
}
return false;
}
// Standard copy constructor works. Verilog: assoca = assocb
// Also must allow conversion from a different N_MaxSize queue
@ -947,7 +954,7 @@ public:
VlAssocArray& operator=(VlAssocArray&&) = default;
bool operator==(const VlAssocArray& rhs) const { return m_map == rhs.m_map; }
bool operator!=(const VlAssocArray& rhs) const { return m_map != rhs.m_map; }
bool operator<(const VlAssocArray& rhs) const { return m_map < rhs.m_map; }
// METHODS
T_Value& atDefault() { return m_defaultValue; }
const T_Value& atDefault() const { return m_defaultValue; }
@ -1312,7 +1319,7 @@ public:
WData* data() { return &m_storage[0]; }
const WData* data() const { return &m_storage[0]; }
std::size_t size() const { return N_Depth; }
constexpr std::size_t size() const { return N_Depth; }
// To fit C++14
template <std::size_t N_CurrentDimension = 0, typename U = T_Value>
int find_length(int dimension, std::false_type) const {
@ -1350,7 +1357,7 @@ public:
}
T_Value& operator[](size_t index) { return m_storage[index]; }
const T_Value& operator[](size_t index) const { return m_storage[index]; }
constexpr const T_Value& operator[](size_t index) const { return m_storage[index]; }
// *this != that, which might be used for change detection/trigger computation, but avoid
// operator overloading in VlUnpacked for safety in other contexts.
@ -1363,6 +1370,12 @@ public:
bool neq(const T_Value that[N_Depth]) const { return neq(*this, that); }
void assign(const T_Value that[N_Depth]) { std::copy_n(that, N_Depth, m_storage); }
void operator=(const T_Value that[N_Depth]) { assign(that); }
bool operator<(const VlUnpacked<T_Value, N_Depth>& that) const {
for (int index = 0; index < N_Depth; ++index) {
if (m_storage[index] < that.m_storage[index]) return true;
}
return false;
}
// inside (set membership operator)
bool inside(const T_Value& value) const {

File diff suppressed because it is too large Load Diff

View File

@ -474,6 +474,7 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
#ifndef VL_VALUE_STRING_MAX_WORDS
#define VL_VALUE_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation
#endif
#define VL_VALUE_STRING_MAX_CHARS (VL_VALUE_STRING_MAX_WORDS) * 4
//=========================================================================
// Base macros

View File

@ -349,7 +349,7 @@ public:
class ActiveDlyVisitor final : public VNVisitor {
public:
enum CheckType : uint8_t { CT_SEQ, CT_COMB, CT_INITIAL };
enum CheckType : uint8_t { CT_SEQ, CT_COMB, CT_INITIAL, CT_SUSPENDABLE };
private:
// MEMBERS
@ -358,7 +358,7 @@ private:
// VISITORS
void visit(AstAssignDly* nodep) override {
// Non-blocking assignments are OK in sequential processes
if (m_check == CT_SEQ) return;
if (m_check == CT_SEQ || m_check == CT_SUSPENDABLE) return;
// Issue appropriate warning
if (m_check == CT_INITIAL) {
@ -477,10 +477,8 @@ class ActiveVisitor final : public VNVisitor {
AstActive* const wantactivep
= !m_clockedProcess ? m_namer.getSpecialActive<AstSenItem::Combo>(nodep->fileline())
: oldsensesp ? m_namer.getActive(nodep->fileline(), oldsensesp)
: m_namer.getSpecialActive<AstSenItem::Initial>(nodep->fileline());
// Delete sensitivity list
if (oldsensesp) VL_DO_DANGLING(oldsensesp->deleteTree(), oldsensesp);
// Clocked, no sensitivity lists, it's a suspendable, put it in initial
: m_namer.getSpecialActive<AstSenItem::Initial>(nodep->fileline());
// Move node to new active
nodep->unlinkFrBack();
@ -488,10 +486,14 @@ class ActiveVisitor final : public VNVisitor {
// Warn and convert any delayed assignments
{
ActiveDlyVisitor{nodep, m_clockedProcess ? ActiveDlyVisitor::CT_SEQ
: ActiveDlyVisitor::CT_COMB};
ActiveDlyVisitor{nodep, !m_clockedProcess ? ActiveDlyVisitor::CT_COMB
: oldsensesp ? ActiveDlyVisitor::CT_SEQ
: ActiveDlyVisitor::CT_SUSPENDABLE};
}
// Delete sensitivity list
if (oldsensesp) VL_DO_DANGLING(oldsensesp->deleteTree(), oldsensesp);
// check combinational processes for latches
if (!m_clockedProcess || kwd == VAlwaysKwd::ALWAYS_LATCH) {
const ActiveLatchCheckVisitor latchvisitor{nodep, kwd == VAlwaysKwd::ALWAYS_LATCH};

View File

@ -534,8 +534,9 @@ class AssertVisitor final : public VNVisitor {
while (monExprsp) {
if (AstNodeVarRef* varrefp = VN_CAST(monExprsp, NodeVarRef)) {
AstSenItem* const senItemp
= new AstSenItem(fl, VEdgeType::ET_CHANGED,
new AstVarRef{fl, varrefp->varp(), VAccess::READ});
= new AstSenItem{fl, VEdgeType::ET_CHANGED,
// Clone so get VarRef or VarXRef as needed
varrefp->cloneTree(false)};
if (!monSenItemsp) {
monSenItemsp = senItemp;
} else {

View File

@ -196,6 +196,11 @@ string AstNode::prettyName(const string& namein) VL_PURE {
pos += 7;
continue;
}
if (0 == std::strncmp(pos, "__Viftop", 8)) {
pretty += "";
pos += 8;
continue;
}
if (pos[0] == '_' && pos[1] == '_' && pos[2] == '0' && std::isxdigit(pos[3])
&& std::isxdigit(pos[4])) {
char value = 0;
@ -797,13 +802,7 @@ void AstNode::swapWith(AstNode* bp) {
AstNode* AstNode::cloneTreeIter(bool needPure) {
// private: Clone single node and children
if (VL_UNLIKELY(needPure && !isPure())) {
this->v3warn(SIDEEFFECT,
"Expression side effect may be mishandled\n"
<< this->warnMore()
<< "... Suggest use a temporary variable in place of this expression");
// this->v3fatalSrc("cloneTreePure debug backtrace"); // Comment in to debug where caused
}
if (needPure) purityCheck();
AstNode* const newp = this->clone();
if (this->m_op1p) newp->op1p(this->m_op1p->cloneTreeIterList(needPure));
if (this->m_op2p) newp->op2p(this->m_op2p->cloneTreeIterList(needPure));
@ -850,6 +849,16 @@ AstNode* AstNode::cloneTree(bool cloneNextLink, bool needPure) {
return newp;
}
void AstNode::purityCheck() {
if (VL_UNLIKELY(!isPure())) {
this->v3warn(SIDEEFFECT,
"Expression side effect may be mishandled\n"
<< this->warnMore()
<< "... Suggest use a temporary variable in place of this expression");
// this->v3fatalSrc("cloneTreePure debug backtrace"); // Comment in to debug where caused
}
}
//======================================================================
// Delete

View File

@ -2077,6 +2077,7 @@ private:
string instanceStr() const;
public:
void purityCheck();
static void relinkOneLink(AstNode*& pointpr, AstNode* newp);
// cppcheck-suppress functionConst
static void debugTreeChange(const AstNode* nodep, const char* prefix, int lineno, bool next);

View File

@ -188,8 +188,9 @@ bool AstVarRef::sameNode(const AstVarRef* samep) const {
if (varScopep()) {
return (varScopep() == samep->varScopep() && access() == samep->access());
} else {
return (selfPointer() == samep->selfPointer() && varp()->name() == samep->varp()->name()
&& access() == samep->access());
return (selfPointer() == samep->selfPointer()
&& classOrPackagep() == samep->classOrPackagep()
&& varp()->name() == samep->varp()->name() && access() == samep->access());
}
}
bool AstVarRef::sameNoLvalue(AstVarRef* samep) const {
@ -197,6 +198,7 @@ bool AstVarRef::sameNoLvalue(AstVarRef* samep) const {
return (varScopep() == samep->varScopep());
} else {
return (selfPointer() == samep->selfPointer()
&& classOrPackagep() == samep->classOrPackagep()
&& (!selfPointer().isEmpty() || !samep->selfPointer().isEmpty())
&& varp()->name() == samep->varp()->name());
}

View File

@ -230,6 +230,7 @@ class AstNodeUOrStructDType VL_NOT_FINAL : public AstNodeDType {
const int m_uniqueNum;
bool m_packed;
bool m_isFourstate = false; // V3Width computes
bool m_constrainedRand = false; // True if struct has constraint expression
protected:
AstNodeUOrStructDType(VNType t, FileLine* fl, VSigning numericUnpack)
@ -244,7 +245,8 @@ protected:
, m_name(other.m_name)
, m_uniqueNum(uniqueNumInc())
, m_packed(other.m_packed)
, m_isFourstate(other.m_isFourstate) {}
, m_isFourstate(other.m_isFourstate)
, m_constrainedRand(false) {}
public:
ASTGEN_MEMBERS_AstNodeUOrStructDType;
@ -284,6 +286,8 @@ public:
VNumRange declRange() const VL_MT_STABLE { return VNumRange{hi(), lo()}; }
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
void classOrPackagep(AstNodeModule* classpackagep) { m_classOrPackagep = classpackagep; }
bool isConstrainedRand() { return m_constrainedRand; }
void markConstrainedRand(bool flag) { m_constrainedRand = flag; }
};
// === Concrete node types =====================================================
@ -905,12 +909,14 @@ class AstMemberDType final : public AstNodeDType {
string m_name; // Name of variable
string m_tag; // Holds the string of the verilator tag -- used in XML output.
int m_lsb = -1; // Within this level's packed struct, the LSB of the first bit of the member
bool m_constrainedRand = false;
// UNSUP: int m_randType; // Randomization type (IEEE)
public:
AstMemberDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp,
AstNode* valuep)
: ASTGEN_SUPER_MemberDType(fl)
, m_name{name} {
, m_name{name}
, m_constrainedRand(false) {
childDTypep(dtp); // Only for parser
this->valuep(valuep);
dtypep(nullptr); // V3Width will resolve
@ -918,13 +924,16 @@ public:
}
AstMemberDType(FileLine* fl, const string& name, AstNodeDType* dtp)
: ASTGEN_SUPER_MemberDType(fl)
, m_name{name} {
, m_name{name}
, m_constrainedRand(false) {
UASSERT(dtp, "AstMember created with no dtype");
refDTypep(dtp);
dtypep(this);
widthFromSub(subDTypep());
}
ASTGEN_MEMBERS_AstMemberDType;
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
void dumpSmall(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
bool hasDType() const override VL_MT_SAFE { return true; }
@ -958,6 +967,8 @@ public:
v3fatalSrc("call isCompound on subdata type, not reference");
return false;
}
bool isConstrainedRand() const { return m_constrainedRand; }
void markConstrainedRand(bool flag) { m_constrainedRand = flag; }
};
class AstNBACommitQueueDType final : public AstNodeDType {
// @astgen ptr := m_subDTypep : AstNodeDType // Type of the corresponding variable

View File

@ -1668,7 +1668,7 @@ class AstParseHolder final : public AstNodeExpr {
// A reference to something soon to replace, used in a select at parse time
// that needs conversion to pull the upper lvalue later
public:
AstParseHolder(FileLine* fl)
explicit AstParseHolder(FileLine* fl)
: ASTGEN_SUPER_ParseHolder(fl) {}
ASTGEN_MEMBERS_AstParseHolder;
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
@ -1820,7 +1820,6 @@ public:
return "VL_RANDOM_SEEDED_%nq%lq(%li)";
}
}
if (isWide()) {
return "VL_RANDOM_%nq(%nw, %P)";
} else {
@ -1830,6 +1829,7 @@ public:
bool cleanOut() const override { return false; }
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
bool isPure() override { return !m_reset && !seedp(); }
int instrCount() const override { return INSTR_COUNT_PLI; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool combinable(const AstRand* samep) const {
@ -4151,7 +4151,7 @@ public:
return new AstArraySel{fileline(), lhsp, rhsp};
}
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
V3ERROR_NA; /* How can from be a const? */
V3ERROR_NA; // How can from be a const?
}
string emitVerilog() override { return "%k(%l%f[%r])"; }
string emitC() override { return "%li%k[%ri]"; }
@ -4511,7 +4511,7 @@ public:
// === AstNodeTermop ===
class AstInferredDisable final : public AstNodeTermop {
public:
AstInferredDisable(FileLine* fl)
explicit AstInferredDisable(FileLine* fl)
: ASTGEN_SUPER_InferredDisable(fl) {
dtypeSetLogicSized(1, VSigning::UNSIGNED);
}

View File

@ -625,6 +625,7 @@ class AstCFunc final : public AstNode {
bool m_isTrace : 1; // Function is related to tracing
bool m_dontCombine : 1; // V3Combine shouldn't compare this func tree, it's special
bool m_declPrivate : 1; // Declare it private
bool m_keepIfEmpty : 1; // Keep declaration and definition separate, even if empty
bool m_slow : 1; // Slow routine, called once or just at init time
bool m_funcPublic : 1; // From user public task/function
bool m_isConstructor : 1; // Is C class constructor
@ -643,6 +644,7 @@ class AstCFunc final : public AstNode {
bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code
bool m_needProcess : 1; // Needs access to VlProcess of the caller
bool m_recursive : 1; // Recursive or part of recursion
int m_cost; // Function call cost
public:
AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "")
: ASTGEN_SUPER_CFunc(fl) {
@ -654,6 +656,7 @@ public:
m_isTrace = false;
m_dontCombine = false;
m_declPrivate = false;
m_keepIfEmpty = false;
m_slow = false;
m_funcPublic = false;
m_isConstructor = false;
@ -671,6 +674,7 @@ public:
m_dpiImportPrototype = false;
m_dpiImportWrapper = false;
m_recursive = false;
m_cost = v3Global.opt.instrCountDpi(); // As proxy for unknown general DPI cost
}
ASTGEN_MEMBERS_AstCFunc;
string name() const override VL_MT_STABLE { return m_name; }
@ -685,9 +689,7 @@ public:
}
//
void name(const string& name) override { m_name = name; }
int instrCount() const override {
return dpiImportPrototype() ? v3Global.opt.instrCountDpi() : 0;
}
int instrCount() const override { return m_cost; }
VBoolOrUnknown isConst() const { return m_isConst; }
void isConst(bool flag) { m_isConst.setTrueOrFalse(flag); }
void isConst(VBoolOrUnknown flag) { m_isConst = flag; }
@ -706,6 +708,8 @@ public:
bool dontInline() const { return dontCombine() || slow() || funcPublic(); }
bool declPrivate() const { return m_declPrivate; }
void declPrivate(bool flag) { m_declPrivate = flag; }
bool keepIfEmpty() const VL_MT_SAFE { return m_keepIfEmpty; }
void keepIfEmpty(bool flag) { m_keepIfEmpty = flag; }
bool slow() const VL_MT_SAFE { return m_slow; }
void slow(bool flag) { m_slow = flag; }
bool funcPublic() const { return m_funcPublic; }
@ -746,10 +750,10 @@ public:
bool isCoroutine() const { return m_rtnType == "VlCoroutine"; }
void recursive(bool flag) { m_recursive = flag; }
bool recursive() const { return m_recursive; }
void cost(int cost) { m_cost = cost; }
// Special methods
bool emptyBody() const {
return argsp() == nullptr && initsp() == nullptr && stmtsp() == nullptr
&& finalsp() == nullptr;
return !keepIfEmpty() && !argsp() && !initsp() && !stmtsp() && !finalsp();
}
};
class AstCLocalScope final : public AstNode {
@ -1248,6 +1252,7 @@ public:
, m_name{name} {
this->addVarsp(varsp);
}
string verilogKwd() const override { return "modport"; }
string name() const override VL_MT_STABLE { return m_name; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
ASTGEN_MEMBERS_AstModport;

View File

@ -570,15 +570,28 @@ string AstVar::vlEnumDir() const {
string AstVar::vlPropDecl(const string& propName) const {
string out;
std::vector<int> plims; // Packed dimension limits
std::vector<int> ulims; // Unpacked dimension limits
for (const AstNodeDType* dtp = dtypep(); dtp;) {
dtp = dtp->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
if (const AstNodeArrayDType* const adtypep = VN_CAST(dtp, NodeArrayDType)) {
ulims.push_back(adtypep->declRange().left());
ulims.push_back(adtypep->declRange().right());
dtp = adtypep->subDTypep();
} else {
break; // AstBasicDType - nothing below
if (const AstBasicDType* const bdtypep = basicp()) {
for (const AstNodeDType* dtp = dtypep(); dtp;) {
dtp = dtp->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
if (const AstNodeArrayDType* const adtypep = VN_CAST(dtp, NodeArrayDType)) {
if (VN_IS(dtp, PackArrayDType)) {
plims.push_back(adtypep->declRange().left());
plims.push_back(adtypep->declRange().right());
} else {
ulims.push_back(adtypep->declRange().left());
ulims.push_back(adtypep->declRange().right());
}
dtp = adtypep->subDTypep();
} else {
if (bdtypep->isRanged()) {
plims.push_back(bdtypep->left());
plims.push_back(bdtypep->right());
}
break; // AstBasicDType - nothing below
}
}
}
@ -595,23 +608,37 @@ string AstVar::vlPropDecl(const string& propName) const {
out += "};\n";
}
if (!plims.empty()) {
out += "static const int " + propName + "__plims[";
out += cvtToStr(plims.size());
out += "] = {";
auto it = plims.cbegin();
out += cvtToStr(*it);
while (++it != plims.cend()) {
out += ", ";
out += cvtToStr(*it);
}
out += "};\n";
}
out += "static const VerilatedVarProps ";
out += propName;
out += "(";
out += vlEnumType(); // VLVT_UINT32 etc
out += ", " + vlEnumDir(); // VLVD_IN etc
if (const AstBasicDType* const bdtypep = basicp()) {
out += ", VerilatedVarProps::Packed()";
out += ", " + cvtToStr(bdtypep->left());
out += ", " + cvtToStr(bdtypep->right());
}
if (!ulims.empty()) {
out += ", VerilatedVarProps::Unpacked()";
out += ", VerilatedVarProps::Unpacked{}";
out += ", " + cvtToStr(ulims.size() / 2);
out += ", " + propName + "__ulims";
}
if (!plims.empty()) {
out += ", VerilatedVarProps::Packed{}";
out += ", " + cvtToStr(plims.size() / 2);
out += ", " + propName + "__plims";
}
out += ");\n";
return out;
}
@ -1760,7 +1787,7 @@ void AstClocking::dumpJson(std::ostream& str) const {
}
void AstDisplay::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
// str << " " << displayType().ascii();
str << " [" << displayType().ascii() << "]";
}
void AstDisplay::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
void AstEnumDType::dump(std::ostream& str) const {
@ -1923,6 +1950,20 @@ void AstJumpLabel::dump(std::ostream& str) const {
}
void AstJumpLabel::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
void AstMemberDType::dump(std::ostream& str) const {
this->AstNodeDType::dump(str);
if (isConstrainedRand()) str << " [CONSTRAINEDRAND]";
if (name() != "") str << " name=" << name();
if (tag() != "") str << " tag=" << tag();
}
void AstMemberDType::dumpJson(std::ostream& str) const {
dumpJsonBoolFunc(str, isConstrainedRand);
dumpJsonStrFunc(str, name);
dumpJsonStrFunc(str, tag);
dumpJsonGen(str);
}
void AstMemberDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "member";
@ -2838,15 +2879,17 @@ void AstCFile::dumpJson(std::ostream& str) const {
void AstCFunc::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (slow()) str << " [SLOW]";
if (dpiPure()) str << " [DPIPURE]";
if (isStatic()) str << " [STATIC]";
if (dpiContext()) str << " [DPICTX]";
if (dpiExportDispatcher()) str << " [DPIED]";
if (dpiExportImpl()) str << " [DPIEI]";
if (dpiImportPrototype()) str << " [DPIIP]";
if (dpiImportWrapper()) str << " [DPIIW]";
if (dpiContext()) str << " [DPICTX]";
if (dpiPure()) str << " [DPIPURE]";
if (isConstructor()) str << " [CTOR]";
if (isDestructor()) str << " [DTOR]";
if (isMethod()) str << " [METHOD]";
if (isLoose()) str << " [LOOSE]";
if (isVirtual()) str << " [VIRT]";
if (isCoroutine()) str << " [CORO]";
if (needProcess()) str << " [NPRC]";

View File

@ -148,6 +148,18 @@ class BeginVisitor final : public VNVisitor {
void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
m_modp = nodep;
// Rename it (e.g. class under a generate)
if (m_unnamedScope != "") {
nodep->name(dot(m_unnamedScope, nodep->name()));
UINFO(8, " rename to " << nodep->name() << endl);
m_statep->userMarkChanged(nodep);
}
VL_RESTORER(m_displayScope);
VL_RESTORER(m_namedScope);
VL_RESTORER(m_unnamedScope);
m_displayScope = "";
m_namedScope = "";
m_unnamedScope = "";
iterateChildren(nodep);
}
void visit(AstNodeFTask* nodep) override {

View File

@ -62,6 +62,7 @@ class V3CCtorsBuilder final {
AstCFunc* const funcp = new AstCFunc{m_modp->fileline(), funcName, nullptr, "void"};
funcp->isStatic(false);
funcp->isLoose(!m_type.isClass());
funcp->keepIfEmpty(true); // TODO relax
funcp->declPrivate(true);
funcp->slow(!m_type.isClass()); // Only classes construct on fast path
string preventUnusedStmt;
@ -210,6 +211,7 @@ void V3CCtors::evalAsserts() {
funcp->declPrivate(true);
funcp->isStatic(false);
funcp->isLoose(true);
funcp->keepIfEmpty(true);
funcp->slow(false);
funcp->ifdef("VL_DEBUG");
modp->addStmtsp(funcp);

View File

@ -533,11 +533,13 @@ public:
// Resolve modules and files in the design
class V3ConfigResolver final {
enum ProfileDataMode : uint8_t { NONE = 0, MTASK = 1, HIER_DPI = 2 };
V3ConfigModuleResolver m_modules; // Access to module names (with wildcards)
V3ConfigFileResolver m_files; // Access to file names (with wildcards)
V3ConfigScopeTraceResolver m_scopeTraces; // Regexp to trace enables
std::unordered_map<string, std::unordered_map<string, uint64_t>>
m_profileData; // Access to profile_data records
uint8_t m_mode = NONE;
FileLine* m_profileFileLine = nullptr;
V3ConfigResolver() = default;
@ -552,10 +554,21 @@ public:
V3ConfigFileResolver& files() { return m_files; }
V3ConfigScopeTraceResolver& scopeTraces() { return m_scopeTraces; }
void addProfileData(FileLine* fl, const string& model, const string& key, uint64_t cost) {
void addProfileData(FileLine* fl, const string& hierDpi, uint64_t cost) {
// Empty key for hierarchical DPI wrapper costs.
addProfileData(fl, hierDpi, "", cost, HIER_DPI);
}
void addProfileData(FileLine* fl, const string& model, const string& key, uint64_t cost,
ProfileDataMode mode = MTASK) {
if (!m_profileFileLine) m_profileFileLine = fl;
if (cost == 0) cost = 1; // Cost 0 means delete (or no data)
m_profileData[model][key] += cost;
m_mode |= mode;
}
bool containsMTaskProfileData() const { return m_mode & MTASK; }
uint64_t getProfileData(const string& hierDpi) const {
// Empty key for hierarchical DPI wrapper costs.
return getProfileData(hierDpi, "");
}
uint64_t getProfileData(const string& model, const string& key) const {
const auto mit = m_profileData.find(model);
@ -619,6 +632,10 @@ void V3Config::addModulePragma(const string& module, VPragmaType pragma) {
V3ConfigResolver::s().modules().at(module).addModulePragma(pragma);
}
void V3Config::addProfileData(FileLine* fl, const string& hierDpi, uint64_t cost) {
V3ConfigResolver::s().addProfileData(fl, hierDpi, cost);
}
void V3Config::addProfileData(FileLine* fl, const string& model, const string& key,
uint64_t cost) {
V3ConfigResolver::s().addProfileData(fl, model, key, cost);
@ -724,6 +741,9 @@ void V3Config::applyVarAttr(AstNodeModule* modulep, AstNodeFTask* ftaskp, AstVar
if (vp) vp->apply(varp);
}
uint64_t V3Config::getProfileData(const string& hierDpi) {
return V3ConfigResolver::s().getProfileData(hierDpi);
}
uint64_t V3Config::getProfileData(const string& model, const string& key) {
return V3ConfigResolver::s().getProfileData(model, key);
}
@ -736,6 +756,10 @@ bool V3Config::getScopeTraceOn(const string& scope) {
void V3Config::contentsPushText(const string& text) { return WildcardContents::pushText(text); }
bool V3Config::containsMTaskProfileData() {
return V3ConfigResolver::s().containsMTaskProfileData();
}
bool V3Config::waive(FileLine* filelinep, V3ErrorCode code, const string& message) {
V3ConfigFile* filep = V3ConfigResolver::s().files().resolve(filelinep->filename());
if (!filep) return false;

View File

@ -38,6 +38,7 @@ public:
const string& match);
static void addInline(FileLine* fl, const string& module, const string& ftask, bool on);
static void addModulePragma(const string& module, VPragmaType pragma);
static void addProfileData(FileLine* fl, const string& hierDpi, uint64_t cost);
static void addProfileData(FileLine* fl, const string& model, const string& key,
uint64_t cost);
static void addScopeTraceOn(bool on, const string& scope, int levels);
@ -51,12 +52,15 @@ public:
static void applyModule(AstNodeModule* modulep);
static void applyVarAttr(AstNodeModule* modulep, AstNodeFTask* ftaskp, AstVar* varp);
static uint64_t getProfileData(const string& hierDpi);
static uint64_t getProfileData(const string& model, const string& key);
static FileLine* getProfileDataFileLine();
static bool getScopeTraceOn(const string& scope);
static void contentsPushText(const string& text);
static bool containsMTaskProfileData();
static bool waive(FileLine* filelinep, V3ErrorCode code, const string& message);
};

View File

@ -2266,6 +2266,19 @@ class ConstVisitor final : public VNVisitor {
}
return false;
}
bool replaceJumpGoNext(AstJumpGo* nodep, AstNode* abovep) {
// If JumpGo has an upper JumpBlock that is to same label, then
// code will by normal sequential operation do the JUMPGO and it
// can be removed.
if (nodep->nextp()) return false; // Label jumps other statements
AstJumpBlock* const aboveBlockp = VN_CAST(abovep, JumpBlock);
if (!aboveBlockp) return false;
if (aboveBlockp != nodep->labelp()->blockp()) return false;
if (aboveBlockp->endStmtsp() != nodep->labelp()) return false;
UINFO(4, "JUMPGO => last remove " << nodep << endl);
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
return true;
}
// Boolean replacements
bool operandBoolShift(const AstNode* nodep) {
@ -3158,8 +3171,9 @@ class ConstVisitor final : public VNVisitor {
nodep->condp(new AstLogAnd{lowerIfp->fileline(), condp, lowerCondp});
lowerIfp->replaceWith(lowerThensp);
VL_DO_DANGLING(pushDeletep(lowerIfp), lowerIfp);
} else if (operandBoolShift(nodep->condp())) {
replaceBoolShift(nodep->condp());
} else {
// Optimizations that don't reform the IF itself
if (operandBoolShift(nodep->condp())) replaceBoolShift(nodep->condp());
}
}
}
@ -3377,7 +3391,7 @@ class ConstVisitor final : public VNVisitor {
void visit(AstJumpGo* nodep) override {
iterateChildren(nodep);
// Jump to label where label immediately follows this go is not useful
// Jump to label where label immediately follows this JumpGo is not useful
if (nodep->labelp() == VN_CAST(nodep->nextp(), JumpLabel)) {
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
// Keep the label, might be other jumps pointing to it, gets cleaned later
@ -3390,14 +3404,14 @@ class ConstVisitor final : public VNVisitor {
}
// If last statement in a jump label we have JumpLabel(...., JumpGo)
// Often caused by "return" in a Verilog function. The Go is pointless, remove.
if (replaceJumpGoNext(nodep, nodep->abovep())) return;
// Also optimize If with a then or else's final statement being this JumpGo
// We only do single ifs... Ideally we'd look at control flow and delete any
// Jumps where any following control flow point is the label
if (!nodep->nextp()) {
if (AstJumpBlock* const aboveBlockp = VN_CAST(nodep->abovep(), JumpBlock)) {
if (aboveBlockp == nodep->labelp()->blockp()) {
if (aboveBlockp->endStmtsp() == nodep->labelp()) {
UINFO(4, "JUMPGO => last remove " << nodep << endl);
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
return;
}
if (AstNodeIf* const aboveIfp = VN_CAST(nodep->abovep(), NodeIf)) {
if (!aboveIfp->nextp()) {
if (replaceJumpGoNext(nodep, aboveIfp->abovep())) return;
}
}
}

View File

@ -28,6 +28,9 @@
#include "V3Coverage.h"
#include "V3EmitV.h"
#include <list>
#include <unordered_map>
VL_DEFINE_DEBUG_FUNCTIONS;
@ -39,6 +42,18 @@ class CoverageVisitor final : public VNVisitor {
// TYPES
using LinenoSet = std::set<int>;
struct CoverTerm final {
AstNodeExpr* m_exprp; // Expression branch term
bool m_objective; // Term objective
std::string m_emitV; // V3EmitV string for cover point comment
CoverTerm(AstNodeExpr* exprp, bool objective, const string& emitV)
: m_exprp{exprp}
, m_objective{objective}
, m_emitV(emitV) {}
};
using CoverExpr = std::deque<CoverTerm>;
using CoverExprs = std::list<CoverExpr>;
struct ToggleEnt final {
const string m_comment; // Comment for coverage dump
AstNodeExpr* m_varRefp; // How to get to this element
@ -64,12 +79,20 @@ class CoverageVisitor final : public VNVisitor {
return m_on && !m_inModOff && nodep->fileline()->coverageOn()
&& v3Global.opt.coverageLine();
}
bool exprCoverageOn(const AstNode* nodep) const {
return m_on && !m_inModOff && nodep->fileline()->coverageOn()
&& v3Global.opt.coverageExpr();
}
};
enum Objective : uint8_t { NONE, SEEKING, ABORTED };
// NODE STATE
// Entire netlist:
// AstIf::user1() -> bool. True indicates ifelse processed
// AstIf::user2() -> bool. True indicates coverage-generated
const VNUser1InUse m_inuser1;
const VNUser2InUse m_inuser2;
// STATE - across all visitors
int m_nextHandle = 0;
@ -77,6 +100,13 @@ class CoverageVisitor final : public VNVisitor {
// STATE - for current visit position (use VL_RESTORER)
CheckState m_state; // State save-restored on each new coverage scope/block
AstNodeModule* m_modp = nullptr; // Current module to add statement to
AstNode* m_exprStmtsp = nullptr; // Node to add expr coverage to
bool m_then = false; // Whether we're iterating the then or else branch
// when m_exprStmtps is an AstIf
CoverExprs m_exprs; // List of expressions that can reach objective
Objective m_seeking = NONE; // Seeking objective for expression coverage
bool m_objective = false; // Expression objective
bool m_ifCond = false; // Visiting if condition
bool m_inToggleOff = false; // In function/task etc
string m_beginHier; // AstBegin hier name for user coverage points
@ -160,7 +190,7 @@ class CoverageVisitor final : public VNVisitor {
UINFO(9, "line create h" << m_state.m_handle << " " << nodep << endl);
}
void lineTrack(const AstNode* nodep) {
if (m_state.lineCoverageOn(nodep)
if (m_state.lineCoverageOn(nodep) && !m_ifCond
&& m_state.m_nodep->fileline()->filenameno() == nodep->fileline()->filenameno()) {
for (int lineno = nodep->fileline()->firstLineno();
lineno <= nodep->fileline()->lastLineno(); ++lineno) {
@ -222,13 +252,36 @@ class CoverageVisitor final : public VNVisitor {
}
void visit(AstNodeProcedure* nodep) override { iterateProcedure(nodep); }
void visit(AstWhile* nodep) override { iterateProcedure(nodep); }
// we can cover expressions in while loops, but the counting goes outside
// the while, see: "minimally-intelligent decision about ... clock domain"
// in the Toggle Coverage docs
void visit(AstWhile* nodep) override { iterateProcedure(nodep, false); }
void visit(AstNodeFTask* nodep) override {
if (!nodep->dpiImport()) iterateProcedure(nodep);
}
void iterateProcedure(AstNode* nodep) {
void insertProcStatement(AstNode* nodep, AstNode* stmtp) {
if (AstNodeProcedure* const itemp = VN_CAST(nodep, NodeProcedure)) {
itemp->addStmtsp(stmtp);
} else if (AstNodeFTask* const itemp = VN_CAST(nodep, NodeFTask)) {
itemp->addStmtsp(stmtp);
} else if (AstWhile* const itemp = VN_CAST(nodep, While)) {
itemp->addStmtsp(stmtp);
} else if (AstIf* const itemp = VN_CAST(nodep, If)) {
if (m_then) {
itemp->addThensp(stmtp);
} else {
itemp->addElsesp(stmtp);
}
} else {
nodep->v3fatalSrc("Bad node type");
}
}
void iterateProcedure(AstNode* nodep, bool exprProc = true) {
VL_RESTORER(m_state);
VL_RESTORER(m_exprStmtsp);
VL_RESTORER(m_inToggleOff);
if (exprProc) m_exprStmtsp = nodep;
m_inToggleOff = true;
createHandle(nodep);
iterateChildren(nodep);
@ -237,15 +290,7 @@ class CoverageVisitor final : public VNVisitor {
AstNode* const newp
= newCoverInc(nodep->fileline(), "", "v_line", "block", linesCov(m_state, nodep),
0, traceNameForLine(nodep, "block"));
if (AstNodeProcedure* const itemp = VN_CAST(nodep, NodeProcedure)) {
itemp->addStmtsp(newp);
} else if (AstNodeFTask* const itemp = VN_CAST(nodep, NodeFTask)) {
itemp->addStmtsp(newp);
} else if (AstWhile* const itemp = VN_CAST(nodep, While)) {
itemp->addStmtsp(newp);
} else {
nodep->v3fatalSrc("Bad node type");
}
insertProcStatement(nodep, newp);
}
}
@ -404,6 +449,8 @@ class CoverageVisitor final : public VNVisitor {
// VISITORS - LINE COVERAGE
// Note not AstNodeIf; other types don't get covered
void visit(AstIf* nodep) override {
if (nodep->user2()) return;
UINFO(4, " IF: " << nodep << endl);
if (m_state.m_on) {
// An else-if. When we iterate the if, use "elsif" marking
@ -422,6 +469,10 @@ class CoverageVisitor final : public VNVisitor {
CheckState ifState;
CheckState elseState;
{
VL_RESTORER(m_exprStmtsp);
VL_RESTORER(m_then);
m_exprStmtsp = nodep;
m_then = true;
createHandle(nodep);
iterateAndNextNull(nodep->thensp());
lineTrack(nodep);
@ -429,6 +480,10 @@ class CoverageVisitor final : public VNVisitor {
}
m_state = lastState;
{
VL_RESTORER(m_exprStmtsp);
VL_RESTORER(m_then);
m_exprStmtsp = nodep;
m_then = false;
createHandle(nodep);
iterateAndNextNull(nodep->elsesp());
elseState = m_state;
@ -478,6 +533,9 @@ class CoverageVisitor final : public VNVisitor {
}
m_state = lastState;
}
VL_RESTORER(m_ifCond);
m_ifCond = true;
iterateAndNextNull(nodep->condp());
UINFO(9, " done HANDLE " << m_state.m_handle << " for " << nodep << endl);
}
void visit(AstCaseItem* nodep) override {
@ -542,6 +600,301 @@ class CoverageVisitor final : public VNVisitor {
lineTrack(nodep);
}
void abortExprCoverage() {
// is possible to hit max while in NONE, see: exprReduce()
// if that happens we don't want to set ABORTED if it isn't already
// since that will bleed into other expressions
if (m_seeking != NONE) m_seeking = ABORTED;
m_exprs.clear();
}
bool checkMaxExprs(size_t additional = 0) {
if (m_seeking != ABORTED
&& static_cast<int>(m_exprs.size() + additional) <= v3Global.opt.coverageExprMax())
return false;
abortExprCoverage();
return true;
}
void addExprCoverInc(AstNodeExpr* nodep, int start = 0) {
FileLine* const fl = nodep->fileline();
int count = start;
for (CoverExpr& expr : m_exprs) {
const string name = "expr_" + std::to_string(count);
string comment = "(";
bool first = true;
AstNodeExpr* condp = nullptr;
for (CoverTerm& term : expr) {
comment += (first ? "" : " && ") + term.m_emitV
+ "==" + (term.m_objective ? "1" : "0");
AstNodeExpr* const clonep = term.m_exprp->cloneTree(true);
AstNodeExpr* const termp = term.m_objective ? clonep : new AstLogNot{fl, clonep};
if (condp) {
condp = new AstLogAnd{fl, condp, termp};
} else {
condp = termp;
}
first = false;
}
comment += ") => ";
comment += (m_objective ? '1' : '0');
AstNode* const newp
= newCoverInc(fl, "", "v_expr", comment, "", 0, traceNameForLine(nodep, name));
UASSERT_OBJ(condp, nodep, "No terms in expression coverage branch");
AstIf* const ifp = new AstIf{fl, condp, newp, nullptr};
ifp->user2(true);
insertProcStatement(m_exprStmtsp, ifp);
++count;
}
}
void coverExprs(AstNodeExpr* nodep) {
if (!m_state.exprCoverageOn(nodep) || nodep->dtypep()->width() != 1 || !m_exprStmtsp) {
return;
}
UASSERT_OBJ(m_seeking == NONE, nodep, "recursively covering expressions is not expected");
UASSERT_OBJ(m_exprs.empty(), nodep, "unexpected expression coverage garbage");
VL_RESTORER(m_seeking);
VL_RESTORER(m_objective);
VL_RESTORER(m_exprs);
m_seeking = SEEKING;
m_objective = false;
iterate(nodep);
CoverExprs falseExprs;
m_exprs.swap(falseExprs);
m_objective = true;
iterate(nodep);
if (checkMaxExprs(falseExprs.size())) return;
addExprCoverInc(nodep);
const int start = m_exprs.size();
m_objective = false;
m_exprs.swap(falseExprs);
addExprCoverInc(nodep, start);
}
void exprEither(AstNodeBiop* nodep, bool overrideObjective = false, bool lObjective = false,
bool rObjective = false) {
VL_RESTORER(m_objective);
AstNodeExpr* const lhsp = nodep->lhsp();
AstNodeExpr* const rhsp = nodep->rhsp();
if (overrideObjective) m_objective = lObjective;
iterate(lhsp);
if (checkMaxExprs()) return;
CoverExprs lhsExprs;
m_exprs.swap(lhsExprs);
if (overrideObjective) m_objective = rObjective;
iterate(rhsp);
m_exprs.splice(m_exprs.end(), lhsExprs);
checkMaxExprs();
}
void exprBoth(AstNodeBiop* nodep, bool overrideObjective = false, bool lObjective = false,
bool rObjective = false) {
VL_RESTORER(m_objective);
AstNodeExpr* const lhsp = nodep->lhsp();
AstNodeExpr* const rhsp = nodep->rhsp();
if (overrideObjective) m_objective = lObjective;
iterate(lhsp);
if (checkMaxExprs()) return;
CoverExprs lhsExprs;
m_exprs.swap(lhsExprs);
if (overrideObjective) m_objective = rObjective;
iterate(rhsp);
if (checkMaxExprs()) return;
CoverExprs rhsExprs;
m_exprs.swap(rhsExprs);
for (CoverExpr& l : lhsExprs) {
for (CoverExpr& r : rhsExprs) {
// array size 2 -> (false, true)
std::array<std::set<AstVar*>, 2> varps;
std::array<std::set<std::string>, 2> strs;
UASSERT_OBJ(!l.empty() && !r.empty(), nodep, "Empty coverage expression branch");
CoverExpr expr;
// Compare Vars for simple VarRefs otherwise compare stringified terms
// remove redundant terms and remove entire expression branches when
// terms conflict
// Equivalent terms which don't match on either of these criteria will
// not be flagged as redundant or impossible, however the results will
// still be valid, albeit messier
for (CoverTerm& term : l) {
if (AstVarRef* const refp = VN_CAST(term.m_exprp, VarRef)) {
varps[term.m_objective].insert(refp->varp());
} else {
strs[term.m_objective].insert(term.m_emitV);
}
expr.push_back(term);
}
bool impossible = false;
for (CoverTerm& term : r) {
bool redundant = false;
if (AstNodeVarRef* const refp = VN_CAST(term.m_exprp, NodeVarRef)) {
if (varps[term.m_objective].find(refp->varp())
!= varps[term.m_objective].end())
redundant = true;
if (varps[!term.m_objective].find(refp->varp())
!= varps[!term.m_objective].end())
impossible = true;
} else {
if (strs[term.m_objective].find(term.m_emitV)
!= strs[term.m_objective].end())
redundant = true;
if (strs[!term.m_objective].find(term.m_emitV)
!= strs[!term.m_objective].end())
impossible = true;
}
if (!redundant) expr.push_back(term);
}
if (!impossible) m_exprs.push_back(std::move(expr));
if (checkMaxExprs()) return;
}
}
}
void orExpr(AstNodeBiop* nodep) {
if (m_seeking == NONE) {
coverExprs(nodep);
} else if (m_objective) {
exprEither(nodep);
} else {
exprBoth(nodep);
}
lineTrack(nodep);
}
void visit(AstLogOr* nodep) override { orExpr(nodep); }
void visit(AstOr* nodep) override { orExpr(nodep); }
void andExpr(AstNodeBiop* nodep) {
if (m_seeking == NONE) {
coverExprs(nodep);
} else if (m_objective) {
exprBoth(nodep);
} else {
exprEither(nodep);
}
lineTrack(nodep);
}
void visit(AstLogAnd* nodep) override { andExpr(nodep); }
void visit(AstAnd* nodep) override { andExpr(nodep); }
void xorExpr(AstNodeBiop* nodep) {
if (m_seeking == NONE) {
coverExprs(nodep);
} else {
for (const bool lObjective : {false, true}) {
CoverExprs prevExprs;
m_exprs.swap(prevExprs);
const bool rObjective = lObjective ^ m_objective;
exprBoth(nodep, true, lObjective, rObjective);
m_exprs.splice(m_exprs.end(), prevExprs);
if (checkMaxExprs()) break;
}
}
lineTrack(nodep);
}
void visit(AstXor* nodep) override { xorExpr(nodep); }
void exprNot(AstNodeExpr* nodep) {
VL_RESTORER(m_objective);
if (m_seeking == NONE) {
coverExprs(nodep);
} else {
m_objective = !m_objective;
iterateChildren(nodep);
lineTrack(nodep);
}
}
void visit(AstNot* nodep) override { exprNot(nodep); }
void visit(AstLogNot* nodep) override { exprNot(nodep); }
template <typename T_Oper>
void exprReduce(AstNodeUniop* nodep) {
if (m_seeking != ABORTED) {
FileLine* const fl = nodep->fileline();
AstNodeExpr* const lhsp = nodep->lhsp();
const int width = lhsp->dtypep()->width();
const size_t expected = std::is_same<T_Oper, AstXor>::value ? 0x1 << width : width + 1;
if (checkMaxExprs(expected)) return;
AstNodeExpr* unrolledp = new AstSel{fl, lhsp->cloneTree(true),
new AstConst{fl, static_cast<uint32_t>(width - 1)},
new AstConst{fl, 1}};
for (int bit = width - 2; bit >= 0; bit--) {
AstSel* const selp = new AstSel{fl, lhsp->cloneTree(true),
new AstConst{fl, static_cast<uint32_t>(bit)},
new AstConst{fl, 1}};
unrolledp = new T_Oper{fl, selp, unrolledp};
}
iterate(unrolledp);
pushDeletep(unrolledp);
} else {
iterateChildren(nodep);
lineTrack(nodep);
}
}
void visit(AstRedOr* nodep) override { exprReduce<AstOr>(nodep); }
void visit(AstRedAnd* nodep) override { exprReduce<AstAnd>(nodep); }
void visit(AstRedXor* nodep) override { exprReduce<AstXor>(nodep); }
void visit(AstLogIf* nodep) override {
if (m_seeking == NONE) {
coverExprs(nodep);
} else if (m_objective) {
exprEither(nodep, true, false, true);
} else {
exprBoth(nodep, true, true, false);
}
lineTrack(nodep);
}
void visit(AstLogEq* nodep) override {
VL_RESTORER(m_objective);
if (m_seeking == NONE) {
coverExprs(nodep);
} else {
m_objective = !m_objective;
xorExpr(nodep);
lineTrack(nodep);
}
}
void visit(AstCond* nodep) override {
if (m_seeking == NONE) coverExprs(nodep->condp());
lineTrack(nodep);
}
// Lambdas not supported for expression coverage
void visit(AstWith* nodep) override {
VL_RESTORER(m_seeking);
if (m_seeking == SEEKING) abortExprCoverage();
m_seeking = ABORTED;
iterateChildren(nodep);
lineTrack(nodep);
}
void visit(AstNodeExpr* nodep) override {
if (m_seeking != SEEKING) {
iterateChildren(nodep);
} else {
std::stringstream emitV;
V3EmitV::verilogForTree(nodep, emitV);
// Add new expression with a single term
CoverExpr expr;
expr.emplace_back(nodep, m_objective, emitV.str());
m_exprs.push_back(std::move(expr));
checkMaxExprs();
}
lineTrack(nodep);
}
// VISITORS - BOTH
void visit(AstNode* nodep) override {
iterateChildren(nodep);

View File

@ -62,7 +62,8 @@ class DeadVisitor final : public VNVisitor {
const bool m_elimCells; // Allow removal of Cells
// List of all encountered to avoid another loop through tree
std::vector<AstVar*> m_varsp;
std::vector<AstNode*> m_dtypesp;
std::vector<AstNode*> m_dtypeElimsp; // Data types might eliminate
std::map<AstNodeDType*, AstNodeModule*> m_dtypePkgsp; // Data type's containing package
std::vector<AstVarScope*> m_vscsp;
std::vector<AstScope*> m_scopesp;
std::vector<AstCell*> m_cellsp;
@ -73,6 +74,7 @@ class DeadVisitor final : public VNVisitor {
// STATE - for current visit position (use VL_RESTORER)
bool m_inAssign = false; // Currently in an assign
AstNodeDType* m_curDTypep = nullptr; // Current NodeDType
AstNodeModule* m_modp = nullptr; // Current module
AstSelLoopVars* m_selloopvarsp = nullptr; // Current loop vars
@ -84,8 +86,11 @@ class DeadVisitor final : public VNVisitor {
}
void checkAll(AstNode* nodep) {
if (nodep != nodep->dtypep()) { // NodeDTypes reference themselves
if (AstNode* const subnodep = nodep->dtypep()) subnodep->user1Inc();
if (AstNode* const subnodep = nodep->dtypep()) {
if (nodep != subnodep // Not NodeDTypes reference themselves
&& m_curDTypep != subnodep) { // Not EnumItem referencing parent Enum
subnodep->user1Inc();
}
}
if (AstNode* const subnodep = nodep->getChildDTypep()) subnodep->user1Inc();
}
@ -98,8 +103,9 @@ class DeadVisitor final : public VNVisitor {
&& !VN_IS(nodep, MemberDType) // Keep member names iff upper type exists
&& !nodep->undead() // VoidDType or something Netlist points to
) {
m_dtypesp.push_back(nodep);
m_dtypeElimsp.push_back(nodep);
}
if (VN_IS(m_modp, Package) || VN_IS(m_modp, Class)) m_dtypePkgsp.emplace(nodep, m_modp);
if (AstNode* const subnodep = nodep->virtRefDTypep()) subnodep->user1Inc();
if (AstNode* const subnodep = nodep->virtRefDType2p()) subnodep->user1Inc();
}
@ -212,6 +218,8 @@ class DeadVisitor final : public VNVisitor {
if (nodep->ifaceViaCellp()) nodep->ifaceViaCellp()->user1Inc();
}
void visit(AstNodeDType* nodep) override {
VL_RESTORER(m_curDTypep);
m_curDTypep = nodep;
iterateChildren(nodep);
checkDType(nodep);
checkAll(nodep);
@ -442,7 +450,8 @@ class DeadVisitor final : public VNVisitor {
}
}
}
for (std::vector<AstNode*>::iterator it = m_dtypesp.begin(); it != m_dtypesp.end(); ++it) {
for (std::vector<AstNode*>::iterator it = m_dtypeElimsp.begin(); it != m_dtypeElimsp.end();
++it) {
if ((*it)->user1() == 0) {
// It's possible that there if a reference to each individual member, but
// not to the dtype itself. Check and don't remove the parent dtype if
@ -517,6 +526,11 @@ public:
vscp->varp()->user1Inc();
}
// If data type has a reference in another package, then keep defining package around
for (auto& itr : m_dtypePkgsp) {
if (itr.first->user1()) itr.second->user1Inc();
}
deadCheckTypedefs();
deadCheckVar();
// We only eliminate scopes when in a flattened structure

View File

@ -208,6 +208,7 @@ class DelayedVisitor final : public VNVisitor {
// NODE STATE
// AstVar::user1() -> bool. Set true if already issued MULTIDRIVEN warning
// AstVarRef::user1() -> bool. Set true if target of NBA
// AstAssignDly::user1() -> bool. Set true if already visited
// AstNodeModule::user1p() -> std::unorded_map<std::string, AstVar*> temp map via m_varMap
// AstVarScope::user1p() -> VarScopeInfo via m_vscpInfo
// AstVarScope::user2p() -> AstVarRef*: First write reference to the Variable
@ -909,6 +910,9 @@ class DelayedVisitor final : public VNVisitor {
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
void visit(AstAssignDly* nodep) override {
// Prevent double processing due to AstExprStmt being moved before this node
if (nodep->user1SetOnce()) return;
if (m_cfuncp) {
if (!v3Global.rootp()->nbaEventp()) {
nodep->v3warn(
@ -922,21 +926,31 @@ class DelayedVisitor final : public VNVisitor {
UASSERT_OBJ(m_inSuspendableOrFork || m_activep->hasClocked(), nodep,
"<= assignment in non-clocked block, should have been converted in V3Active");
// Grab the reference to the target of the NBA
// Grab the reference to the target of the NBA, also lift ExprStmt statements on the LHS
VL_RESTORER(m_currNbaLhsRefp);
UASSERT_OBJ(!m_currNbaLhsRefp, nodep, "NBAs should not nest");
nodep->lhsp()->foreach([&](AstVarRef* nodep) {
// Ignore reads (e.g.: '_[*here*] <= _')
if (nodep->access().isReadOnly()) return;
// A RW ref on the LHS (e.g.: '_[preInc(*here*)] <= _') is asking for trouble at this
// point. These should be lowered in an earlier pass into sequenced temporaries.
UASSERT_OBJ(!nodep->access().isRW(), nodep, "RW ref on LHS of NBA");
// Multiple target variables
// (e.g.: '{*here*, *and here*} <= _',or '*here*[*and here* = _] <= _').
// These should be lowered in an earlier pass into sequenced statements.
UASSERT_OBJ(!m_currNbaLhsRefp, nodep, "Multiple Write refs on LHS of NBA");
// Hold on to it
m_currNbaLhsRefp = nodep;
nodep->lhsp()->foreach([&](AstNode* currp) {
if (AstExprStmt* const exprp = VN_CAST(currp, ExprStmt)) {
// Move statements before the NBA
nodep->addHereThisAsNext(exprp->stmtsp()->unlinkFrBackWithNext());
// Replace with result
currp->replaceWith(exprp->resultp()->unlinkFrBack());
// Get rid of the AstExprStmt
VL_DO_DANGLING(pushDeletep(currp), currp);
} else if (AstVarRef* const refp = VN_CAST(currp, VarRef)) {
// Ignore reads (e.g.: '_[*here*] <= _')
if (refp->access().isReadOnly()) return;
// A RW ref on the LHS (e.g.: '_[preInc(*here*)] <= _') is asking for trouble at
// this point. These should be lowered in an earlier pass into sequenced
// temporaries.
UASSERT_OBJ(!refp->access().isRW(), refp, "RW ref on LHS of NBA");
// Multiple target variables
// (e.g.: '{*here*, *and here*} <= _',or '*here*[*and here* = _] <= _').
// These should be lowered in an earlier pass into sequenced statements.
UASSERT_OBJ(!m_currNbaLhsRefp, refp, "Multiple Write refs on LHS of NBA");
// Hold on to it
m_currNbaLhsRefp = refp;
}
});
// The target variable of the NBA (there can only be one per NBA at this point)
AstVarScope* const vscp = m_currNbaLhsRefp->varScopep();

View File

@ -171,7 +171,11 @@ void EmitCBaseVisitorConst::emitCFuncDecl(const AstCFunc* funcp, const AstNodeMo
putns(funcp, "virtual ");
}
emitCFuncHeader(funcp, modp, /* withScope: */ false);
putns(funcp, ";\n");
if (funcp->emptyBody() && !funcp->isLoose() && !cLinkage) {
putns(funcp, " {}\n");
} else {
putns(funcp, ";\n");
}
if (!funcp->ifdef().empty()) putns(funcp, "#endif // " + funcp->ifdef() + "\n");
}

View File

@ -456,9 +456,7 @@ void EmitCFunc::emitDereference(AstNode* nodep, const string& pointer) {
void EmitCFunc::emitCvtPackStr(AstNode* nodep) {
if (const AstConst* const constp = VN_CAST(nodep, Const)) {
putnbs(nodep, "std::string{");
putsQuoted(constp->num().toString());
puts("}");
emitConstantString(constp);
} else if (VN_IS(nodep->dtypep(), StreamDType)) {
putnbs(nodep, "VL_CVT_PACK_STR_ND(");
iterateAndNextConstNull(nodep);
@ -494,9 +492,7 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string
} else if (nodep->num().isFourState()) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: 4-state numbers in this context");
} else if (nodep->num().isString()) {
putnbs(nodep, "std::string{");
putsQuoted(nodep->num().toString());
puts("}");
emitConstantString(nodep);
} else if (nodep->isWide()) {
int upWidth = nodep->num().widthMin();
int chunks = 0;
@ -594,6 +590,13 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string
}
}
void EmitCFunc::emitConstantString(const AstConst* nodep) {
putnbs(nodep, "std::string{");
const string str = nodep->num().toString();
if (!str.empty()) putsQuoted(str);
puts("}");
}
void EmitCFunc::emitSetVarConstant(const string& assignString, AstConst* constp) {
if (!constp->isWide()) {
puts(assignString);

View File

@ -208,6 +208,7 @@ public:
void emitCvtPackStr(AstNode* nodep);
void emitCvtWideArray(AstNode* nodep, AstNode* fromp);
void emitConstant(AstConst* nodep, AstVarRef* assigntop, const string& assignString);
void emitConstantString(const AstConst* nodep);
void emitSetVarConstant(const string& assignString, AstConst* constp);
void emitVarReset(AstVar* varp);
string emitVarResetRecurse(const AstVar* varp, const string& varNameProtected,
@ -279,6 +280,7 @@ public:
// VISITORS
using EmitCConstInit::visit;
void visit(AstCFunc* nodep) override {
if (nodep->emptyBody() && !nodep->isLoose()) return;
VL_RESTORER(m_useSelfForThis);
VL_RESTORER(m_cfuncp);
VL_RESTORER(m_instantiatesOwnProcess)
@ -303,15 +305,6 @@ public:
}
puts(" {\n");
if (nodep->isLoose()) {
m_lazyDecls.declared(nodep); // Defined here, so no longer needs declaration
if (!nodep->isStatic()) { // Standard prologue
m_useSelfForThis = true;
puts("(void)vlSelf; // Prevent unused variable warning\n");
if (!VN_IS(m_modp, Class)) puts(symClassAssign());
}
}
// "+" in the debug indicates a print from the model
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+ ");
for (int i = 0; i < m_modp->level(); ++i) puts(" ");
@ -319,6 +312,18 @@ public:
puts(nodep->isLoose() ? "__" : "::");
puts(nodep->nameProtect() + "\\n\"); );\n");
if (nodep->isLoose()) {
m_lazyDecls.declared(nodep); // Defined here, so no longer needs declaration
if (!nodep->isStatic()) { // Standard prologue
m_useSelfForThis = true;
if (!VN_IS(m_modp, Class)) {
puts(symClassAssign()); // Uses vlSelf
} else {
puts("(void)vlSelf; // Prevent unused variable warning\n");
}
}
}
// Instantiate a process class if it's going to be needed somewhere later
nodep->forall([&](const AstNodeCCall* ccallp) -> bool {
if (ccallp->funcp()->needProcess()
@ -344,16 +349,14 @@ public:
if (m_useSelfForThis) {
m_usevlSelfRef = true;
/*
* Using reference to the vlSelf pointer will help the C++
* compiler to have dereferenceable hints, which can help to
* reduce the need for branch instructions in the generated
* code to allow the compiler to generate load store after the
* if condition (including short-circuit evaluation)
* speculatively and also reduce the data cache pollution when
* executing in the wrong path to make verilator-generated code
* run faster.
*/
// Using reference to the vlSelf pointer will help the C++
// compiler to have dereferenceable hints, which can help to
// reduce the need for branch instructions in the generated
// code to allow the compiler to generate load store after the
// if condition (including short-circuit evaluation)
// speculatively and also reduce the data cache pollution when
// executing in the wrong path to make verilator-generated code
// run faster.
puts("auto& vlSelfRef = std::ref(*vlSelf).get();\n");
}
@ -968,10 +971,10 @@ public:
putns(nodep, "if (");
if (!nodep->branchPred().unknown()) {
puts(nodep->branchPred().ascii());
puts("(");
puts("(("); // Two parens, so that VL_UNLIKELY((class<foo,bar>)) works
}
iterateAndNextConstNull(nodep->condp());
if (!nodep->branchPred().unknown()) puts(")");
if (!nodep->branchPred().unknown()) puts("))");
puts(") {\n");
iterateAndNextConstNull(nodep->thensp());
puts("}");

View File

@ -240,6 +240,45 @@ class EmitCHeader final : public EmitCConstInit {
emitUnpackedUOrSBody(sdtypep);
}
}
enum class AttributeType { Width, Dimension };
// Get member attribute based on type
int getNodeAttribute(const AstMemberDType* itemp, AttributeType type) {
const bool isArrayType
= VN_IS(itemp->dtypep(), UnpackArrayDType) || VN_IS(itemp->dtypep(), DynArrayDType)
|| VN_IS(itemp->dtypep(), QueueDType) || VN_IS(itemp->dtypep(), AssocArrayDType);
switch (type) {
case AttributeType::Width: {
if (isArrayType) {
// For arrays, get innermost element width
AstNodeDType* dtype = itemp->dtypep();
while (dtype->subDTypep()) dtype = dtype->subDTypep();
return dtype->width();
}
return itemp->width();
}
case AttributeType::Dimension: {
// Return array dimension or 0 for non-arrays
return isArrayType ? itemp->dtypep()->dimensions(true).second : 0;
}
default: {
return 0;
}
}
}
template <AttributeType T>
void emitMemberVector(const AstNodeUOrStructDType* sdtypep) {
puts("return {");
bool needComma = false;
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
if (!itemp->isConstrainedRand()) continue;
// Comma handling: add before element except first
if (needComma) puts(",\n");
putns(itemp, std::to_string(getNodeAttribute(itemp, T)));
needComma = true;
}
puts("};\n}\n");
}
void emitUnpackedUOrSBody(AstNodeUOrStructDType* sdtypep) {
putns(sdtypep, sdtypep->verilogKwd()); // "struct"/"union"
puts(" " + EmitCBase::prefixNameProtect(sdtypep) + " {\n");
@ -248,7 +287,51 @@ class EmitCHeader final : public EmitCConstInit {
putns(itemp, itemp->dtypep()->cType(itemp->nameProtect(), false, false));
puts(";\n");
}
// Three helper functions for struct constrained randomization:
// - memberNames: Get member names
// - getMembers: Access member references
// - memberIndices: Retrieve member indices
// - memberWidth: Retrieve member width
// - memberDimension: Retrieve member dimension
if (sdtypep->isConstrainedRand()) {
putns(sdtypep, "\nstd::vector<std::string> memberNames(void) const {\n");
puts("return {");
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
if (itemp->isConstrainedRand()) putns(itemp, "\"" + itemp->shortName() + "\"");
if (itemp->nextp() && VN_AS(itemp->nextp(), MemberDType)->isConstrainedRand())
puts(",\n");
}
puts("};\n}\n");
putns(sdtypep, "\nstd::vector<int> memberWidth(void) const {\n");
emitMemberVector<AttributeType::Width>(sdtypep);
putns(sdtypep, "\nstd::vector<int> memberDimension(void) const {\n");
emitMemberVector<AttributeType::Dimension>(sdtypep);
putns(sdtypep, "\nauto memberIndices(void) const {\n");
puts("return std::index_sequence_for<");
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
if (itemp->isConstrainedRand())
putns(itemp, itemp->dtypep()->cType("", false, false));
if (itemp->nextp() && VN_AS(itemp->nextp(), MemberDType)->isConstrainedRand())
puts(",\n");
}
puts(">{};\n}\n");
putns(sdtypep, "\ntemplate <typename T>");
putns(sdtypep, "\nauto getMembers(T& obj) {\n");
puts("return std::tie(");
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
if (itemp->isConstrainedRand()) putns(itemp, "obj." + itemp->nameProtect());
if (itemp->nextp() && VN_AS(itemp->nextp(), MemberDType)->isConstrainedRand())
puts(", ");
}
puts(");\n}\n");
}
putns(sdtypep, "\nbool operator==(const " + EmitCBase::prefixNameProtect(sdtypep)
+ "& rhs) const {\n");
puts("return ");
@ -262,7 +345,27 @@ class EmitCHeader final : public EmitCConstInit {
putns(sdtypep, "bool operator!=(const " + EmitCBase::prefixNameProtect(sdtypep)
+ "& rhs) const {\n");
puts("return !(*this == rhs);\n}\n");
putns(sdtypep, "\nbool operator<(const " + EmitCBase::prefixNameProtect(sdtypep)
+ "& rhs) const {\n");
puts("return ");
puts("std::tie(");
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
if (itemp != sdtypep->membersp()) puts(", ");
putns(itemp, itemp->nameProtect());
}
puts(")\n < std::tie(");
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
if (itemp != sdtypep->membersp()) puts(", ");
putns(itemp, "rhs." + itemp->nameProtect());
}
puts(");\n");
puts("}\n");
puts("};\n");
puts("template <>\n");
putns(sdtypep, "struct VlIsCustomStruct<" + EmitCBase::prefixNameProtect(sdtypep)
+ "> : public std::true_type {};\n");
}
// getfunc: VL_ASSIGNSEL_XX(rbits, obits, off, lhsdata, rhsdata);

View File

@ -106,7 +106,8 @@ class EmitCSyms final : EmitCBaseVisitorConst {
std::vector<ModVarPair> m_modVars; // Each public {mod,var}
std::map<const std::string, ScopeFuncData> m_scopeFuncs; // Each {scope,dpi-export-func}
std::map<const std::string, ScopeVarData> m_scopeVars; // Each {scope,public-var}
ScopeNames m_scopeNames; // Each unique AstScopeName
ScopeNames m_scopeNames; // Each unique AstScopeName. Dpi scopes added later
ScopeNames m_dpiScopeNames; // Each unique AstScopeName for DPI export
ScopeNames m_vpiScopeCandidates; // All scopes for VPI
ScopeNameHierarchy m_vpiScopeHierarchy; // The actual hierarchy of scopes
int m_coverBins = 0; // Coverage bin number
@ -181,7 +182,6 @@ class EmitCSyms final : EmitCBaseVisitorConst {
/// Then add/update entry in m_scopeNames if not already there
void varHierarchyScopes(string scp) {
// we want no result to be -1, so use ints
string::size_type prd_pos = scp.rfind('.');
string::size_type dot_pos = scp.rfind("__DOT__");
@ -195,7 +195,7 @@ class EmitCSyms final : EmitCBaseVisitorConst {
}
// resize and advance pointers
if (prd_pos < dot_pos && dot_pos != string::npos) {
if ((prd_pos < dot_pos || prd_pos == string::npos) && dot_pos != string::npos) {
scp.resize(dot_pos);
dot_pos = scp.rfind("__DOT__");
} else {
@ -209,7 +209,7 @@ class EmitCSyms final : EmitCBaseVisitorConst {
void varsExpand() {
// We didn't have all m_scopes loaded when we encountered variables, so expand them now
// It would be less code if each module inserted its own variables.
// Someday. For now public isn't common.
// Someday.
for (std::vector<ScopeModPair>::iterator itsc = m_scopes.begin(); itsc != m_scopes.end();
++itsc) {
AstScope* const scopep = itsc->first;
@ -283,6 +283,15 @@ class EmitCSyms final : EmitCBaseVisitorConst {
if (v3Global.opt.vpi()) buildVpiHierarchy();
if (v3Global.dpi()) {
// add dpi scopes to m_scopeNames if not already there
for (const auto& scp : m_dpiScopeNames) {
if (m_scopeNames.find(scp.first) == m_scopeNames.end()) {
m_scopeNames.emplace(scp.first, scp.second);
}
}
}
// Sort by names, so line/process order matters less
stable_sort(m_scopes.begin(), m_scopes.end(), CmpName());
stable_sort(m_dpis.begin(), m_dpis.end(), CmpDpi());
@ -331,7 +340,8 @@ class EmitCSyms final : EmitCBaseVisitorConst {
const int timeunit = m_modp->timeunit().powerOfTen();
m_vpiScopeCandidates.emplace(scopeSymString(nodep->name()),
ScopeData{nodep, scopeSymString(nodep->name()),
name_pretty, "<null>", timeunit, type});
name_pretty, nodep->modp()->origName(),
timeunit, type});
}
iterateChildrenConst(nodep);
}
@ -340,26 +350,27 @@ class EmitCSyms final : EmitCBaseVisitorConst {
// 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{nodep, name, nodep->scopePrettySymName(), "<null>",
timeunit, "SCOPE_OTHER"});
m_dpiScopeNames.emplace(name, ScopeData{nodep, name, nodep->scopePrettySymName(), "<null>",
timeunit, "SCOPE_OTHER"});
if (nodep->dpiExport()) {
UASSERT_OBJ(m_cfuncp, nodep, "ScopeName not under DPI function");
m_scopeFuncs.emplace(name + " " + m_cfuncp->name(),
ScopeFuncData(nodep, m_cfuncp, m_modp));
} else {
if (m_scopeNames.find(nodep->scopeDpiName()) == m_scopeNames.end()) {
if (m_dpiScopeNames.find(nodep->scopeDpiName()) == m_dpiScopeNames.end()) {
// cppcheck-suppress stlFindInsert
m_scopeNames.emplace(nodep->scopeDpiName(),
ScopeData{nodep, nodep->scopeDpiName(),
nodep->scopePrettyDpiName(), "<null>", timeunit,
"SCOPE_OTHER"});
m_dpiScopeNames.emplace(nodep->scopeDpiName(),
ScopeData{nodep, nodep->scopeDpiName(),
nodep->scopePrettyDpiName(), "<null>", timeunit,
"SCOPE_OTHER"});
}
}
}
void visit(AstVar* nodep) override {
nameCheck(nodep);
iterateChildrenConst(nodep);
if (nodep->isSigUserRdPublic() && !m_cfuncp) m_modVars.emplace_back(m_modp, nodep);
if ((nodep->isSigUserRdPublic() || nodep->isSigUserRWPublic()) && !m_cfuncp)
m_modVars.emplace_back(m_modp, nodep);
}
void visit(AstVarScope* nodep) override {
iterateChildrenConst(nodep);
@ -929,21 +940,11 @@ void EmitCSyms::emitSymImp() {
checkSplit(true);
AstScope* const scopep = it->second.m_scopep;
AstVar* const varp = it->second.m_varp;
//
int pwidth = 1;
int pdim = 0;
int udim = 0;
string bounds;
if (AstBasicDType* const basicp = varp->basicp()) {
// Range is always first, it's not in "C" order
if (basicp->isRanged()) {
bounds += " ,";
bounds += cvtToStr(basicp->hi());
bounds += ",";
bounds += cvtToStr(basicp->lo());
pdim++;
pwidth *= basicp->elements();
}
for (AstNodeDType* dtypep = varp->dtypep(); dtypep;) {
dtypep
= dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
@ -952,28 +953,24 @@ void EmitCSyms::emitSymImp() {
bounds += cvtToStr(adtypep->left());
bounds += ",";
bounds += cvtToStr(adtypep->right());
if (VN_IS(dtypep, PackArrayDType)) {
if (VN_IS(dtypep, PackArrayDType))
pdim++;
pwidth *= adtypep->elementsConst();
} else {
else
udim++;
}
dtypep = adtypep->subDTypep();
} else {
if (basicp->isRanged()) {
bounds += " ,";
bounds += cvtToStr(basicp->hi());
bounds += ",";
bounds += cvtToStr(basicp->lo());
pdim++;
}
break; // AstBasicDType - nothing below, 1
}
}
}
// TODO: actually expose packed arrays as vpiRegArray
if (pdim > 1 && udim == 0) {
bounds = ", ";
bounds += cvtToStr(pwidth - 1);
bounds += ",0";
pdim = 1;
}
if (pdim > 1 || udim > 1) {
puts("//UNSUP "); // VerilatedImp can't deal with >2d or packed arrays
}
putns(scopep, protect("__Vscope_" + it->second.m_scopeName));
putns(varp, ".varInsert(__Vfinal,");
putsQuoted(protect(it->second.m_varBasePretty));
@ -983,7 +980,8 @@ void EmitCSyms::emitSymImp() {
varName += protect(varp->name());
if (varp->isParam()) {
if (varp->vlEnumType() == "VLVT_STRING") {
if (varp->vlEnumType() == "VLVT_STRING"
&& !VN_IS(varp->subDTypep(), UnpackArrayDType)) {
puts(", const_cast<void*>(static_cast<const void*>(");
puts(varName);
puts(".c_str())), ");
@ -1004,7 +1002,9 @@ void EmitCSyms::emitSymImp() {
puts(",");
puts(varp->vlEnumDir()); // VLVD_IN etc
puts(",");
puts(cvtToStr(pdim + udim));
puts(cvtToStr(udim));
puts(",");
puts(cvtToStr(pdim));
puts(bounds);
puts(");\n");
++m_numStmts;

View File

@ -96,7 +96,7 @@ private:
std::sort(sortedScores.begin(), sortedScores.end());
const int64_t topScore = sortedScores.back();
os << "Top score: " << topScore << endl;
os << "Top score: " << topScore << '\n';
const int maxScoreWidth = std::to_string(topScore).length();
const int64_t intervalsNum = std::min<int64_t>(topScore + 1, MAX_INTERVALS_NUM);
@ -123,7 +123,7 @@ private:
for (const Interval& iv : intervals)
topIntervalSize = std::max(topIntervalSize, iv.m_size);
os << "Input files' scores histogram:" << endl;
os << "Input files' scores histogram:\n";
for (const Interval& iv : intervals) {
const int scaledSize = iv.m_size * (MAX_BAR_LENGTH + 1) / topIntervalSize;
@ -132,14 +132,14 @@ private:
os << std::setw(maxScoreWidth) << iv.m_lowerBound << line << " " << iv.m_size << '\n';
}
os << std::setw(maxScoreWidth) << (topScore + 1) << '\n';
os << endl;
os << '\n';
}
// PRIVATE METHODS
// Debug logging: dumps Work Lists and their lists of files
void dumpWorkLists(std::ostream& os) {
os << "Initial Work Lists with their concatenation eligibility status:" << endl;
os << "Initial Work Lists with their concatenation eligibility status:\n";
for (const WorkList& list : m_workLists) {
os << "+ [" << (list.m_isConcatenable ? 'x' : ' ') << "] Work List #" << list.m_dbgId
<< " (num of files: " << list.m_files.size()
@ -157,13 +157,13 @@ private:
os << "| + " << last.m_filename << " (score: " << last.m_score << ")\n";
}
}
os << endl;
os << '\n';
}
// Debug logging: dumps list of output files. List of grouped files is additionally printed
// for each concatenating file.
void dumpOutputList(std::ostream& os) const {
os << "List of output files after execution of concatenation:" << endl;
os << "List of output files after execution of concatenation:\n";
for (const FileOrConcatenatedFilesList& entry : m_outputFiles) {
if (entry.isConcatenatingFile()) {
@ -182,15 +182,16 @@ private:
const int totalBucketsNum = v3Global.opt.outputGroups();
// Return early if there's nothing to do.
bool groupingRedundant = false;
if (inputFilesCount < MIN_FILES_COUNT) {
UINFO(4, "File concatenation skipped: Too few files ("
<< m_inputFiles.size() << " < " << MIN_FILES_COUNT << ")" << endl);
if (inputFilesCount < MIN_FILES_COUNT
&& inputFilesCount <= static_cast<size_t>(totalBucketsNum)) {
UINFO(4, "File concatenation skipped: Too few files (" << m_inputFiles.size() << " < "
<< MIN_FILES_COUNT << ")\n");
groupingRedundant = true;
}
if (inputFilesCount < (MIN_FILES_PER_BUCKET * totalBucketsNum)) {
UINFO(4, "File concatenation skipped: Too few files per bucket ("
<< m_inputFiles.size() << " < " << MIN_FILES_PER_BUCKET << " - "
<< totalBucketsNum << ")" << endl);
<< totalBucketsNum << ")\n");
groupingRedundant = true;
}
if (!groupingRedundant) return false;
@ -212,13 +213,12 @@ private:
V3Stats::addStat("Concatenation max score", concatenableFileMaxScore);
int nextWorkListId = 0;
if (m_logp) *m_logp << "Input files with their concatenation eligibility status:" << endl;
if (m_logp) *m_logp << "Input files with their concatenation eligibility status:\n";
for (const FilenameWithScore& inputFile : m_inputFiles) {
const bool fileIsConcatenable = (inputFile.m_score <= concatenableFileMaxScore);
if (m_logp)
*m_logp << " + [" << (fileIsConcatenable ? 'x' : ' ') << "] "
<< inputFile.m_filename << " (score: " << inputFile.m_score << ")"
<< endl;
<< inputFile.m_filename << " (score: " << inputFile.m_score << ")\n";
V3Stats::addStatSum(fileIsConcatenable ? "Concatenation total grouped score"
: "Concatenation total non-grouped score",
inputFile.m_score);
@ -233,7 +233,7 @@ private:
list.m_files.push_back({inputFile.m_filename, inputFile.m_score});
list.m_totalScore += inputFile.m_score;
}
if (m_logp) *m_logp << endl;
if (m_logp) *m_logp << '\n';
}
void assignBuckets(uint64_t concatenableFilesTotalScore) {
@ -248,8 +248,7 @@ private:
// Debugging: Log which work lists will be kept
if (m_logp) {
*m_logp << "More Work Lists than buckets; "
"Work Lists with statuses indicating whether the list will be kept:"
<< endl;
"Work Lists with statuses indicating whether the list will be kept:\n";
// Only lists that will be kept. List that will be removed are logged below.
std::for_each(m_concatenableListsByDescSize.begin(),
m_concatenableListsByDescSize.begin() + totalBucketsNum,
@ -269,7 +268,7 @@ private:
<< " (num of files: " << listp->m_files.size()
<< "; total score: " << listp->m_totalScore << ")\n";
});
if (m_logp) *m_logp << endl;
if (m_logp) *m_logp << '\n';
m_concatenableListsByDescSize.resize(totalBucketsNum);
// Recalculate stats
@ -283,7 +282,7 @@ private:
V3Stats::addStat("Concatenation ideal bucket score", idealBucketScore);
if (m_logp) *m_logp << "Buckets assigned to Work Lists:" << endl;
if (m_logp) *m_logp << "Buckets assigned to Work Lists:\n";
int availableBuckets = v3Global.opt.outputGroups();
for (WorkList* listp : m_concatenableListsByDescSize) {
if (availableBuckets > 0) {
@ -301,7 +300,7 @@ private:
<< std::right << " (excluding from concatenation)\n";
}
}
if (m_logp) *m_logp << endl;
if (m_logp) *m_logp << '\n';
}
void buildOutputList() {
@ -395,19 +394,19 @@ private:
}
void process() {
UINFO(4, __FUNCTION__ << ":" << endl);
UINFO(5, "Number of input files: " << m_inputFiles.size() << endl);
UINFO(5, "Total score: " << m_totalScore << endl);
UINFO(5, "Group file prefix: " << m_groupFilePrefix << endl);
UINFO(4, __FUNCTION__ << " group file prefix: " << m_groupFilePrefix << '\n');
UINFO(5, "Number of input files: " << m_inputFiles.size() << '\n');
UINFO(5, "Total score: " << m_totalScore << '\n');
const int totalBucketsNum = v3Global.opt.outputGroups();
UINFO(5, "Number of buckets: " << totalBucketsNum << endl);
UINFO(5, "Number of buckets: " << totalBucketsNum << '\n');
UASSERT(totalBucketsNum > 0, "More than 0 buckets required");
if (fallbackNoGrouping(m_inputFiles.size())) return;
if (dumpLevel() >= 6) {
if (debug() >= 6 || dumpLevel() >= 6) {
const string filename = v3Global.debugFilename("outputgroup") + ".txt";
UINFO(5, "Dumping " << filename << endl);
m_logp = std::unique_ptr<std::ofstream>{V3File::new_ofstream(filename)};
if (m_logp->fail()) v3fatal("Can't write " << filename);
}
@ -417,7 +416,6 @@ private:
createWorkLists();
// Collect stats and mark lists with only one file as non-concatenable
size_t concatenableFilesCount = 0;
int64_t concatenableFilesTotalScore = 0;
@ -479,6 +477,9 @@ class EmitMk final {
using FileOrConcatenatedFilesList = EmitGroup::FileOrConcatenatedFilesList;
using FilenameWithScore = EmitGroup::FilenameWithScore;
// MEMBERS
double m_putClassCount = 0; // Number of classEntries printed
public:
// METHODS
@ -494,6 +495,7 @@ public:
void putMakeClassEntry(V3OutMkFile& of, const string& name) {
of.puts("\t" + V3Os::filenameNonDirExt(name) + " \\\n");
++m_putClassCount;
}
void emitClassMake() {
@ -577,9 +579,12 @@ public:
} else {
of.puts(", fast-path, compile with highest optimization\n");
}
of.puts(support == 2 ? "VM_GLOBAL" : support == 1 ? "VM_SUPPORT" : "VM_CLASSES");
of.puts(slow ? "_SLOW" : "_FAST");
of.puts(" += \\\n");
const string targetVar = (support == 2 ? "VM_GLOBAL"s
: support == 1 ? "VM_SUPPORT"s
: "VM_CLASSES"s)
+ (slow ? "_SLOW" : "_FAST");
m_putClassCount = 0;
of.puts(targetVar + " += \\\n");
if (support == 2 && v3Global.opt.hierChild()) {
// Do nothing because VM_GLOBAL is necessary per executable. Top module will
// have them.
@ -605,7 +610,7 @@ public:
const std::vector<FileOrConcatenatedFilesList>& list
= slow ? vmClassesSlowList : vmClassesFastList;
for (const FileOrConcatenatedFilesList& entry : list) {
if (entry.isConcatenatingFile()) { emitConcatenatingFile(entry); }
if (entry.isConcatenatingFile()) emitConcatenatingFile(entry);
putMakeClassEntry(of, entry.m_filename);
}
} else {
@ -619,6 +624,7 @@ public:
}
}
of.puts("\n");
V3Stats::addStat("Makefile targets, " + targetVar, m_putClassCount);
}
}
@ -808,6 +814,7 @@ public:
};
//######################################################################
class EmitMkHierVerilation final {
const V3HierBlockPlan* const m_planp;
const string m_makefile; // path of this makefile

View File

@ -28,13 +28,17 @@ VL_DEFINE_DEBUG_FUNCTIONS;
// Emit statements and expressions
class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
// MEMBERS
bool m_suppressSemi = false;
// STATE - across all visitors
const bool m_suppressUnknown = false;
// STATE - for current visit position (use VL_RESTORER)
AstSenTree* m_sensesp; // Domain for printing one a ALWAYS under a ACTIVE
bool m_suppressSemi = false; // Non-statement, don't print ;
bool m_suppressVarSemi = false; // Suppress emitting semicolon for AstVars
bool m_arrayPost = false; // Print array information that goes after identifier (vs after)
std::deque<AstNodeArrayDType*> m_packedps; // Packed arrays to print with BasicDType
// METHODS
virtual void puts(const string& str) = 0;
virtual void putbs(const string& str) = 0;
virtual void putfs(AstNode* nodep, const string& str) = 0; // Fileline and node %% mark
@ -49,6 +53,20 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
putsNoTracking("\"");
}
void iterateAndCommaConstNull(AstNode* nodep) {
for (; nodep; nodep = nodep->nextp()) {
iterateConst(nodep);
if (nodep->nextp()) puts(", ");
}
}
void emitPacked() {
for (AstNodeArrayDType* packedp : m_packedps) {
puts(" ");
iterateConstNull(packedp->rangep());
}
m_packedps.clear();
}
// VISITORS
void visit(AstNetlist* nodep) override { iterateAndNextConstNull(nodep->modulesp()); }
void visit(AstNodeModule* nodep) override {
@ -58,13 +76,14 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
}
void visit(AstPort* nodep) override {}
void visit(AstNodeFTask* nodep) override {
putfs(nodep, nodep->isFunction() ? "function" : "task");
const bool func = nodep->isFunction() || nodep->name() == "new";
putfs(nodep, func ? "function" : "task");
puts(" ");
puts(nodep->prettyName());
puts(";\n");
// Only putfs the first time for each visitor; later for same node is putqs
iterateAndNextConstNull(nodep->stmtsp());
putfs(nodep, nodep->isFunction() ? "endfunction\n" : "endtask\n");
putfs(nodep, func ? "endfunction\n" : "endtask\n");
}
void visit(AstBegin* nodep) override {
@ -169,7 +188,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
}
void visit(AstSenItem* nodep) override {
putfs(nodep, "");
puts(nodep->edgeType().verilogKwd());
if (nodep->edgeType() != VEdgeType::ET_CHANGED) puts(nodep->edgeType().verilogKwd());
if (nodep->sensp()) puts(" ");
iterateChildrenConst(nodep);
}
@ -216,18 +235,30 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
void visit(AstCoverInc*) override {} // N/A
void visit(AstCoverToggle*) override {} // N/A
void visit(AstTestPlusArgs* nodep) override {
putfs(nodep, nodep->verilogKwd());
putbs("(");
iterateChildrenConst(nodep);
puts(")");
}
void visit(AstValuePlusArgs* nodep) override {
putfs(nodep, nodep->verilogKwd());
putbs("(");
iterateChildrenConst(nodep);
puts(")");
}
void visitNodeDisplay(AstNode* nodep, AstNode* fileOrStrgp, const string& text,
AstNode* exprsp) {
putfs(nodep, nodep->verilogKwd());
putbs("(");
if (fileOrStrgp) {
iterateAndNextConstNull(fileOrStrgp);
iterateConstNull(fileOrStrgp);
putbs(", ");
}
putsQuoted(text);
for (AstNode* expp = exprsp; expp; expp = expp->nextp()) {
puts(", ");
iterateAndNextConstNull(expp);
iterateConstNull(expp);
}
puts(");\n");
}
@ -393,7 +424,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
}
void visit(AstTextBlock* nodep) override {
visit(static_cast<AstNodeSimpleText*>(nodep));
VL_RESTORER(m_suppressSemi);
VL_RESTORER(m_suppressVarSemi);
m_suppressVarSemi = nodep->commas();
for (AstNode* childp = nodep->nodesp(); childp; childp = childp->nextp()) {
iterateConst(childp);
@ -403,17 +434,17 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
void visit(AstScopeName* nodep) override {}
void visit(AstCStmt* nodep) override {
putfs(nodep, "$_CSTMT(");
iterateAndNextConstNull(nodep->exprsp());
iterateAndCommaConstNull(nodep->exprsp());
puts(");\n");
}
void visit(AstCExpr* nodep) override {
putfs(nodep, "$_CEXPR(");
iterateAndNextConstNull(nodep->exprsp());
iterateAndCommaConstNull(nodep->exprsp());
puts(")");
}
void visit(AstUCStmt* nodep) override {
putfs(nodep, "$c(");
iterateAndNextConstNull(nodep->exprsp());
iterateAndCommaConstNull(nodep->exprsp());
puts(");\n");
}
void visit(AstUCFunc* nodep) override {
@ -431,19 +462,13 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
void visit(AstCMethodHard* nodep) override {
iterateConst(nodep->fromp());
puts("." + nodep->name() + "(");
for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) {
if (pinp != nodep->pinsp()) puts(", ");
iterateConst(pinp);
}
iterateAndCommaConstNull(nodep->pinsp());
puts(")");
}
void visit(AstCMethodCall* nodep) override {
iterateConst(nodep->fromp());
puts("." + nodep->name() + "(");
for (AstNode* pinp = nodep->argsp(); pinp; pinp = pinp->nextp()) {
if (pinp != nodep->argsp()) puts(", ");
iterateConst(pinp);
}
iterateAndCommaConstNull(nodep->argsp());
puts(")");
}
@ -463,7 +488,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
bool inPct = false;
putbs("");
for (const char c : format) {
if (c == '%') {
if (!inPct && c == '%') {
inPct = true;
} else if (!inPct) { // Normal text
string s;
@ -565,32 +590,40 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
puts(cvtToStr(nodep->leftConst()));
puts(":");
puts(cvtToStr(nodep->rightConst()));
puts("]");
} else {
iterateAndNextConstNull(nodep->leftp());
puts(":");
iterateAndNextConstNull(nodep->rightp());
puts("]");
}
puts("]");
}
void visit(AstRand* nodep) override {
emitVerilogFormat(nodep, nodep->emitVerilog(), nodep->seedp());
}
void visit(AstSel* nodep) override {
iterateAndNextConstNull(nodep->fromp());
int offset = 0;
AstNodeDType* const dtypep = nodep->fromp()->dtypep();
if (VN_IS(dtypep, BasicDType)) {
AstBasicDType* const basicDtypep = VN_AS(dtypep, BasicDType);
offset = basicDtypep->lo();
}
puts("[");
if (VN_IS(nodep->lsbp(), Const)) {
if (nodep->widthp()->isOne()) {
if (VN_IS(nodep->lsbp(), Const)) {
puts(cvtToStr(VN_AS(nodep->lsbp(), Const)->toSInt()));
} else {
iterateAndNextConstNull(nodep->lsbp());
}
puts(cvtToStr(VN_AS(nodep->lsbp(), Const)->toSInt() + offset));
} else {
puts(cvtToStr(VN_AS(nodep->lsbp(), Const)->toSInt()
+ VN_AS(nodep->widthp(), Const)->toSInt() - 1));
+ VN_AS(nodep->widthp(), Const)->toSInt() + offset - 1));
puts(":");
puts(cvtToStr(VN_AS(nodep->lsbp(), Const)->toSInt()));
puts(cvtToStr(VN_AS(nodep->lsbp(), Const)->toSInt() + offset));
}
} else {
iterateAndNextConstNull(nodep->lsbp());
if (offset != 0) {
puts(" + ");
puts(cvtToStr(offset));
}
putfs(nodep, "+:");
iterateAndNextConstNull(nodep->widthp());
puts("]");
@ -603,14 +636,31 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
}
void visit(AstTypedef* nodep) override {
putfs(nodep, "typedef ");
iterateAndNextConstNull(nodep->subDTypep());
iterateConstNull(nodep->subDTypep());
puts(" ");
puts(nodep->prettyName());
puts(";\n");
}
void visit(AstAssocArrayDType* nodep) override {
if (!m_arrayPost) {
iterateConst(nodep->subDTypep());
} else {
VL_RESTORER(m_arrayPost);
m_arrayPost = false;
puts("[");
iterateConst(nodep->keyDTypep());
puts("]");
m_arrayPost = true;
iterateConst(nodep->subDTypep()); // For post's key
}
}
void visit(AstBasicDType* nodep) override {
if (m_arrayPost) return;
putfs(nodep, nodep->prettyName());
if (nodep->isSigned()) putfs(nodep, " signed");
if (nodep->isSigned() && !nodep->keyword().isDouble()) putfs(nodep, " signed");
// Do not emit ranges for integer atoms.
if (nodep->keyword().isIntNumeric() && !nodep->keyword().isBitLogic()) return;
emitPacked();
if (nodep->rangep()) {
puts(" ");
iterateAndNextConstNull(nodep->rangep());
@ -624,12 +674,70 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
}
}
void visit(AstConstDType* nodep) override {
if (m_arrayPost) return;
putfs(nodep, "const ");
iterateConst(nodep->subDTypep());
}
void visit(AstNodeArrayDType* nodep) override {
void visit(AstDynArrayDType* nodep) override {
if (!m_arrayPost) {
iterateConst(nodep->subDTypep());
} else {
puts("[]");
iterateConst(nodep->subDTypep()); // For post's key
}
}
void visit(AstEnumDType* nodep) override {
if (m_arrayPost) return;
putfs(nodep, "enum ");
iterateConst(nodep->subDTypep());
iterateAndNextConstNull(nodep->rangep());
puts("{\n");
iterateAndNextConstNull(nodep->itemsp());
puts("}");
}
void visit(AstEnumItemRef* nodep) override {
if (AstNodeModule* const classOrPackagep = nodep->classOrPackagep()) {
putfs(nodep, classOrPackagep->prettyName());
puts("::");
}
putfs(nodep, nodep->name());
}
void visit(AstEnumItem* nodep) override {
putfs(nodep, nodep->name());
iterateConstNull(nodep->rangep());
puts(" = ");
iterateConstNull(nodep->valuep());
if (nodep->nextp()) puts(",");
puts("\n");
}
void visit(AstNodeArrayDType* nodep) override {
if (!m_arrayPost) {
if (VN_IS(nodep, PackArrayDType)) {
// Unpacked ranges handled in BasicDType, as they print "backwards"
m_packedps.push_back(nodep);
}
iterateConst(nodep->subDTypep());
} else {
if (VN_IS(nodep, UnpackArrayDType)) {
VL_RESTORER(m_arrayPost);
m_arrayPost = false;
iterateAndNextConstNull(nodep->rangep());
m_arrayPost = true;
}
iterateConst(nodep->subDTypep()); // For post's key
}
}
void visit(AstIfaceRefDType* nodep) override {
if (m_arrayPost) {
puts(" (");
if (nodep->cellp()) {
iterateConst(nodep->cellp());
} else {
puts("????");
}
puts(")");
return;
}
puts(nodep->ifaceName());
}
void visit(AstRefDType* nodep) override {
if (nodep->subDTypep()) {
@ -638,22 +746,68 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
puts("\n???? // "s + nodep->prettyTypeName() + " -> UNLINKED\n");
}
}
void visit(AstModport* nodep) override {
puts(nodep->verilogKwd());
puts(" ");
puts(nodep->prettyName());
puts(" (\n");
if (nodep->varsp()) {
iterateConst(nodep->varsp());
} else {
puts("????");
}
puts(");\n");
}
void visit(AstModportVarRef* nodep) override {
puts(nodep->direction().verilogKwd());
puts(" ");
if (nodep->varp()) {
VL_RESTORER(m_suppressVarSemi);
m_suppressVarSemi = true;
iterateConst(nodep->varp());
} else {
puts(nodep->prettyName());
}
if (nodep->nextp()) puts(", ");
}
void visit(AstNodeUOrStructDType* nodep) override {
if (m_arrayPost) return;
puts(nodep->verilogKwd() + " ");
if (nodep->packed()) puts("packed ");
puts("\n");
puts("{");
for (AstMemberDType* itemp = nodep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
iterateConst(itemp);
puts(";");
{
puts("{\n");
VL_RESTORER(m_packedps);
m_packedps.clear();
for (AstMemberDType* itemp = nodep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
iterateConst(itemp);
}
puts("}");
}
puts("}");
emitPacked();
}
void visit(AstMemberDType* nodep) override {
if (m_arrayPost) return;
iterateConst(nodep->subDTypep());
puts(" ");
puts(nodep->name());
puts(";\n");
}
void visit(AstQueueDType* nodep) override {
if (!m_arrayPost) {
iterateConst(nodep->subDTypep());
} else {
VL_RESTORER(m_arrayPost);
m_arrayPost = false;
puts("[$");
if (nodep->boundp()) {
puts(":");
iterateConst(nodep->boundp());
}
puts("]");
m_arrayPost = true;
iterateConst(nodep->subDTypep()); // For post's key
}
}
void visit(AstNodeFTaskRef* nodep) override {
if (nodep->dotted() != "") {
@ -698,7 +852,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
}
}
void visit(AstVarXRef* nodep) override {
putfs(nodep, nodep->dotted());
putfs(nodep, nodep->prettyName(nodep->dotted()));
puts(".");
if (nodep->varp()) {
puts(nodep->varp()->prettyName());
@ -716,37 +870,23 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
putfs(nodep, nodep->verilogKwd());
puts(" ");
}
std::vector<const AstUnpackArrayDType*> unpackps;
for (AstNodeDType* dtypep = nodep->dtypep(); dtypep;) {
dtypep = dtypep->skipRefp();
if (const AstUnpackArrayDType* const unpackp = VN_CAST(dtypep, UnpackArrayDType)) {
unpackps.push_back(unpackp);
dtypep = unpackp->subDTypep();
} else {
iterateConst(dtypep);
puts(" ");
puts(nodep->prettyName());
dtypep = nullptr;
}
}
// If nodep is an unpacked array, append unpacked dimensions
for (const auto& unpackp : unpackps) {
puts("[");
puts(cvtToStr(unpackp->rangep()->leftConst()));
puts(":");
puts(cvtToStr(unpackp->rangep()->rightConst()));
puts("]");
}
VL_RESTORER(m_arrayPost);
m_arrayPost = false;
iterateConstNull(nodep->dtypep()); // Dtype part before identifier
puts(" ");
puts(nodep->prettyName());
m_arrayPost = true;
iterateConstNull(nodep->dtypep()); // Dtype part after identifier
puts(m_suppressVarSemi ? "\n" : ";\n");
}
void visit(AstActive* nodep) override {
VL_RESTORER(m_sensesp);
m_sensesp = nodep->sensesp();
iterateAndNextConstNull(nodep->stmtsp());
m_sensesp = nullptr;
}
void visit(AstParseRef* nodep) override { puts(nodep->prettyName()); }
void visit(AstVarScope*) override {}
void visit(AstNodeText*) override {}
void visit(AstVarScope*) override {}
void visit(AstTraceDecl*) override {}
void visit(AstTraceInc*) override {}
// NOPs
@ -764,7 +904,6 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
}
public:
bool m_suppressVarSemi = false; // Suppress emitting semicolon for AstVars
explicit EmitVBaseVisitorConst(bool suppressUnknown, AstSenTree* domainp)
: m_suppressUnknown{suppressUnknown}
, m_sensesp{domainp} {}

View File

@ -154,7 +154,7 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra)
}
// Suppress duplicate messages
if (!m_messages.insert(msg).second) return;
if (!extra.empty()) {
if (!extra.empty() && !m_errorSuppressed) {
const string extraMsg = warnMore() + extra + "\n";
const size_t pos = msg.find('\n');
msg.insert(pos + 1, extraMsg);

View File

@ -88,6 +88,7 @@ public:
COMBDLY, // Combinatorial delayed assignment
CONSTRAINTIGN, // Constraint ignored
CONTASSREG, // Continuous assignment on reg
COVERIGN, // Coverage ignored
DECLFILENAME, // Declaration doesn't match filename
DEFPARAM, // Style: Defparam
DEPRECATED, // Feature will be deprecated
@ -194,7 +195,7 @@ public:
"ALWCOMBORDER", "ASCRANGE", "ASSIGNDLY", "ASSIGNIN", "BADSTDPRAGMA",
"BLKANDNBLK", "BLKLOOPINIT", "BLKSEQ", "BSSPACE",
"CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST", "CDCRSTLOGIC", "CLKDATA",
"CMPCONST", "COLONPLUS", "COMBDLY", "CONSTRAINTIGN", "CONTASSREG",
"CMPCONST", "COLONPLUS", "COMBDLY", "CONSTRAINTIGN", "CONTASSREG", "COVERIGN",
"DECLFILENAME", "DEFPARAM", "DEPRECATED",
"ENCAPSULATED", "ENDLABEL", "ENUMVALUE", "EOFNEWLINE", "GENCLK",
"GENUNNAMED", "HIERBLOCK",

View File

@ -528,9 +528,11 @@ void fillinCosts(V3Graph* execMTaskGraphp) {
if (missingProfiles) {
if (FileLine* const fl = V3Config::getProfileDataFileLine()) {
fl->v3warn(PROFOUTOFDATE, "Profile data for mtasks may be out of date. "
<< missingProfiles << " of " << totalEstimates
<< " mtasks had no data");
if (V3Config::containsMTaskProfileData()) {
fl->v3warn(PROFOUTOFDATE, "Profile data for mtasks may be out of date. "
<< missingProfiles << " of " << totalEstimates
<< " mtasks had no data");
}
}
}
}

View File

@ -183,6 +183,19 @@ public:
, m_filenameno{singleton().nameToNumber(filename)}
, m_waive{false}
, m_contentLineno{0} {}
explicit FileLine(const FileLine& from)
: m_msgEnIdx{from.m_msgEnIdx}
, m_filenameno{from.m_filenameno}
, m_waive{from.m_waive}
, m_contentLineno{from.m_contentLineno}
, m_firstLineno{from.m_firstLineno}
, m_firstColumn{from.m_firstColumn}
, m_lastLineno{from.m_lastLineno}
, m_lastColumn{from.m_lastColumn}
, m_contentp{from.m_contentp}
, m_parent{from.m_parent} {
if (m_contentp) m_contentp->refInc();
}
explicit FileLine(FileLine* fromp)
: m_msgEnIdx{fromp->m_msgEnIdx}
, m_filenameno{fromp->m_filenameno}
@ -275,6 +288,7 @@ public:
warnOn(V3ErrorCode::WIDTHEXPAND, flag);
warnOn(V3ErrorCode::WIDTHXZEXPAND, flag);
}
if (code == V3ErrorCode::E_UNSUPPORTED) warnOn(V3ErrorCode::COVERIGN, flag);
m_msgEnIdx = singleton().msgEnSetBit(m_msgEnIdx, code, flag);
}
void warnOff(V3ErrorCode code, bool flag) { warnOn(code, !flag); }

View File

@ -104,7 +104,7 @@ void V3Global::readFiles() {
// v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("parse.tree"));
V3Error::abortIfErrors();
if (!v3Global.opt.preprocOnly()) {
if (!v3Global.opt.preprocOnly() || v3Global.opt.preprocResolve()) {
// Resolve all modules cells refer to
V3LinkCells::link(v3Global.rootp(), &filter, &parseSyms);
}

View File

@ -56,7 +56,6 @@ private:
inline void V3LanguageWords::Singleton::init() {
// C++ keywords
// clang-format off
addKwd("nullptr", "C++ common word");
addKwd("abort", "C++ common word");
addKwd("alignas", "C++11 keyword");
addKwd("alignof", "C++11 keyword");
@ -91,7 +90,6 @@ inline void V3LanguageWords::Singleton::init() {
addKwd("decltype", "C++11 keyword");
addKwd("default", "C++ keyword");
addKwd("delete", "C++ keyword");
addKwd("deque", "C++ common word");
addKwd("do", "C++ keyword");
addKwd("double", "C++ keyword");
addKwd("dynamic_cast", "C++ keyword");
@ -113,12 +111,8 @@ inline void V3LanguageWords::Singleton::init() {
addKwd("int", "C++ keyword");
addKwd("interrupt", "C++ common word");
addKwd("iterator", "C++ common word");
addKwd("list", "C++ common word");
addKwd("long", "C++ keyword");
addKwd("map", "C++ common word");
addKwd("module", "C++ modules TS keyword");
addKwd("std::multimap", "C++ common word");
addKwd("std::multiset", "C++ common word");
addKwd("mutable", "C++ keyword");
addKwd("namespace", "C++ keyword");
addKwd("near", "C++ common word");
@ -126,6 +120,7 @@ inline void V3LanguageWords::Singleton::init() {
addKwd("noexcept", "C++11 keyword");
addKwd("not", "C++ keyword");
addKwd("not_eq", "C++ keyword");
addKwd("nullptr", "C++ common word");
addKwd("nullptr", "C++11 keyword");
addKwd("operator", "C++ keyword");
addKwd("or", "C++ keyword");
@ -142,7 +137,6 @@ inline void V3LanguageWords::Singleton::init() {
addKwd("requires", "C++20 keyword");
addKwd("restrict", "C++ keyword");
addKwd("return", "C++ keyword");
addKwd("set", "C++ common word");
addKwd("short", "C++ keyword");
addKwd("signed", "C++ keyword");
addKwd("sizeof", "C++ keyword");
@ -150,6 +144,13 @@ inline void V3LanguageWords::Singleton::init() {
addKwd("static", "C++ keyword");
addKwd("static_assert", "C++11 keyword");
addKwd("static_cast", "C++ keyword");
addKwd("std::deque", "C++ common word");
addKwd("std::list", "C++ common word");
addKwd("std::map", "C++ common word");
addKwd("std::multimap", "C++ common word");
addKwd("std::multiset", "C++ common word");
addKwd("std::set", "C++ common word");
addKwd("std::vector", "C++ common word");
addKwd("struct", "C++ keyword");
addKwd("switch", "C++ keyword");
addKwd("synchronized", "C++ TM TS keyword");
@ -171,7 +172,6 @@ inline void V3LanguageWords::Singleton::init() {
addKwd("union", "C++ keyword");
addKwd("unsigned", "C++ keyword");
addKwd("using", "C++ keyword");
addKwd("vector", "C++ common word");
addKwd("virtual", "C++ keyword");
addKwd("void", "C++ keyword");
addKwd("volatile", "C++ keyword");

View File

@ -287,8 +287,8 @@ class LifeVisitor final : public VNVisitor {
}
}
void visit(AstNodeAssign* nodep) override {
if (nodep->isTimingControl()) {
// V3Life doesn't understand time sense - don't optimize
if (nodep->isTimingControl() || VN_IS(nodep, AssignForce)) {
// V3Life doesn't understand time sense nor force assigns - don't optimize
setNoopt();
iterateChildren(nodep);
return;

View File

@ -986,8 +986,8 @@ class LinkDotFindVisitor final : public VNVisitor {
if (nodep->hierParams()) {
UINFO(1, "Found module with hier type parameters" << endl);
m_hierParamsName = nodep->name();
for (const AstNode* node = nodep->op2p(); node; node = node->nextp()) {
if (const AstTypedef* const tdef = VN_CAST(node, Typedef)) {
for (const AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
if (const AstTypedef* const tdef = VN_CAST(stmtp, Typedef)) {
UINFO(1, "Inserting hier type parameter typedef: " << tdef << endl);
VSymEnt* const upperSymp = m_curSymp ? m_curSymp : m_statep->rootEntp();
m_curSymp = m_modSymp = m_statep->insertBlock(upperSymp, nodep->name(),
@ -1013,6 +1013,7 @@ class LinkDotFindVisitor final : public VNVisitor {
VL_RESTORER(m_modBlockNum);
VL_RESTORER(m_modWithNum);
VL_RESTORER(m_modArgNum);
VL_RESTORER(m_explicitNew);
{
UINFO(4, " Link Class: " << nodep << endl);
VSymEnt* const upperSymp = m_curSymp;
@ -1176,12 +1177,39 @@ class LinkDotFindVisitor final : public VNVisitor {
}
// Change to appropriate package if extern declaration (vs definition)
if (nodep->classOrPackagep()) {
// class-in-class
AstDot* const dotp = VN_CAST(nodep->classOrPackagep(), Dot);
AstClassOrPackageRef* const cpackagerefp
= VN_CAST(nodep->classOrPackagep(), ClassOrPackageRef);
if (!cpackagerefp) {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: extern function definition with class-in-class");
} else {
if (dotp) {
AstClassOrPackageRef* const lhsp = VN_AS(dotp->lhsp(), ClassOrPackageRef);
m_statep->resolveClassOrPackage(m_curSymp, lhsp, false,
"External definition :: reference");
AstClass* const lhsclassp = VN_CAST(lhsp->classOrPackageSkipp(), Class);
if (!lhsclassp) {
nodep->v3error("Extern declaration's scope is not a defined class");
} else {
m_curSymp = m_statep->getNodeSym(lhsclassp);
upSymp = m_curSymp;
AstClassOrPackageRef* const rhsp = VN_AS(dotp->rhsp(), ClassOrPackageRef);
m_statep->resolveClassOrPackage(m_curSymp, rhsp, false,
"External definition :: reference");
AstClass* const rhsclassp = VN_CAST(rhsp->classOrPackageSkipp(), Class);
if (!rhsclassp) {
nodep->v3error("Extern declaration's scope is not a defined class");
} else {
m_curSymp = m_statep->getNodeSym(rhsclassp);
upSymp = m_curSymp;
if (!nodep->isExternDef()) {
// Move it to proper spot under the target class
nodep->unlinkFrBack();
rhsclassp->addStmtsp(nodep);
nodep->isExternDef(true); // So we check there's a matching extern
nodep->classOrPackagep()->unlinkFrBack()->deleteTree();
}
}
}
} else if (cpackagerefp) {
if (!cpackagerefp->classOrPackageSkipp()) {
m_statep->resolveClassOrPackage(m_curSymp, cpackagerefp, false,
"External definition :: reference");
@ -1200,6 +1228,8 @@ class LinkDotFindVisitor final : public VNVisitor {
nodep->classOrPackagep()->unlinkFrBack()->deleteTree();
}
}
} else {
v3fatalSrc("Unhandled extern function definition package");
}
}
// Set the class as package for iteration
@ -1501,9 +1531,9 @@ class LinkDotFindVisitor final : public VNVisitor {
if (const VSymEnt* const typedefEntp = m_curSymp->findIdFallback(m_hierParamsName)) {
const AstModule* modp = VN_CAST(typedefEntp->nodep(), Module);
for (const AstNode* node = modp ? modp->stmtsp() : nullptr; node;
node = node->nextp()) {
const AstTypedef* tdefp = VN_CAST(node, Typedef);
for (const AstNode* stmtp = modp ? modp->stmtsp() : nullptr; stmtp;
stmtp = stmtp->nextp()) {
const AstTypedef* tdefp = VN_CAST(stmtp, Typedef);
if (tdefp && tdefp->name() == nodep->name() && m_statep->forPrimary()) {
UINFO(8, "Replacing type of" << nodep << endl
@ -2336,11 +2366,6 @@ class LinkDotResolveVisitor final : public VNVisitor {
nodep->addStmtsp(superNewStmtp);
return superNewStmtp;
}
void taskFuncSwapCheck(AstNodeFTaskRef* nodep) {
if (nodep->taskp() && VN_IS(nodep->taskp(), Task) && VN_IS(nodep, FuncRef)) {
nodep->v3error("Illegal call of a task as a function: " << nodep->prettyNameQ());
}
}
void checkNoDot(AstNode* nodep) {
if (VL_UNLIKELY(m_ds.m_dotPos != DP_NONE)) {
// UINFO(9, indent() << "ds=" << m_ds.ascii() << endl);
@ -3779,7 +3804,6 @@ class LinkDotResolveVisitor final : public VNVisitor {
okSymp->cellErrorScopes(nodep);
}
}
taskFuncSwapCheck(nodep);
}
}
void visit(AstSelBit* nodep) override {
@ -4013,6 +4037,11 @@ class LinkDotResolveVisitor final : public VNVisitor {
LINKDOT_VISIT_START();
UINFO(5, indent() << "visit " << nodep << endl);
checkNoDot(nodep);
AstClass* const topclassp = VN_CAST(m_modp, Class);
if (nodep->isInterfaceClass() && topclassp && topclassp->isInterfaceClass()) {
nodep->v3error("Interface class shall not be nested within another interface class."
" (IEEE 1800-2023 8.26)");
}
VL_RESTORER(m_curSymp);
VL_RESTORER(m_modSymp);
VL_RESTORER(m_modp);

View File

@ -27,6 +27,7 @@
// Create a temporary __VIncrementX variable, assign the value of
// of the current variable (after the operation) to it. Substitute
// The original variable with the temporary one in the statement.
//
// prepost_stmt_visit
// PREADD/PRESUB/POSTADD/POSTSUB
// Increment/decrement the current variable by the given value.
@ -34,6 +35,12 @@
// the pre/post operations are treated equally and there is no
// need for a temporary variable.
//
// prepost_stmt_sel_visit
// For e.g. 'array[something_with_side_eff]++', common in UVM etc
// PREADD/PRESUB/POSTADD/POSTSUB
// Create temporary with array index.
// Increment/decrement using index of the temporary.
//
//*************************************************************************
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
@ -205,12 +212,69 @@ class LinkIncVisitor final : public VNVisitor {
void visit(AstPropSpec* nodep) override { unsupported_visit(nodep); }
void prepost_visit(AstNodeTriop* nodep) {
// Check if we are underneath a statement
if (!m_insStmtp) {
prepost_stmt_visit(nodep);
AstSelBit* const selbitp = VN_CAST(nodep->thsp(), SelBit);
if (!m_insStmtp && selbitp && VN_IS(selbitp->fromp(), NodeVarRef)
&& !selbitp->bitp()->isPure()) {
prepost_stmt_sel_visit(nodep);
} else {
prepost_expr_visit(nodep);
// Purity check was deferred at creation in verilog.y, check now
nodep->thsp()->purityCheck();
if (!m_insStmtp) {
prepost_stmt_visit(nodep);
} else {
prepost_expr_visit(nodep);
}
}
}
void prepost_stmt_sel_visit(AstNodeTriop* nodep) {
// Special case array[something]++, see comments at file top
// if (debug() >= 9) nodep->dumpTree("-pp-stmt-sel-in: ");
iterateChildren(nodep);
AstConst* const constp = VN_AS(nodep->lhsp(), Const);
UASSERT_OBJ(nodep, constp, "Expecting CONST");
AstConst* const newconstp = constp->cloneTree(true);
AstSelBit* const rdSelbitp = VN_CAST(nodep->rhsp(), SelBit);
AstNodeExpr* const rdFromp = rdSelbitp->fromp()->unlinkFrBack();
AstNodeExpr* const rdBitp = rdSelbitp->bitp()->unlinkFrBack();
AstSelBit* const wrSelbitp = VN_CAST(nodep->thsp(), SelBit);
AstNodeExpr* const wrFromp = wrSelbitp->fromp()->unlinkFrBack();
// Prepare a temporary variable
FileLine* const fl = nodep->fileline();
const string name = "__VincIndex"s + cvtToStr(++m_modIncrementsNum);
AstVar* const varp = new AstVar{
fl, VVarType::BLOCKTEMP, name, VFlagChildDType{},
new AstRefDType{fl, AstRefDType::FlagTypeOfExpr{}, rdBitp->cloneTree(true)}};
if (m_ftaskp) varp->funcLocal(true);
// Declare the variable
insertOnTop(varp);
// Define what operation will we be doing
AstAssign* const varAssignp
= new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, rdBitp};
AstNode* const newp = varAssignp;
AstNodeExpr* const valuep
= new AstSelBit{fl, rdFromp, new AstVarRef{fl, varp, VAccess::READ}};
AstNodeExpr* const storeTop
= new AstSelBit{fl, wrFromp, new AstVarRef{fl, varp, VAccess::READ}};
AstAssign* assignp;
if (VN_IS(nodep, PreSub) || VN_IS(nodep, PostSub)) {
assignp = new AstAssign{nodep->fileline(), storeTop,
new AstSub{nodep->fileline(), valuep, newconstp}};
} else {
assignp = new AstAssign{nodep->fileline(), storeTop,
new AstAdd{nodep->fileline(), valuep, newconstp}};
}
newp->addNext(assignp);
// if (debug() >= 9) newp->dumpTreeAndNext("-pp-stmt-sel-new: ");
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
void prepost_stmt_visit(AstNodeTriop* nodep) {
iterateChildren(nodep);
AstConst* const constp = VN_AS(nodep->lhsp(), Const);

View File

@ -340,22 +340,20 @@ class LinkParseVisitor final : public VNVisitor {
// Maybe this variable has a signal attribute
V3Config::applyVarAttr(m_modp, m_ftaskp, nodep);
if (v3Global.opt.publicFlatRW()
|| (v3Global.opt.publicDepth() && m_modp
&& (m_modp->level() - 1) <= v3Global.opt.publicDepth())) {
switch (nodep->varType()) {
case VVarType::VAR: // FALLTHRU
case VVarType::GPARAM: // FALLTHRU
case VVarType::LPARAM: // FALLTHRU
case VVarType::PORT: // FALLTHRU
case VVarType::WIRE: nodep->sigUserRWPublic(true); break;
default: break;
if (v3Global.opt.anyPublicFlat() && nodep->varType().isVPIAccessible()) {
if (v3Global.opt.publicFlatRW()) {
nodep->sigUserRWPublic(true);
} else if (v3Global.opt.publicParams() && nodep->isParam()) {
nodep->sigUserRWPublic(true);
} else if (m_modp && v3Global.opt.publicDepth()) {
if ((m_modp->level() - 1) <= v3Global.opt.publicDepth()) {
nodep->sigUserRWPublic(true);
} else if (VN_IS(m_modp, Package) && nodep->isParam()) {
nodep->sigUserRWPublic(true);
}
}
}
if (v3Global.opt.publicParams() && nodep->isParam()) nodep->sigUserRWPublic(true);
// We used modTrace before leveling, and we may now
// want to turn it off now that we know the levelizations
if (v3Global.opt.traceDepth() && m_modp
@ -720,6 +718,7 @@ class LinkParseVisitor final : public VNVisitor {
nodep->name(newName);
nodep->origName(newName);
}
iterateChildren(nodep);
}
void visit(AstGenCase* nodep) override {
++m_genblkNum;
@ -895,6 +894,13 @@ class LinkParseVisitor final : public VNVisitor {
}
iterateChildren(nodep);
}
void visit(AstPackageImport* nodep) override {
cleanFileline(nodep);
if (m_modp && !m_ftaskp && VN_IS(m_modp, Class)) {
nodep->v3error("Import statement directly within a class scope is illegal");
}
iterateChildren(nodep);
}
void visit(AstNode* nodep) override {
// Default: Just iterate

View File

@ -221,7 +221,8 @@ class LinkResolveVisitor final : public VNVisitor {
letp->user2(false);
return;
}
if (nodep->taskp() && (nodep->taskp()->dpiContext() || nodep->taskp()->dpiExport())) {
if (nodep->taskp() && !nodep->scopeNamep()
&& (nodep->taskp()->dpiContext() || nodep->taskp()->dpiExport())) {
nodep->scopeNamep(new AstScopeName{nodep->fileline(), false});
}
}

View File

@ -99,8 +99,10 @@ public:
}
void addLangExt(const string& langext, const V3LangCode& lc) {
// New language extension replaces any pre-existing one.
(void)m_langExts.erase(langext);
m_langExts[langext] = lc;
string addext = langext;
if (addext[0] == '.') addext = addext.substr(1);
(void)m_langExts.erase(addext);
m_langExts[addext] = lc;
}
void addLibExtV(const string& libext) {
@ -953,9 +955,6 @@ void V3Options::notify() VL_MT_DISABLED {
cmdfl->v3error("Unsupported: --timing and --savable not supported together");
}
// Mark options as available
m_available = true;
// --dump-tree-dot will turn on tree dumping.
if (!m_dumpLevel.count("tree") && m_dumpLevel.count("tree-dot")) {
m_dumpLevel["tree"] = m_dumpLevel["tree-dot"];
@ -963,9 +962,15 @@ void V3Options::notify() VL_MT_DISABLED {
// Sanity check of expected configuration
UASSERT(threads() >= 1, "'threads()' must return a value >= 1");
if (m_buildJobs == -1) m_buildJobs = 1;
if (m_verilateJobs == -1) m_verilateJobs = 1;
// Preprocessor defines based on options used
if (timing().isSetTrue()) V3PreShell::defineCmdLine("VERILATOR_TIMING", "1");
// === Leave last
// Mark options as available
m_available = true;
}
//######################################################################
@ -1127,8 +1132,15 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
// Plus options
DECL_OPTION("+define+", CbPartialMatch,
[this](const char* optp) VL_MT_DISABLED { addDefine(optp, true); });
DECL_OPTION("+incdir+", CbPartialMatch,
[this, &optdir](const char* optp) { addIncDirUser(parseFileArg(optdir, optp)); });
DECL_OPTION("+incdir+", CbPartialMatch, [this, &optdir](const char* optp) {
string dirs = optp;
string::size_type pos;
while ((pos = dirs.find('+')) != string::npos) {
addIncDirUser(parseFileArg(optdir, dirs.substr(0, pos)));
dirs = dirs.substr(pos + 1);
}
addIncDirUser(parseFileArg(optdir, dirs));
});
DECL_OPTION("+libext+", CbPartialMatch, [this](const char* optp) {
string exts = optp;
string::size_type pos;
@ -1224,8 +1236,10 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
}
});
DECL_OPTION("-compiler-include", CbVal, callStrSetter(&V3Options::addCompilerIncludes));
DECL_OPTION("-coverage", CbOnOff, [this](bool flag) { coverage(flag); });
DECL_OPTION("-converge-limit", Set, &m_convergeLimit);
DECL_OPTION("-coverage", CbOnOff, [this](bool flag) { coverage(flag); });
DECL_OPTION("-coverage-expr", OnOff, &m_coverageExpr);
DECL_OPTION("-coverage-expr-max", Set, &m_coverageExprMax);
DECL_OPTION("-coverage-line", OnOff, &m_coverageLine);
DECL_OPTION("-coverage-max-width", Set, &m_coverageMaxWidth);
DECL_OPTION("-coverage-toggle", OnOff, &m_coverageToggle);
@ -1268,8 +1282,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
DECL_OPTION("-dumpi-", CbPartialMatchVal, [this](const char* optp, const char* valp) {
m_dumpLevel[optp] = std::atoi(valp);
});
DECL_OPTION("-json-edit-nums", OnOff, &m_jsonEditNums);
DECL_OPTION("-json-ids", OnOff, &m_jsonIds);
DECL_OPTION("-E", CbOnOff, [this](bool flag) {
if (flag) {
m_stdPackage = false;
@ -1302,6 +1315,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
DECL_OPTION("-fconst", FOnOff, &m_fConst);
DECL_OPTION("-fconst-before-dfg", FOnOff, &m_fConstBeforeDfg);
DECL_OPTION("-fconst-bit-op-tree", FOnOff, &m_fConstBitOpTree);
DECL_OPTION("-fdead-assigns", FOnOff, &m_fDeadAssigns);
DECL_OPTION("-fdead-cells", FOnOff, &m_fDeadCells);
DECL_OPTION("-fdedup", FOnOff, &m_fDedupe);
DECL_OPTION("-fdfg", CbFOnOff, [this](bool flag) {
m_fDfgPreInline = flag;
@ -1316,8 +1331,6 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
});
DECL_OPTION("-fdfg-pre-inline", FOnOff, &m_fDfgPreInline);
DECL_OPTION("-fdfg-post-inline", FOnOff, &m_fDfgPostInline);
DECL_OPTION("-fdead-assigns", FOnOff, &m_fDeadAssigns);
DECL_OPTION("-fdead-cells", FOnOff, &m_fDeadCells);
DECL_OPTION("-fexpand", FOnOff, &m_fExpand);
DECL_OPTION("-ffunc-opt", CbFOnOff, [this](bool flag) { //
m_fFuncSplitCat = flag;
@ -1379,6 +1392,18 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
if (m_instrCountDpi < 0) fl->v3fatal("--instr-count-dpi must be non-negative: " << val);
});
DECL_OPTION("-json-edit-nums", OnOff, &m_jsonEditNums);
DECL_OPTION("-json-ids", OnOff, &m_jsonIds);
DECL_OPTION("-json-only", OnOff, &m_jsonOnly);
DECL_OPTION("-json-only-meta-output", CbVal, [this](const char* valp) {
m_jsonOnlyMetaOutput = valp;
m_jsonOnly = true;
});
DECL_OPTION("-json-only-output", CbVal, [this](const char* valp) {
m_jsonOnlyOutput = valp;
m_jsonOnly = true;
});
DECL_OPTION("-LDFLAGS", CbVal, callStrSetter(&V3Options::addLdLibs));
DECL_OPTION("-l2-name", Set, &m_l2Name);
DECL_OPTION("-no-l2name", CbCall, [this]() { m_l2Name = ""; }).undocumented(); // Historical
@ -1403,7 +1428,6 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
});
DECL_OPTION("-lint-only", OnOff, &m_lintOnly);
DECL_OPTION("-localize-max-size", Set, &m_localizeMaxSize);
DECL_OPTION("-main-top-name", Set, &m_mainTopName);
DECL_OPTION("-MAKEFLAGS", CbVal, callStrSetter(&V3Options::addMakeFlags));
DECL_OPTION("-MMD", OnOff, &m_makeDepend);
@ -1413,6 +1437,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
addIncDirFallback(m_makeDir); // Need to find generated files there too
});
DECL_OPTION("-main", OnOff, &m_main);
DECL_OPTION("-main-top-name", Set, &m_mainTopName);
DECL_OPTION("-make", CbVal, [this, fl](const char* valp) {
if (!std::strcmp(valp, "cmake")) {
m_cmake = true;
@ -1437,6 +1462,10 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
DECL_OPTION("-order-clock-delay", CbOnOff, [fl](bool /*flag*/) {
fl->v3warn(DEPRECATED, "Option order-clock-delay is deprecated and has no effect.");
});
DECL_OPTION("-output-groups", CbVal, [this, fl](const char* valp) {
m_outputGroups = std::atoi(valp);
if (m_outputGroups < 0) { fl->v3error("--output-groups must be >= 0: " << valp); }
});
DECL_OPTION("-output-split", Set, &m_outputSplit);
DECL_OPTION("-output-split-cfuncs", CbVal, [this, fl](const char* valp) {
m_outputSplitCFuncs = std::atoi(valp);
@ -1450,20 +1479,15 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
fl->v3error("--output-split-ctrace must be >= 0: " << valp);
}
});
DECL_OPTION("-output-groups", CbVal, [this, fl](const char* valp) {
m_outputGroups = std::atoi(valp);
if (m_outputGroups < 0) { fl->v3error("--output-groups must be >= 0: " << valp); }
});
DECL_OPTION("-P", Set, &m_preprocNoLine);
DECL_OPTION("-pvalue+", CbPartialMatch,
[this](const char* varp) { addParameter(varp, false); });
DECL_OPTION("-pins64", CbCall, [this]() { m_pinsBv = 65; });
DECL_OPTION("-no-pins64", CbCall, [this]() { m_pinsBv = 33; });
DECL_OPTION("-pins-bv", CbVal, [this, fl](const char* valp) {
m_pinsBv = std::atoi(valp);
if (m_pinsBv > 65) fl->v3fatal("--pins-bv maximum is 65: " << valp);
});
DECL_OPTION("-pins-inout-enables", OnOff, &m_pinsInoutEnables);
DECL_OPTION("-pins-sc-uint", CbOnOff, [this](bool flag) {
m_pinsScUint = flag;
if (!m_pinsScBigUint) m_pinsBv = 65;
@ -1473,7 +1497,6 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
m_pinsScBigUint = flag;
m_pinsBv = 513;
});
DECL_OPTION("-pins-inout-enables", OnOff, &m_pinsInoutEnables);
DECL_OPTION("-pins-uint8", OnOff, &m_pinsUint8);
DECL_OPTION("-pipe-filter", Set, &m_pipeFilter);
DECL_OPTION("-pp-comments", OnOff, &m_ppComments);
@ -1481,13 +1504,18 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
validateIdentifier(fl, valp, "--prefix");
m_prefix = valp;
});
DECL_OPTION("-preproc-resolve", OnOff, &m_preprocResolve);
DECL_OPTION("-preproc-token-limit", CbVal, [this, fl](const char* valp) {
m_preprocTokenLimit = std::atoi(valp);
if (m_preprocTokenLimit <= 0) fl->v3error("--preproc-token-limit must be > 0: " << valp);
});
DECL_OPTION("-private", CbCall, [this]() { m_public = false; });
DECL_OPTION("-prof-c", OnOff, &m_profC);
DECL_OPTION("-prof-cfuncs", CbCall, [this]() { m_profC = m_profCFuncs = true; });
DECL_OPTION("-profile-cfuncs", CbCall,
[this]() { m_profC = m_profCFuncs = true; }); // Renamed
DECL_OPTION("-prof-exec", OnOff, &m_profExec);
DECL_OPTION("-prof-pgo", OnOff, &m_profPgo);
DECL_OPTION("-profile-cfuncs", CbCall,
[this]() { m_profC = m_profCFuncs = true; }); // Renamed
DECL_OPTION("-protect-ids", OnOff, &m_protectIds);
DECL_OPTION("-protect-key", Set, &m_protectKey);
DECL_OPTION("-protect-lib", CbVal, [this, fl](const char* valp) {
@ -1501,10 +1529,14 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
m_publicFlatRW = flag;
v3Global.dpi(true);
});
DECL_OPTION("-public-ignore", CbOnOff, [this](bool flag) { m_publicIgnore = flag; });
DECL_OPTION("-public-params", CbOnOff, [this](bool flag) {
m_public_params = flag;
m_publicParams = flag;
v3Global.dpi(true);
});
DECL_OPTION("-pvalue+", CbPartialMatch,
[this](const char* varp) { addParameter(varp, false); });
DECL_OPTION("-quiet", CbOnOff, [this](bool flag) {
m_quietExit = flag;
m_quietStats = flag;
@ -1550,7 +1582,6 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
DECL_OPTION("-structs-packed", OnOff, &m_structsPacked);
DECL_OPTION("-sv", CbCall, [this]() { m_defaultLanguage = V3LangCode::L1800_2023; });
DECL_OPTION("-threads-coarsen", OnOff, &m_threadsCoarsen).undocumented(); // Debug
DECL_OPTION("-no-threads", CbCall, [this, fl]() {
fl->v3warn(DEPRECATED, "Option --no-threads is deprecated, use '--threads 1' instead");
m_threads = 1;
@ -1563,6 +1594,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
m_threads = 1;
}
});
DECL_OPTION("-threads-coarsen", OnOff, &m_threadsCoarsen).undocumented(); // Debug
DECL_OPTION("-threads-dpi", CbVal, [this, fl](const char* valp) {
if (!std::strcmp(valp, "all")) {
m_threadsDpiPure = true;
@ -1604,9 +1636,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
}
});
DECL_OPTION("-timing", OnOff, &m_timing);
DECL_OPTION("-top-module", Set, &m_topModule);
DECL_OPTION("-top", Set, &m_topModule);
DECL_OPTION("-no-trace-top", Set, &m_noTraceTop);
DECL_OPTION("-top-module", Set, &m_topModule);
DECL_OPTION("-trace", OnOff, &m_trace);
DECL_OPTION("-trace-coverage", OnOff, &m_traceCoverage);
DECL_OPTION("-trace-depth", Set, &m_traceDepth);
@ -1632,6 +1663,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
m_traceThreads = std::atoi(valp);
if (m_traceThreads < 1) fl->v3fatal("--trace-threads must be >= 1: " << valp);
});
DECL_OPTION("-no-trace-top", Set, &m_noTraceTop);
DECL_OPTION("-trace-underscore", OnOff, &m_traceUnderscore);
DECL_OPTION("-U", CbPartialMatch, &V3PreShell::undef);
@ -1649,6 +1681,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
V3Options::addLibraryFile(parseFileArg(optdir, valp));
});
DECL_OPTION("-valgrind", CbCall, []() {}); // Processed only in bin/verilator shell
DECL_OPTION("-verilate", OnOff, &m_verilate);
DECL_OPTION("-verilate-jobs", CbVal, [this, fl](const char* valp) {
int val = std::atoi(valp);
if (val < 0) {
@ -1660,17 +1693,12 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
}
m_verilateJobs = val;
});
DECL_OPTION("-verilate", OnOff, &m_verilate);
DECL_OPTION("-version", CbCall, [this]() {
showVersion(false);
std::exit(0);
});
DECL_OPTION("-vpi", OnOff, &m_vpi);
DECL_OPTION("-Wpedantic", CbCall, [this]() {
m_pedantic = true;
V3Error::pretendError(V3ErrorCode::ASSIGNIN, false);
});
DECL_OPTION("-Wall", CbCall, []() {
FileLine::globalWarnLintOff(false);
FileLine::globalWarnStyleOff(false);
@ -1713,6 +1741,10 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
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("-Wpedantic", CbCall, [this]() {
m_pedantic = true;
V3Error::pretendError(V3ErrorCode::ASSIGNIN, false);
});
DECL_OPTION("-Wwarn-", CbPartialMatch, [this, fl, &parser](const char* optp) VL_MT_DISABLED {
const V3ErrorCode code{optp};
if (code == V3ErrorCode::EC_ERROR) {
@ -1735,6 +1767,12 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
V3Error::pretendError(V3ErrorCode::UNUSEDSIGNAL, false);
V3Error::pretendError(V3ErrorCode::UNUSEDPARAM, false);
});
DECL_OPTION("-Wwarn-UNSUPPORTED", CbCall, []() {
FileLine::globalWarnOff(V3ErrorCode::E_UNSUPPORTED, false);
FileLine::globalWarnOff(V3ErrorCode::COVERIGN, false);
V3Error::pretendError(V3ErrorCode::E_UNSUPPORTED, false);
V3Error::pretendError(V3ErrorCode::COVERIGN, false);
});
DECL_OPTION("-Wwarn-WIDTH", CbCall, []() {
FileLine::globalWarnOff(V3ErrorCode::WIDTH, false);
V3Error::pretendError(V3ErrorCode::WIDTH, false);
@ -1776,19 +1814,11 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
m_xmlOutput = valp;
m_xmlOnly = true;
});
DECL_OPTION("-json-only", OnOff, &m_jsonOnly);
DECL_OPTION("-json-only-output", CbVal, [this](const char* valp) {
m_jsonOnlyOutput = valp;
m_jsonOnly = true;
});
DECL_OPTION("-json-only-meta-output", CbVal, [this](const char* valp) {
m_jsonOnlyMetaOutput = valp;
m_jsonOnly = true;
});
DECL_OPTION("-y", CbVal, [this, &optdir](const char* valp) {
addIncDirUser(parseFileArg(optdir, string{valp}));
});
parser.finalize();
for (int i = 0; i < argc;) {
@ -1837,8 +1867,6 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
++i;
}
}
if (m_buildJobs == -1) m_buildJobs = 1;
if (m_verilateJobs == -1) m_verilateJobs = 1;
}
//======================================================================

View File

@ -219,6 +219,7 @@ private:
V3StringSet m_fDfgPeepholeDisabled; // argument: -f[no-]dfg-peephole-<name>
bool m_preprocOnly = false; // main switch: -E
bool m_preprocResolve = false; // main switch: --preproc-resolve
bool m_makePhony = false; // main switch: -MP
bool m_preprocNoLine = false; // main switch: -P
bool m_assert = false; // main switch: --assert
@ -230,6 +231,7 @@ private:
bool m_build = false; // main switch: --build
bool m_cmake = false; // main switch: --make cmake
bool m_context = true; // main switch: --Wcontext
bool m_coverageExpr = false; // main switch: --coverage-expr
bool m_coverageLine = false; // main switch: --coverage-block
bool m_coverageToggle = false; // main switch: --coverage-toggle
bool m_coverageUnderscore = false; // main switch: --coverage-underscore
@ -255,6 +257,7 @@ private:
bool m_flatten = false; // main switch: --flatten
bool m_hierarchical = false; // main switch: --hierarchical
bool m_ignc = false; // main switch: --ignc
bool m_jsonOnly = false; // main switch: --json-only
bool m_lintOnly = false; // main switch: --lint-only
bool m_gmake = false; // main switch: --make gmake
bool m_main = false; // main switch: --main
@ -273,7 +276,8 @@ private:
bool m_protectIds = false; // main switch: --protect-ids
bool m_public = false; // main switch: --public
bool m_publicFlatRW = false; // main switch: --public-flat-rw
bool m_public_params = false; // main switch: --public-params
bool m_publicIgnore = false; // main switch: --public-ignore
bool m_publicParams = false; // main switch: --public-params
bool m_quietExit = false; // main switch: --quiet-exit
bool m_quietStats = false; // main switch: --quiet-stats
bool m_relativeIncludes = false; // main switch: --relative-includes
@ -301,9 +305,9 @@ private:
bool m_waiverMultiline = false; // main switch: --waiver-multiline
bool m_xInitialEdge = false; // main switch: --x-initial-edge
bool m_xmlOnly = false; // main switch: --xml-only
bool m_jsonOnly = false; // main switch: --json-only
int m_buildJobs = -1; // main switch: --build-jobs, -j
int m_coverageExprMax = 32; // main switch: --coverage-expr-max
int m_convergeLimit = 100; // main switch: --converge-limit
int m_coverageMaxWidth = 256; // main switch: --coverage-max-width
int m_expandLimit = 64; // main switch: --expand-limit
@ -323,6 +327,7 @@ private:
int m_outputSplitCFuncs = -1; // main switch: --output-split-cfuncs
int m_outputSplitCTrace = -1; // main switch: --output-split-ctrace
int m_pinsBv = 65; // main switch: --pins-bv
int m_preprocTokenLimit = 40000; // main switch: --preproc-token-limit
int m_publicDepth = 0; // main switch: --public-depth
int m_reloopLimit = 40; // main switch: --reloop-limit
VOptionBool m_skipIdentical; // main switch: --skip-identical
@ -350,6 +355,8 @@ private:
string m_exeName; // main switch: -o {name}
string m_flags; // main switch: -f {name}
string m_hierParamsFile; // main switch: --hierarchical-params-file
string m_jsonOnlyOutput; // main switch: --json-only-output
string m_jsonOnlyMetaOutput; // main switch: --json-only-meta-output
string m_l2Name; // main switch: --l2name; "" for top-module's name
string m_libCreate; // main switch: --lib-create {lib_name}
string m_mainTopName; // main switch: --main-top-name
@ -364,8 +371,6 @@ private:
string m_xAssign; // main switch: --x-assign
string m_xInitial; // main switch: --x-initial
string m_xmlOutput; // main switch: --xml-output
string m_jsonOnlyOutput; // main switch: --json-only-output
string m_jsonOnlyMetaOutput; // main switch: --json-only-meta-output
// Language is now held in FileLine, on a per-node basis. However we still
// have a concept of the default language at a global level.
@ -424,7 +429,9 @@ private:
void addLibExtV(const string& libext);
void optimize(int level);
void showVersion(bool verbose);
void coverage(bool flag) { m_coverageLine = m_coverageToggle = m_coverageUser = flag; }
void coverage(bool flag) {
m_coverageLine = m_coverageToggle = m_coverageExpr = m_coverageUser = flag;
}
static bool suffixed(const string& sw, const char* arg);
static string parseFileArg(const string& optdir, const string& relfilename);
string filePathCheckOneDir(const string& modname, const string& dirname);
@ -464,6 +471,8 @@ public:
bool preprocOnly() const { return m_preprocOnly; }
bool makePhony() const { return m_makePhony; }
bool preprocNoLine() const { return m_preprocNoLine; }
bool preprocResolve() const { return m_preprocResolve; }
int preprocTokenLimit() const { return m_preprocTokenLimit; }
bool underlineZero() const { return m_underlineZero; }
string flags() const { return m_flags; }
bool systemC() const VL_MT_SAFE { return m_systemC; }
@ -485,8 +494,9 @@ public:
bool cmake() const { return m_cmake; }
bool context() const VL_MT_SAFE { return m_context; }
bool coverage() const VL_MT_SAFE {
return m_coverageLine || m_coverageToggle || m_coverageUser;
return m_coverageLine || m_coverageToggle || m_coverageExpr || m_coverageUser;
}
bool coverageExpr() const { return m_coverageExpr; }
bool coverageLine() const { return m_coverageLine; }
bool coverageToggle() const { return m_coverageToggle; }
bool coverageUnderscore() const { return m_coverageUnderscore; }
@ -526,6 +536,7 @@ public:
bool traceUnderscore() const { return m_traceUnderscore; }
bool main() const { return m_main; }
bool outFormatOk() const { return m_outFormatOk; }
bool jsonOnly() const { return m_jsonOnly; }
bool keepTempFiles() const { return (V3Error::debugDefault() != 0); }
bool pedantic() const { return m_pedantic; }
bool pinsInoutEnables() const { return m_pinsInoutEnables; }
@ -541,8 +552,11 @@ public:
bool usesProfiler() const { return profExec() || profPgo(); }
bool protectIds() const VL_MT_SAFE { return m_protectIds; }
bool allPublic() const { return m_public; }
bool publicParams() const { return m_public_params; }
bool publicParams() const { return m_publicParams; }
bool publicOff() const { return m_publicIgnore; }
bool publicFlatRW() const { return m_publicFlatRW; }
int publicDepth() const { return m_publicDepth; }
bool anyPublicFlat() const { return m_publicParams || m_publicFlatRW || m_publicDepth; }
bool lintOnly() const VL_MT_SAFE { return m_lintOnly; }
bool ignc() const { return m_ignc; }
bool quietExit() const VL_MT_SAFE { return m_quietExit; }
@ -553,12 +567,12 @@ public:
bool waiverMultiline() const { return m_waiverMultiline; }
bool xInitialEdge() const { return m_xInitialEdge; }
bool xmlOnly() const { return m_xmlOnly; }
bool jsonOnly() const { return m_jsonOnly; }
bool serializeOnly() const { return m_xmlOnly || m_jsonOnly; }
bool topIfacesSupported() const { return lintOnly() && !hierarchical(); }
int buildJobs() const VL_MT_SAFE { return m_buildJobs; }
int convergeLimit() const { return m_convergeLimit; }
int coverageExprMax() const { return m_coverageExprMax; }
int coverageMaxWidth() const { return m_coverageMaxWidth; }
bool dumpTreeAddrids() const VL_MT_SAFE;
int expandLimit() const { return m_expandLimit; }
@ -577,7 +591,6 @@ public:
int outputSplitCTrace() const { return m_outputSplitCTrace; }
int outputGroups() const { return m_outputGroups; }
int pinsBv() const VL_MT_SAFE { return m_pinsBv; }
int publicDepth() const { return m_publicDepth; }
int reloopLimit() const { return m_reloopLimit; }
VOptionBool skipIdentical() const { return m_skipIdentical; }
bool stopFail() const { return m_stopFail; }
@ -614,6 +627,8 @@ public:
string exeName() const { return m_exeName != "" ? m_exeName : prefix(); }
string hierParamFile() const { return m_hierParamsFile; }
string jsonOnlyOutput() const { return m_jsonOnlyOutput; }
string jsonOnlyMetaOutput() const { return m_jsonOnlyMetaOutput; }
string l2Name() const { return m_l2Name; }
string libCreate() const { return m_libCreate; }
string libCreateName(bool shared) {
@ -641,8 +656,6 @@ public:
string xAssign() const { return m_xAssign; }
string xInitial() const { return m_xInitial; }
string xmlOutput() const { return m_xmlOutput; }
string jsonOnlyOutput() const { return m_jsonOnlyOutput; }
string jsonOnlyMetaOutput() const { return m_jsonOnlyMetaOutput; }
const V3StringSet& cppFiles() const { return m_cppFiles; }
const V3StringList& cFlags() const { return m_cFlags; }

View File

@ -1710,7 +1710,11 @@ class DpiImportCallVisitor final : public VNVisitor {
if (nodep->dpiImportWrapper()) {
if (nodep->dpiPure() ? !v3Global.opt.threadsDpiPure()
: !v3Global.opt.threadsDpiUnpure()) {
m_hasDpiHazard = true;
// If hierarchical DPI wrapper cost is not found or is of a 0 cost,
// we have a normal DPI which induces DPI hazard by default.
m_hasDpiHazard = V3Config::getProfileData(nodep->cname()) == 0;
UINFO(9, "DPI wrapper '" << nodep->cname()
<< "' has dpi hazard = " << m_hasDpiHazard << endl);
}
}
iterateChildren(nodep);

View File

@ -91,9 +91,9 @@ string V3Os::getenvStr(const string& envvar, const string& defaultValue) {
void V3Os::setenvStr(const string& envvar, const string& value, const string& why) {
if (why != "") {
UINFO(1, "export " << envvar << "=" << value << " # " << why << endl);
UINFO(1, "export " << envvar << "='" << value << "' # " << why << endl);
} else {
UINFO(1, "export " << envvar << "=" << value << endl);
UINFO(1, "export " << envvar << "='" << value << "'" << endl);
}
#if defined(_WIN32) || defined(__MINGW32__)
_putenv_s(envvar.c_str(), value.c_str());

View File

@ -543,18 +543,18 @@ class ParamProcessor final {
for (auto&& defaultValue : paramsIt->second) {
const auto pinIt = pins.find(defaultValue.first);
// If the pin does not have a value assigned, use the default one.
const AstNode* const node = pinIt == pins.end() ? defaultValue.second : pinIt->second;
const AstNode* const nodep = pinIt == pins.end() ? defaultValue.second : pinIt->second;
// This longname is not valid as verilog symbol, but ok, because it will be hashed
longname += "_" + defaultValue.first + "=";
// constp can be nullptr
if (const AstConst* const p = VN_CAST(node, Const)) {
if (const AstConst* const p = VN_CAST(nodep, Const)) {
// Treat modules parametrized with the same values but with different type as the
// same.
longname += p->num().ascii(false);
} else if (node) {
} else if (nodep) {
std::stringstream type;
V3EmitV::verilogForTree(node, type);
V3EmitV::verilogForTree(nodep, type);
longname += type.str();
}
}

View File

@ -356,7 +356,7 @@ void V3ParseImp::parseFile(FileLine* fileline, const string& modfilename, bool i
dumpInputsFile();
// Parse it
if (!v3Global.opt.preprocOnly()) {
if (!v3Global.opt.preprocOnly() || v3Global.opt.preprocResolve()) {
lexFile(modfilename);
} else {
m_ppBuffers.clear();

View File

@ -486,7 +486,7 @@ void V3PreProcImp::comment(const string& text) {
//}
// else ignore the comment we don't recognize
} // else no assertions
} else if (vlcomment) {
} else if (vlcomment && !(v3Global.opt.publicOff() && VString::startsWith(cmd, "public"))) {
if (VString::startsWith(cmd, "public_flat_rw")) {
// "/*verilator public_flat_rw @(foo) */" -> "/*verilator public_flat_rw*/ @(foo)"
string::size_type endOfCmd = std::strlen("public_flat_rw");
@ -963,9 +963,9 @@ int V3PreProcImp::getRawToken() {
if (m_lastLineno != m_lexp->m_tokFilelinep->lineno()) {
m_lastLineno = m_lexp->m_tokFilelinep->lineno();
m_tokensOnLine = 0;
} else if (++m_tokensOnLine > LINE_TOKEN_MAX) {
error("Too many preprocessor tokens on a line (>" + cvtToStr(LINE_TOKEN_MAX)
+ "); perhaps recursive `define");
} else if (++m_tokensOnLine > v3Global.opt.preprocTokenLimit()) {
error("Too many preprocessor tokens on a line (>"
+ cvtToStr(v3Global.opt.preprocTokenLimit()) + "); perhaps recursive `define");
tok = VP_EOF_ERROR;
}
@ -1065,6 +1065,11 @@ int V3PreProcImp::getStateToken() {
// FALLTHRU, handle as with VP_SYMBOL_JOIN
}
}
if (state() == ps_STRIFY) {
// Ignore joins and symbol joins in stringify as they don't affect the final string
if (tok == VP_JOIN) goto next_tok;
if (tok == VP_SYMBOL_JOIN) tok = VP_SYMBOL;
}
if (tok == VP_SYMBOL_JOIN // not else if, can fallthru from above if()
|| tok == VP_DEFREF_JOIN || tok == VP_JOIN) {
// a`` -> string doesn't include the ``, so can just grab next and continue

View File

@ -42,7 +42,6 @@ public:
// CONSTANTS
enum MiscConsts {
DEFINE_RECURSION_LEVEL_MAX = 1000, // How many `def substitutions before an error
LINE_TOKEN_MAX = 40000, // How many tokens on a line before an error
INCLUDE_DEPTH_MAX = 500, // How many `includes deep before an error
// Streams deep (sometimes `def deep) before an error.
// Set more than DEFINE_RECURSION_LEVEL_MAX or INCLUDE_DEPTH_MAX.

View File

@ -19,6 +19,7 @@
#include "V3ProtectLib.h"
#include "V3Hasher.h"
#include "V3InstrCount.h"
#include "V3String.h"
#include "V3Task.h"
@ -98,6 +99,32 @@ class ProtectVisitor final : public VNVisitor {
txtp->addNodesp(new AstComment{fl, comment});
}
void configSection(AstNodeModule* modp, AstTextBlock* txtp, FileLine* fl) {
txtp->addText(fl, "\n`ifdef VERILATOR\n");
txtp->addText(fl, "`verilator_config\n");
// The `eval` function is called inside both update functions. As those functions
// are created by text bashing, we need to find cost of `_eval` which is the first function
// with a real cost in AST.
uint32_t cost = 0;
modp->foreach([&cost](AstCFunc* cfuncp) {
if (cfuncp->name() == "_eval") cost = V3InstrCount::count(cfuncp, false);
});
txtp->addText(fl, "profile_data -hier-dpi \"" + m_libName
+ "_protectlib_combo_update\" -cost 64'd" + std::to_string(cost)
+ "\n");
txtp->addText(fl, "profile_data -hier-dpi \"" + m_libName
+ "_protectlib_seq_update\" -cost 64'd" + std::to_string(cost)
+ "\n");
// Mark remaining NDA protectlib wrapper DPIs as non-hazardous by deliberately forwarding
// them with non-zero cost.
txtp->addText(fl, "profile_data -hier-dpi \"" + m_libName
+ "_protectlib_combo_ignore\" -cost 64'd1\n");
txtp->addText(fl, "`verilog\n");
txtp->addText(fl, "`endif\n");
}
void hashComment(AstTextBlock* txtp, FileLine* fl) {
addComment(txtp, fl, "Checks to make sure the .sv wrapper and library agree");
}
@ -283,6 +310,9 @@ class ProtectVisitor final : public VNVisitor {
txtp->addText(fl, "final " + m_libName + "_protectlib_final(handle__V);\n\n");
txtp->addText(fl, "endmodule\n");
configSection(modp, txtp, fl);
m_vfilep->tblockp(txtp);
}

View File

@ -629,18 +629,22 @@ class ConstraintExprVisitor final : public VNVisitor {
const uint32_t unpackedDimensions = dims.second;
dimension = unpackedDimensions;
}
if (VN_IS(varp->dtypeSkipRefp(), StructDType)
&& !VN_AS(varp->dtypeSkipRefp(), StructDType)->packed()) {
VN_AS(varp->dtypeSkipRefp(), StructDType)->markConstrainedRand(true);
dimension = 1;
}
methodp->dtypeSetVoid();
AstClass* const classp = VN_AS(varp->user2p(), Class);
AstVarRef* const varRefp
= new AstVarRef{varp->fileline(), classp, varp, VAccess::WRITE};
varRefp->classOrPackagep(classOrPackagep);
methodp->addPinsp(varRefp);
size_t width = varp->width();
AstNodeDType* tmpDtypep = varp->dtypep();
while (VN_IS(tmpDtypep, UnpackArrayDType) || VN_IS(tmpDtypep, DynArrayDType)
|| VN_IS(tmpDtypep, QueueDType) || VN_IS(tmpDtypep, AssocArrayDType))
tmpDtypep = tmpDtypep->subDTypep();
width = tmpDtypep->width();
const size_t width = tmpDtypep->width();
methodp->addPinsp(
new AstConst{varp->dtypep()->fileline(), AstConst::Unsized64{}, width});
AstNodeExpr* const varnamep
@ -706,42 +710,73 @@ class ConstraintExprVisitor final : public VNVisitor {
editSMT(nodep, nodep->fromp(), lsbp, msbp);
}
void visit(AstStructSel* nodep) override {
if (VN_IS(nodep->fromp()->dtypep()->skipRefp(), StructDType)) {
AstNodeExpr* const fromp = nodep->fromp();
if (VN_IS(fromp, StructSel)) {
VN_AS(fromp->dtypep()->skipRefp(), StructDType)->markConstrainedRand(true);
}
AstMemberDType* memberp = VN_AS(fromp->dtypep()->skipRefp(), StructDType)->membersp();
while (memberp) {
if (memberp->name() == nodep->name()) {
memberp->markConstrainedRand(true);
break;
} else
memberp = VN_CAST(memberp->nextp(), MemberDType);
}
}
iterateChildren(nodep);
if (editFormat(nodep)) return;
FileLine* const fl = nodep->fileline();
AstSFormatF* const newp
= new AstSFormatF{fl, nodep->fromp()->name() + "." + nodep->name(), false, nullptr};
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstAssocSel* nodep) override {
if (editFormat(nodep)) return;
FileLine* const fl = nodep->fileline();
if (VN_IS(nodep->bitp(), CvtPackString)) {
// Extract and truncate the string index to fit within 64 bits
if (VN_IS(nodep->bitp(), CvtPackString) && VN_IS(nodep->bitp()->dtypep(), BasicDType)) {
AstCvtPackString* const stringp = VN_AS(nodep->bitp(), CvtPackString);
VNRelinker handle;
AstNodeExpr* const strIdxp = new AstSFormatF{
fl, "#x%16x", false,
new AstAnd{fl, stringp->lhsp()->unlinkFrBack(&handle),
new AstConst(fl, AstConst::Unsized64{}, 0xFFFFFFFFFFFFFFFF)}};
handle.relink(strIdxp);
editSMT(nodep, nodep->fromp(), strIdxp);
} else {
VNRelinker handle;
const int actual_width = nodep->bitp()->width();
std::string fmt;
// Normalize to standard bit width
if (actual_width <= 8) {
fmt = "#x%2x";
} else if (actual_width <= 16) {
fmt = "#x%4x";
} else if (actual_width <= 32) {
fmt = "#x%8x";
} else if (actual_width <= 64) {
fmt = "#x%16x";
} else {
nodep->v3warn(CONSTRAINTIGN,
"Unsupported: Associative array index "
"widths of more than 64 bits during constraint randomization.");
return;
const size_t stringSize = VN_AS(stringp->lhsp(), Const)->width();
if (stringSize > 128) {
stringp->v3warn(
CONSTRAINTIGN,
"Unsupported: Constrained randomization of associative array keys of "
<< stringSize << "bits, limit is 128 bits");
}
VNRelinker handle;
AstNodeExpr* const idxp
= new AstSFormatF{fl, fmt, false, nodep->bitp()->unlinkFrBack(&handle)};
= new AstSFormatF{fl, "#x%32x", false, stringp->lhsp()->unlinkFrBack(&handle)};
handle.relink(idxp);
editSMT(nodep, nodep->fromp(), idxp);
} else {
if (VN_IS(nodep->bitp()->dtypep(), BasicDType)
|| (VN_IS(nodep->bitp()->dtypep(), StructDType)
&& VN_AS(nodep->bitp()->dtypep(), StructDType)->packed())
|| VN_IS(nodep->bitp()->dtypep(), EnumDType)
|| VN_IS(nodep->bitp()->dtypep(), PackArrayDType)) {
VNRelinker handle;
const int actual_width = nodep->bitp()->width();
std::string fmt;
// Normalize to standard bit width
if (actual_width <= 8) {
fmt = "#x%2x";
} else if (actual_width <= 16) {
fmt = "#x%4x";
} else {
fmt = "#x%" + std::to_string(VL_WORDS_I(actual_width) * 8) + "x";
}
AstNodeExpr* const idxp
= new AstSFormatF{fl, fmt, false, nodep->bitp()->unlinkFrBack(&handle)};
handle.relink(idxp);
editSMT(nodep, nodep->fromp(), idxp);
} else {
nodep->bitp()->v3error(
"Illegal non-integral expression or subexpression in random constraint."
" (IEEE 1800-2023 18.3)");
}
}
}
void visit(AstArraySel* nodep) override {
@ -1450,11 +1485,8 @@ class RandomizeVisitor final : public VNVisitor {
AstNodeStmt* stmtsp = nullptr;
auto createLoopIndex = [&](AstNodeDType* tempDTypep) {
if (VN_IS(tempDTypep, AssocArrayDType)) {
return new AstVar{
fl, VVarType::VAR, uniqueNamep->get(""),
dtypep->findBasicDType(
((AstBasicDType*)VN_AS(tempDTypep, AssocArrayDType)->keyDTypep())
->keyword())};
return new AstVar{fl, VVarType::VAR, uniqueNamep->get(""),
VN_AS(tempDTypep, AssocArrayDType)->keyDTypep()};
}
return new AstVar{fl, VVarType::VAR, uniqueNamep->get(""),
dtypep->findBasicDType(VBasicDTypeKwd::UINT32)};

View File

@ -70,6 +70,7 @@ AstCFunc* makeSubFunction(AstNetlist* netlistp, const string& name, bool slow) {
AstCFunc* makeTopFunction(AstNetlist* netlistp, const string& name, bool slow) {
AstCFunc* const funcp = makeSubFunction(netlistp, name, slow);
funcp->entryPoint(true);
funcp->keepIfEmpty(true);
return funcp;
}
@ -788,6 +789,10 @@ const TriggerKit createTriggers(AstNetlist* netlistp, AstCFunc* const initFuncp,
// The debug code might leak signal names, so simply delete it when using --protect-ids
if (v3Global.opt.protectIds()) dumpp->stmtsp()->unlinkFrBackWithNext()->deleteTree();
// These might get large when we have a lot of triggers, so split if necessary
splitCheck(funcp);
splitCheck(dumpp);
return {vscp, funcp, dumpp, map};
}

View File

@ -182,16 +182,11 @@ class SenExprBuilder final {
case VEdgeType::ET_EVENT: {
UASSERT_OBJ(v3Global.hasEvents(), senItemp, "Inconsistent");
{
// If the event is fired, set up the clearing process
AstCMethodHard* const callp = new AstCMethodHard{flp, currp(), "isFired"};
callp->dtypeSetBit();
AstIf* const ifp = new AstIf{flp, callp};
m_postUpdates.push_back(ifp);
// Clear 'fired' state when done
// No need to check if the event was fired, we need the flag clear regardless
AstCMethodHard* const clearp = new AstCMethodHard{flp, currp(), "clearFired"};
clearp->dtypeSetVoid();
ifp->addThensp(clearp->makeStmt());
m_postUpdates.push_back(clearp->makeStmt());
}
// Get 'fired' state

View File

@ -78,8 +78,7 @@ private:
// Cleared on each always/assignw
const VNUser1InUse m_inuser1;
// AstVar/AstVarScope::user1p() -> See AuxAstVar via m_varAux
// AstConst::user1() -> bool. This AstConst (allocated by this class) is in use
// AstNode::user1p() -> See AuxAstVar via m_varAux
enum VarUsage : uint8_t { VU_NONE = 0, VU_LV = 1, VU_RV = 2, VU_LVDLY = 4 };
@ -96,6 +95,32 @@ private:
AstUser1Allocator<AstNode, AuxVariable> m_varAux;
// We want to re-use allocated constants across calls to clear(), but we want to be able
// to 'clear()' fast, so we use a generation number based allocator.
struct ConstAllocator final {
size_t m_generation = 0;
size_t m_nextFree = 0;
std::deque<AstConst*> m_constps;
AstConst* allocate(size_t currentGeneration, AstNode* nodep) {
if (m_generation != currentGeneration) {
m_generation = currentGeneration;
m_nextFree = 0;
}
UASSERT_OBJ(m_nextFree <= m_constps.size(), nodep, "Should only allocate at end");
if (m_nextFree == m_constps.size()) {
m_constps.push_back(
new AstConst{nodep->fileline(), AstConst::DTyped{}, nodep->dtypep()});
}
AstConst* const constp = m_constps[m_nextFree++];
constp->num().nodep(nodep);
return constp;
}
~ConstAllocator() {
for (AstConst* const constp : m_constps) VL_DO_DANGLING(delete constp, constp);
}
};
// STATE
// Major mode
bool m_checkOnly; ///< Checking only (no simulation) mode
@ -113,8 +138,9 @@ private:
int m_dataCount; ///< Bytes of data
AstJumpGo* m_jumpp = nullptr; ///< Jump label we're branching from
// Simulating:
std::unordered_map<const AstNodeDType*, std::deque<AstConst*>>
m_constps; ///< Lists of all AstConst* allocated per dtype
// Allocators for constants of various data types
std::unordered_map<const AstNodeDType*, ConstAllocator> m_constps;
size_t m_constGeneration = 0;
std::vector<SimStackNode*> m_callStack; ///< Call stack for verbose error messages
// Cleanup
@ -223,37 +249,8 @@ public:
// Simulation METHODS
private:
AstConst* allocConst(AstNode* nodep) {
// Save time - kept a list of allocated but unused values
// It would be more efficient to do this by size, but the extra accounting
// slows things down more than we gain.
AstConst* constp;
// Grab free list corresponding to this dtype
std::deque<AstConst*>& freeList = m_constps[nodep->dtypep()];
bool allocNewConst = true;
if (!freeList.empty()) {
constp = freeList.front();
if (!constp->user1()) {
// Front of free list is free, reuse it (otherwise allocate new node)
allocNewConst = false; // No need to allocate
// Mark the AstConst node as used, and move it to the back of the free list. This
// ensures that when all AstConst instances within the list are used, then the
// front of the list will be marked as used, in which case the enclosing 'if' will
// fail and we fall back to allocation.
constp->user1(1);
freeList.pop_front();
freeList.push_back(constp);
// configure const
constp->num().nodep(nodep);
}
}
if (allocNewConst) {
// Need to allocate new constant
constp = new AstConst{nodep->fileline(), AstConst::DTyped{}, nodep->dtypep()};
// Mark as in use, add to free list for later reuse
constp->user1(1);
freeList.push_back(constp);
}
return constp;
// Allocate a constant with this dtype. Reuse them across a 'clear()' call for efficiency.
return m_constps[nodep->dtypep()].allocate(m_constGeneration, nodep);
}
public:
@ -850,6 +847,8 @@ private:
if (!itemp) {
clearOptimizable(nodep, "Array initialization has too few elements, need element "
+ cvtToStr(offset));
} else if (AstConst* const constp = VN_CAST(itemp, Const)) {
setValue(nodep, constp);
} else {
setValue(nodep, fetchValue(itemp));
}
@ -1256,6 +1255,7 @@ public:
AstNode::user1ClearTree();
m_varAux.clear();
++m_constGeneration;
}
void mainTableCheck(AstNode* nodep) {
setMode(true /*scoped*/, true /*checking*/, false /*params*/);
@ -1274,9 +1274,6 @@ public:
mainGuts(nodep);
}
~SimulateVisitor() override {
for (const auto& pair : m_constps) {
for (AstConst* const constp : pair.second) delete constp;
}
m_constps.clear();
for (AstNode* ip : m_reclaimValuesp) delete ip;
m_reclaimValuesp.clear();

View File

@ -900,8 +900,9 @@ public:
const AstBasicDType* basicp() const { return m_basicp; }
// Make a plan for variables after split
// when skipUnused==true, split variable for unread bits will not be created.
std::vector<SplitNewVar> splitPlan(bool skipUnused) const {
std::vector<SplitNewVar> splitPlan(const AstVar* varp, bool skipUnused) const {
UASSERT(m_dedupDone, "dedup() must be called before");
AstNodeDType* const dtypep = varp->dtypeSkipRefp();
std::vector<SplitNewVar> plan;
std::vector<std::pair<int, bool>> points; // <bit location, is end>
points.reserve(m_lhs.size() * 2 + 2); // 2 points will be added per one PackedVarRefEntry
@ -909,9 +910,17 @@ public:
points.emplace_back(ref.lsb(), false); // Start of a region
points.emplace_back(ref.msb() + 1, true); // End of a region
}
int bit_hi, bit_lo;
if (basicp() == dtypep) {
bit_hi = basicp()->hi();
bit_lo = basicp()->lo();
} else { // packed struct, packed array. lo is 0
bit_hi = dtypep->width() - 1;
bit_lo = 0;
}
if (skipUnused && !m_rhs.empty()) { // Range to be read must be kept, so add points here
int lsb = m_basicp->hi() + 1;
int msb = m_basicp->lo() - 1;
int lsb = bit_hi + 1;
int msb = bit_lo - 1;
for (const PackedVarRefEntry& ref : m_rhs) {
lsb = std::min(lsb, ref.lsb());
msb = std::max(msb, ref.msb());
@ -921,8 +930,8 @@ public:
points.emplace_back(msb + 1, true);
}
if (!skipUnused) { // All bits are necessary
points.emplace_back(m_basicp->lo(), false);
points.emplace_back(m_basicp->hi() + 1, true);
points.emplace_back(bit_lo, false);
points.emplace_back(bit_hi + 1, true);
}
std::sort(points.begin(), points.end(), SortByFirst());
@ -1154,7 +1163,7 @@ class SplitPackedVarVisitor final : public VNVisitor, public SplitVarImpl {
<< " which has " << ref.lhs().size() << " lhs refs and "
<< ref.rhs().size() << " rhs refs will be split.\n");
std::vector<SplitNewVar> vars
= ref.splitPlan(!varp->isTrace()); // If traced, all bit must be kept
= ref.splitPlan(varp, !varp->isTrace()); // If traced, all bit must be kept
if (vars.empty()) continue;
if (vars.size() == 1 && vars.front().bitwidth() == varp->width())
continue; // No split

View File

@ -27,6 +27,7 @@
#include "V3Task.h"
#include "V3Config.h"
#include "V3Const.h"
#include "V3EmitCBase.h"
#include "V3Graph.h"
@ -372,6 +373,7 @@ class TaskVisitor final : public VNVisitor {
// STATE - across all visitors
DpiCFuncs m_dpiNames; // Map of all created DPI functions
VDouble0 m_statInlines; // Statistic tracking
VDouble0 m_statHierDpisWithCosts; // Statistic tracking
// METHODS
@ -980,6 +982,11 @@ class TaskVisitor final : public VNVisitor {
funcp->isMethod(false);
funcp->protect(false);
funcp->dpiPure(nodep->dpiPure());
const int cost = static_cast<int>(V3Config::getProfileData(funcp->name()));
m_statHierDpisWithCosts += (cost != 0);
funcp->cost(cost);
// Add DPI Import to top, since it's a global function
m_topScopep->scopep()->addBlocksp(funcp);
makePortList(nodep, funcp);
@ -1259,6 +1266,8 @@ class TaskVisitor final : public VNVisitor {
if (nodep->name() == "new") cfuncp->isConstructor(true);
if (cfuncp->dpiExportImpl()) cfuncp->cname(nodep->cname());
if (cfuncp->dpiImportWrapper()) cfuncp->cname(nodep->cname());
if (!nodep->dpiImport() && !nodep->taskPublic()) {
// Need symbol table
cfuncp->argTypes(EmitCBase::symClassVar());
@ -1613,7 +1622,11 @@ public:
: m_statep{statep} {
iterate(nodep);
}
~TaskVisitor() { V3Stats::addStat("Optimizations, Functions inlined", m_statInlines); }
~TaskVisitor() {
V3Stats::addStat("Optimizations, Functions inlined", m_statInlines);
V3Stats::addStat("Optimizations, Hierarchical DPI wrappers with costs",
m_statHierDpisWithCosts);
}
};
//######################################################################

View File

@ -57,7 +57,7 @@ class UnrollVisitor final : public VNVisitor {
bool cantUnroll(AstNode* nodep, const char* reason) const {
if (m_generate)
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Can't unroll generate for; " << reason);
UINFO(3, " Can't Unroll: " << reason << " :" << nodep << endl);
UINFO(4, " Can't Unroll: " << reason << " :" << nodep << endl);
// if (debug() >= 9) nodep->dumpTree("- cant: ");
V3Stats::addStatSum("Unrolling gave up, "s + reason, 1);
return false;
@ -210,7 +210,7 @@ class UnrollVisitor final : public VNVisitor {
SimulateVisitor simvis;
simvis.mainParamEmulate(clonep);
if (!simvis.optimizable()) {
UINFO(3, "Unable to simulate" << endl);
UINFO(4, "Unable to simulate" << endl);
if (debug() >= 9) nodep->dumpTree("- _simtree: ");
VL_DO_DANGLING(clonep->deleteTree(), clonep);
return false;

View File

@ -2874,8 +2874,8 @@ class WidthVisitor final : public VNVisitor {
if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
// Iterate into subDTypep() to resolve that type and update pointer.
nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
nodep->dtypep(nodep); // The member itself, not subDtype
nodep->widthFromSub(nodep->subDTypep());
nodep->dtypeFrom(nodep->subDTypep());
if (nodep->valuep()) {
userIterateAndNext(nodep->valuep(), WidthVP{nodep->dtypep(), PRELIM}.p());
iterateCheckAssign(nodep, "Initial value", nodep->valuep(), FINAL, nodep->dtypep());
@ -3929,11 +3929,14 @@ class WidthVisitor final : public VNVisitor {
AstNodeExpr* argsp = nullptr;
if (nodep->pinsp()) argsp = nodep->pinsp()->unlinkFrBackWithNext();
AstNodeFTaskRef* newp = nullptr;
if (VN_IS(ftaskp, Task)) {
newp = new AstTaskRef{nodep->fileline(), VN_AS(ftaskp, Task), argsp};
// We use m_vup to determine task or function, so that later error checks
// for funcref->task and taskref->func will pick up properly
if (!m_vup) { // Called as task
newp = new AstTaskRef{nodep->fileline(), nodep->name(), argsp};
} else {
newp = new AstFuncRef{nodep->fileline(), VN_AS(ftaskp, Func), argsp};
newp = new AstFuncRef{nodep->fileline(), nodep->name(), argsp};
}
newp->taskp(ftaskp); // Not passed above as might be func vs task mismatch
newp->classOrPackagep(classp);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
@ -3941,7 +3944,18 @@ class WidthVisitor final : public VNVisitor {
nodep->taskp(ftaskp);
nodep->dtypeFrom(ftaskp);
nodep->classOrPackagep(classp);
if (VN_IS(ftaskp, Task)) nodep->dtypeSetVoid();
if (VN_IS(ftaskp, Task)) {
if (!m_vup) {
nodep->dtypeSetVoid();
} else {
if (m_vup->prelim()) {
nodep->v3error(
"Cannot call a task/void-function as a member function: "
<< nodep->prettyNameQ());
}
nodep->dtypeSetVoid();
}
}
if (withp) nodep->addPinsp(withp);
processFTaskRefArgs(nodep);
}
@ -4223,9 +4237,8 @@ class WidthVisitor final : public VNVisitor {
UASSERT_OBJ(classp, nodep, "Unlinked classOrPackagep()");
UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked taskp()");
}
userIterate(nodep->taskp(), nullptr);
if (!assign) nodep->dtypeFrom(nodep->taskp());
processFTaskRefArgs(nodep);
if (!assign) nodep->dtypeFrom(nodep->taskp());
}
void visit(AstNewCopy* nodep) override {
if (nodep->didWidthAndSet()) return;
@ -4561,8 +4574,8 @@ class WidthVisitor final : public VNVisitor {
if (patp) {
// Don't want the RHS an array
patp->dtypep(arrayDtp->subDTypep());
allConstant &= VN_IS(patp->lhssp(), Const);
patp->dtypep(arrayDtp->subDTypep());
AstNodeExpr* const valuep = patternMemberValueIterate(patp);
if (VN_IS(arrayDtp, UnpackArrayDType)) {
if (!newp) {
@ -5825,7 +5838,15 @@ class WidthVisitor final : public VNVisitor {
}
void visit(AstFuncRef* nodep) override {
visit(static_cast<AstNodeFTaskRef*>(nodep));
nodep->dtypeFrom(nodep->taskp());
if (nodep->taskp() && VN_IS(nodep->taskp(), Task)) {
UASSERT_OBJ(m_vup, nodep, "Function reference where widthed expression expection");
if (m_vup->prelim())
nodep->v3error(
"Cannot call a task/void-function as a function: " << nodep->prettyNameQ());
nodep->dtypeSetVoid();
} else {
nodep->dtypeFrom(nodep->taskp());
}
if (nodep->fileline()->timingOn()) {
AstNodeModule* const classp = nodep->classOrPackagep();
if (nodep->name() == "self" && classp->name() == "process") {
@ -6086,17 +6107,17 @@ class WidthVisitor final : public VNVisitor {
nodep->v3error("Cannot call non-static member function "
<< nodep->prettyNameQ() << " without object (IEEE 1800-2023 8.10)");
}
userIterate(nodep->taskp(), nullptr);
// And do the arguments to the task/function too
processFTaskRefArgs(nodep);
nodep->addPinsp(withp);
nodep->didWidth(true);
// See steps that follow in visit(AstFuncRef*)
}
void visit(AstNodeProcedure* nodep) override {
assertAtStatement(nodep);
VL_RESTORER(m_procedurep);
m_procedurep = nodep;
userIterateChildren(nodep, nullptr);
m_procedurep = nullptr;
}
void visit(AstSenItem* nodep) override {
UASSERT_OBJ(nodep->isClocked(), nodep, "Invalid edge");

View File

@ -100,9 +100,15 @@ private:
if (local || prot) {
const auto refClassp = VN_CAST(m_modp, Class);
const char* how = nullptr;
if (local && defClassp && refClassp != defClassp) {
// Inner nested classes can access `local` or `protected` members of their outer class
const auto nestedAccess = [refClassp](const AstClass*, const AstNode* memberp) {
return memberp == refClassp;
};
if (local && defClassp
&& ((refClassp != defClassp) && !(defClassp->existsMember(nestedAccess)))) {
how = "'local'";
} else if (prot && defClassp && !AstClass::isClassExtendedFrom(refClassp, defClassp)) {
} else if (prot && defClassp && !AstClass::isClassExtendedFrom(refClassp, defClassp)
&& !(defClassp->existsMember(nestedAccess))) {
how = "'protected'";
}
if (how) {

View File

@ -305,13 +305,19 @@ class WidthSelVisitor final : public VNVisitor {
VL_DO_DANGLING(pushDeletep(nodep), nodep);
} else if (VN_IS(ddtypep, BasicDType) && ddtypep->isString()) {
// SELBIT(string, index) -> GETC(string, index)
const AstNodeVarRef* const varrefp = VN_CAST(fromp, NodeVarRef);
if (!varrefp) {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: String array operation on non-variable");
AstNodeExpr* exprp = fromp;
while (true) {
if (AstMemberSel* const memberselp = VN_CAST(exprp, MemberSel)) {
exprp = memberselp->fromp();
} else if (AstStructSel* const structselp = VN_CAST(exprp, StructSel)) {
exprp = structselp->fromp();
} else {
break;
}
}
const AstNodeVarRef* const varrefp = VN_CAST(exprp, NodeVarRef);
AstNodeExpr* newp;
if (varrefp && varrefp->access().isReadOnly()) {
if (!varrefp || varrefp->access().isReadOnly()) {
newp = new AstGetcN{nodep->fileline(), fromp, rhsp};
} else {
newp = new AstGetcRefN{nodep->fileline(), fromp, rhsp};

View File

@ -141,6 +141,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
-?"-cost" { FL; return yVLT_D_COST; }
-?"-file" { FL; return yVLT_D_FILE; }
-?"-function" { FL; return yVLT_D_FUNCTION; }
-?"-hier-dpi" { FL; return yVLT_D_HIER_DPI; }
-?"-levels" { FL; return yVLT_D_LEVELS; }
-?"-lines" { FL; return yVLT_D_LINES; }
-?"-match" { FL; return yVLT_D_MATCH; }
@ -1062,7 +1063,11 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
"`protect" { FL_FWD; FL_BRK; }
"`remove_gatenames" { FL_FWD; FL_BRK; } // Verilog-XL compatibility
"`remove_netnames" { FL_FWD; FL_BRK; } // Verilog-XL compatibility
"`resetall" { FL; PARSEP->lexFileline()->warnOn(V3ErrorCode::I_DEF_NETTYPE_WIRE, true);
"`resetall" { FL;
PARSEP->lexFileline()->warnOn(V3ErrorCode::I_DEF_NETTYPE_WIRE, true);
v3Global.rootp()->timeInit();
PARSEP->lexFileline()->celldefineOn(false);
PARSEP->unconnectedDrive(VOptionBool::OPT_DEFAULT_FALSE);
return yaT_RESETALL; } // Rest handled by preproc
"`suppress_faults" { FL_FWD; FL_BRK; } // Verilog-XL compatibility
"`timescale"{ws}+[^\n\r]* { FL; PARSEP->lexTimescaleParse(yylval.fl,

View File

@ -37,6 +37,7 @@
// Pick up new lexer
#define yylex PARSEP->tokenToBison
#define BBCOVERIGN(fl, msg) (fl)->v3warn(COVERIGN, msg)
#define BBUNSUP(fl, msg) (fl)->v3warn(E_UNSUPPORTED, msg)
#define GATEUNSUP(fl, tok) \
{ BBUNSUP((fl), "Unsupported: Verilog 1995 gate primitive: " << (tok)); }
@ -491,6 +492,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
%token<fl> yVLT_D_COST "--cost"
%token<fl> yVLT_D_FILE "--file"
%token<fl> yVLT_D_FUNCTION "--function"
%token<fl> yVLT_D_HIER_DPI "--hier-dpi"
%token<fl> yVLT_D_LEVELS "--levels"
%token<fl> yVLT_D_LINES "--lines"
%token<fl> yVLT_D_MATCH "--match"
@ -1477,13 +1479,13 @@ portsStarE<nodep>: // IEEE: .* + list_of_ports + list_of_port_decla
;
list_of_portsE<nodep>: // IEEE: [ list_of_ports + list_of_port_declarations ]
portAndTagE { $$ = $1; }
portAndTagE { $$ = $1; }
| list_of_portsE ',' portAndTagE { $$ = addNextNull($1, $3); }
;
list_of_ports<nodep>: // IEEE: list_of_ports + list_of_port_declarations
portAndTag { $$ = $1; }
| list_of_portsE ',' portAndTagE { $$ = addNextNull($1, $3); }
portAndTag { $$ = $1; }
| list_of_portsE ',' portAndTagE { $$ = addNextNull($1, $3); }
;
portAndTagE<nodep>:
@ -1582,33 +1584,43 @@ port<nodep>: // ==IEEE: port
//
| portDirNetE data_type portSig variable_dimensionListE sigAttrListE
{ $$ = $3; VARDTYPE($2); VARIOANSI(); addNextNull($$, VARDONEP($$, $4, $5)); }
| portDirNetE data_type portSig variable_dimensionListE sigAttrListE '=' constExpr
{ $$ = $3; VARDTYPE($2); VARIOANSI();
if (AstVar* vp = VARDONEP($$, $4, $5)) { addNextNull($$, vp); vp->valuep($7); } }
| portDirNetE yVAR data_type portSig variable_dimensionListE sigAttrListE
{ $$ = $4; VARDTYPE($3); VARIOANSI(); addNextNull($$, VARDONEP($$, $5, $6)); }
| portDirNetE yVAR data_type portSig variable_dimensionListE sigAttrListE '=' constExpr
{ $$ = $4; VARDTYPE($3); VARIOANSI();
if (AstVar* vp = VARDONEP($$, $5, $6)) { addNextNull($$, vp); vp->valuep($8); } }
| portDirNetE yVAR implicit_typeE portSig variable_dimensionListE sigAttrListE
{ $$ = $4; VARDTYPE($3); VARIOANSI(); addNextNull($$, VARDONEP($$, $5, $6)); }
| portDirNetE yVAR implicit_typeE portSig variable_dimensionListE sigAttrListE '=' constExpr
{ $$ = $4; VARDTYPE($3); VARIOANSI();
if (AstVar* vp = VARDONEP($$, $5, $6)) { addNextNull($$, vp); vp->valuep($8); } }
| portDirNetE signing portSig variable_dimensionListE sigAttrListE
{ $$ = $3;
AstNodeDType* const dtp = new AstBasicDType{$3->fileline(), LOGIC_IMPLICIT, $2};
VARDTYPE_NDECL(dtp); VARIOANSI();
addNextNull($$, VARDONEP($$, $4, $5)); }
| portDirNetE signing portSig variable_dimensionListE sigAttrListE '=' constExpr
{ $$ = $3;
AstNodeDType* const dtp = new AstBasicDType{$3->fileline(), LOGIC_IMPLICIT, $2};
VARDTYPE_NDECL(dtp); VARIOANSI();
if (AstVar* vp = VARDONEP($$, $4, $5)) { addNextNull($$, vp); vp->valuep($7); } }
| portDirNetE signingE rangeList portSig variable_dimensionListE sigAttrListE
{ $$ = $4;
AstNodeDType* const dtp = GRAMMARP->addRange(
new AstBasicDType{$3->fileline(), LOGIC_IMPLICIT, $2}, $3, true);
VARDTYPE_NDECL(dtp);
addNextNull($$, VARDONEP($$, $5, $6)); }
| portDirNetE signingE rangeList portSig variable_dimensionListE sigAttrListE '=' constExpr
{ $$ = $4;
AstNodeDType* const dtp = GRAMMARP->addRange(
new AstBasicDType{$3->fileline(), LOGIC_IMPLICIT, $2}, $3, true);
VARDTYPE_NDECL(dtp);
if (AstVar* vp = VARDONEP($$, $5, $6)) { addNextNull($$, vp); vp->valuep($8); } }
| portDirNetE /*implicit*/ portSig variable_dimensionListE sigAttrListE
{ $$ = $2; /*VARDTYPE-same*/ addNextNull($$, VARDONEP($$, $3, $4)); }
//
| portDirNetE data_type portSig variable_dimensionListE sigAttrListE '=' constExpr
{ $$ = $3; VARDTYPE($2); VARIOANSI();
if (AstVar* vp = VARDONEP($$, $4, $5)) { addNextNull($$, vp); vp->valuep($7); } }
| portDirNetE yVAR data_type portSig variable_dimensionListE sigAttrListE '=' constExpr
{ $$ = $4; VARDTYPE($3); VARIOANSI();
if (AstVar* vp = VARDONEP($$, $5, $6)) { addNextNull($$, vp); vp->valuep($8); } }
| portDirNetE yVAR implicit_typeE portSig variable_dimensionListE sigAttrListE '=' constExpr
{ $$ = $4; VARDTYPE($3); VARIOANSI();
if (AstVar* vp = VARDONEP($$, $5, $6)) { addNextNull($$, vp); vp->valuep($8); } }
| portDirNetE /*implicit*/ portSig variable_dimensionListE sigAttrListE '=' constExpr
{ $$ = $2; /*VARDTYPE-same*/
if (AstVar* vp = VARDONEP($$, $3, $4)) { addNextNull($$, vp); vp->valuep($6); } }
@ -3831,17 +3843,21 @@ inc_or_dec_expression<nodeExprp>: // ==IEEE: inc_or_dec_expression
// // Need fexprScope instead of variable_lvalue to prevent conflict
~l~exprScope yP_PLUSPLUS
{ $<fl>$ = $<fl>1; $$ = new AstPostAdd{$2, new AstConst{$2, AstConst::StringToParse{}, "'b1"},
$1, $1->cloneTreePure(true)}; }
// Purity checked in V3LinkInc
$1, $1->cloneTree(true)}; }
| ~l~exprScope yP_MINUSMINUS
{ $<fl>$ = $<fl>1; $$ = new AstPostSub{$2, new AstConst{$2, AstConst::StringToParse{}, "'b1"},
$1, $1->cloneTreePure(true)}; }
// Purity checked in V3LinkInc
$1, $1->cloneTree(true)}; }
// // Need expr instead of variable_lvalue to prevent conflict
| yP_PLUSPLUS expr
{ $<fl>$ = $<fl>1; $$ = new AstPreAdd{$1, new AstConst{$1, AstConst::StringToParse{}, "'b1"},
$2, $2->cloneTreePure(true)}; }
// Purity checked in V3LinkInc
$2, $2->cloneTree(true)}; }
| yP_MINUSMINUS expr
{ $<fl>$ = $<fl>1; $$ = new AstPreSub{$1, new AstConst{$1, AstConst::StringToParse{}, "'b1"},
$2, $2->cloneTreePure(true)}; }
// Purity checked in V3LinkInc
$2, $2->cloneTree(true)}; }
;
finc_or_dec_expression<nodeExprp>: // ==IEEE: inc_or_dec_expression
@ -3947,16 +3963,16 @@ value_range<nodeExprp>: // ==IEEE: value_range/open_value_range
covergroup_value_range<nodeExprp>: // ==IEEE-2012: covergroup_value_range
cgexpr { $$ = $1; }
| '[' cgexpr ':' cgexpr ']'
{ $$ = nullptr; BBUNSUP($1, "Unsupported: covergroup value range"); }
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup value range"); }
// // IEEE-2023: added all four:
// // Skipped as '$' is part of our expr
// // IEEE-2023: '[' '$' ':' cgexpr ']'
// // Skipped as '$' is part of our expr
// // IEEE-2023: '[' cgexpr ':' '$' ']'
| '[' cgexpr yP_PLUSSLASHMINUS cgexpr ']'
{ $$ = nullptr; BBUNSUP($1, "Unsupported: covergroup value range"); }
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup value range"); }
| '[' cgexpr yP_PLUSPCTMINUS cgexpr ']'
{ $$ = nullptr; BBUNSUP($1, "Unsupported: covergroup value range"); }
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup value range"); }
;
caseCondList<nodeExprp>: // IEEE: part of case_item
@ -6173,13 +6189,13 @@ concurrent_assertion_statement<nodep>: // ==IEEE: concurrent_assertion_statemen
{ $$ = new AstCover{$1, $4, $6, VAssertType::CONCURRENT}; }
// // IEEE: cover_sequence_statement
| yCOVER ySEQUENCE '(' sexpr ')' stmt
{ $$ = nullptr; BBUNSUP($2, "Unsupported: cover sequence"); }
{ $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover sequence"); }
// // IEEE: yCOVER ySEQUENCE '(' clocking_event sexpr ')' stmt
// // sexpr already includes "clocking_event sexpr"
| yCOVER ySEQUENCE '(' clocking_event yDISABLE yIFF '(' expr/*expression_or_dist*/ ')' sexpr ')' stmt
{ $$ = nullptr; BBUNSUP($2, "Unsupported: cover sequence"); }
{ $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover sequence"); }
| yCOVER ySEQUENCE '(' yDISABLE yIFF '(' expr/*expression_or_dist*/ ')' sexpr ')' stmt
{ $$ = nullptr; BBUNSUP($2, "Unsupported: cover sequence"); }
{ $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover sequence"); }
// // IEEE: restrict_property_statement
| yRESTRICT yPROPERTY '(' property_spec ')' ';'
{ $$ = new AstRestrict{$1, $4}; }
@ -6656,7 +6672,7 @@ covergroup_declaration<nodep>: // ==IEEE: covergroup_declaration
covergroup_declarationFront<constraintp>: // IEEE: part of covergroup_declaration
yCOVERGROUP idAny
{ $$ = new AstConstraint{$<fl>2, *$2, nullptr};
BBUNSUP($<fl>1, "Unsupported: covergroup");
BBCOVERIGN($<fl>1, "Ignoring unsupported: covergroup");
SYMP->pushNew($<constraintp>$); }
;
@ -6686,27 +6702,27 @@ coverage_option<nodep>: // ==IEEE: coverage_option
// // option/type_option aren't really keywords
id/*yOPTION | yTYPE_OPTION*/ '.' idAny/*member_identifier*/ '=' expr
{ // TODO: check that 'id' is 'option' or 'type_option'
$$ = nullptr; BBUNSUP($<fl>1, "Unsupported: coverage option"); }
$$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: coverage option"); }
;
cover_point<nodep>: // ==IEEE: cover_point
// // [ [ data_type_or_implicit ] cover_point_identifier ':' ] yCOVERPOINT
yCOVERPOINT expr iffE bins_or_empty
{ $$ = nullptr; BBUNSUP($<fl>1, "Unsupported: cover point"); }
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: coverpoint"); }
// // IEEE-2012: class_scope before an ID
| id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
{ $$ = nullptr; BBUNSUP($<fl>3, "Unsupported: cover point"); }
{ $$ = nullptr; BBCOVERIGN($<fl>3, "Ignoring unsupported: coverpoint"); }
// // data_type_or_implicit expansion
| data_type id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
{ $$ = nullptr; BBUNSUP($<fl>4, "Unsupported: cover point"); }
{ $$ = nullptr; BBCOVERIGN($<fl>4, "Ignoring unsupported: coverpoint"); }
| yVAR data_type id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
{ $$ = nullptr; BBUNSUP($<fl>5, "Unsupported: cover point"); }
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: coverpoint"); }
| yVAR implicit_typeE id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
{ $$ = nullptr; BBUNSUP($<fl>5, "Unsupported: cover point"); }
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: coverpoint"); }
| signingE rangeList id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
{ $$ = nullptr; BBUNSUP($<fl>5, "Unsupported: cover point"); }
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: coverpoint"); }
| signing id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
{ $$ = nullptr; BBUNSUP($<fl>4, "Unsupported: cover point"); }
{ $$ = nullptr; BBCOVERIGN($<fl>4, "Ignoring unsupported: coverpoint"); }
// // IEEE-2012:
| bins_or_empty { $$ = $1; }
;
@ -6714,7 +6730,7 @@ cover_point<nodep>: // ==IEEE: cover_point
iffE<nodep>: // IEEE: part of cover_point, others
/* empty */ { $$ = nullptr; }
| yIFF '(' expr ')'
{ $$ = nullptr; BBUNSUP($<fl>1, "Unsupported: cover 'iff'"); }
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: cover 'iff'"); }
;
bins_or_empty<nodep>: // ==IEEE: bins_or_empty
@ -6739,24 +6755,24 @@ bins_or_options<nodep>: // ==IEEE: bins_or_options
coverage_option { $$ = $1; }
// // Can't use wildcardE as results in conflicts
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
{ $$ = nullptr; BBUNSUP($<fl>4, "Unsupported: cover bin specification"); }
{ $$ = nullptr; BBCOVERIGN($<fl>4, "Ignoring unsupported: cover bin specification"); }
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__CUR '{' cgexpr '}' iffE
{ $$ = nullptr; BBUNSUP($<fl>8, "Unsupported: cover bin 'with' specification"); }
{ $$ = nullptr; BBCOVERIGN($<fl>8, "Ignoring unsupported: cover bin 'with' specification"); }
| yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
{ $$ = nullptr; BBUNSUP($<fl>5, "Unsupported: cover bin 'wildcard' specification"); }
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: cover bin 'wildcard' specification"); }
| yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__CUR '{' cgexpr '}' iffE
{ $$ = nullptr; BBUNSUP($<fl>9, "Unsupported: cover bin 'wildcard' 'with' specification"); }
{ $$ = nullptr; BBCOVERIGN($<fl>9, "Ignoring unsupported: cover bin 'wildcard' 'with' specification"); }
//
// // cgexpr part of trans_list
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
{ $$ = nullptr; BBUNSUP($<fl>4, "Unsupported: cover bin trans list"); }
{ $$ = nullptr; BBCOVERIGN($<fl>4, "Ignoring unsupported: cover bin trans list"); }
| yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
{ $$ = nullptr; BBUNSUP($<fl>1, "Unsupported: cover bin 'wildcard' trans list"); }
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: cover bin 'wildcard' trans list"); }
//
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE
{ $$ = nullptr; BBUNSUP($<fl>5, "Unsupported: cover bin 'default'"); }
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: cover bin 'default'"); }
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE
{ $$ = nullptr; BBUNSUP($<fl>6, "Unsupported: cover bin 'default' 'sequence'"); }
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Ignoring unsupported: cover bin 'default' 'sequence'"); }
;
bins_orBraE<nodep>: // IEEE: part of bins_or_options:
@ -6780,23 +6796,23 @@ trans_set<nodep>: // ==IEEE: trans_set
trans_range_list { $$ = $1; }
// // Note the { => } in the grammar, this is really a list
| trans_set yP_EQGT trans_range_list
{ $$ = $1; BBUNSUP($<fl>2, "Unsupported: cover trans set '=>'"); }
{ $$ = $1; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover trans set '=>'"); }
;
trans_range_list<nodep>: // ==IEEE: trans_range_list
trans_item { $$ = $1; }
| trans_item yP_BRASTAR cgexpr ']'
{ $$ = nullptr; BBUNSUP($<fl>2, "Unsupported: cover '[*'"); }
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[*'"); }
| trans_item yP_BRASTAR cgexpr ':' cgexpr ']'
{ $$ = nullptr; BBUNSUP($<fl>2, "Unsupported: cover '[*'"); }
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[*'"); }
| trans_item yP_BRAMINUSGT cgexpr ']'
{ $$ = nullptr; BBUNSUP($<fl>2, "Unsupported: cover '[->'"); }
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[->'"); }
| trans_item yP_BRAMINUSGT cgexpr ':' cgexpr ']'
{ $$ = nullptr; BBUNSUP($<fl>2, "Unsupported: cover '[->'"); }
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[->'"); }
| trans_item yP_BRAEQ cgexpr ']'
{ $$ = nullptr; BBUNSUP($<fl>2, "Unsupported: cover '[='"); }
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[='"); }
| trans_item yP_BRAEQ cgexpr ':' cgexpr ']'
{ $$ = nullptr; BBUNSUP($<fl>2, "Unsupported: cover '[='"); }
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[='"); }
;
trans_item<nodep>: // ==IEEE: range_list
@ -6811,9 +6827,9 @@ covergroup_range_list<nodep>: // ==IEEE: covergroup_range_list
cover_cross<nodep>: // ==IEEE: cover_cross
id/*cover_point_identifier*/ ':' yCROSS list_of_cross_items iffE cross_body
{ $$ = nullptr; BBUNSUP($<fl>3, "Unsupported: cross"); }
{ $$ = nullptr; BBCOVERIGN($<fl>3, "Ignoring unsupported: cover cross"); }
| yCROSS list_of_cross_items iffE cross_body
{ $$ = nullptr; BBUNSUP($<fl>1, "Unsupported: cross"); }
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: cover cross"); }
;
list_of_cross_items<nodep>: // ==IEEE: list_of_cross_items
@ -6855,25 +6871,25 @@ cross_body_item<nodep>: // ==IEEE: cross_body_item
coverage_option { $$ = $1; }
// // IEEE: bins_selection
| function_declaration
{ $$ = $1; BBUNSUP($1->fileline(), "Unsupported: coverage cross 'function' declaration"); }
{ $$ = $1; BBCOVERIGN($1->fileline(), "Ignoring unsupported: coverage cross 'function' declaration"); }
| bins_keyword idAny/*new-bin_identifier*/ '=' select_expression iffE
{ $$ = nullptr; BBUNSUP($1, "Unsupported: coverage cross bin"); }
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage cross bin"); }
;
select_expression<nodep>: // ==IEEE: select_expression
// // IEEE: select_condition expanded here
yBINSOF '(' bins_expression ')'
{ $$ = nullptr; BBUNSUP($1, "Unsupported: coverage select expression 'binsof'"); }
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression 'binsof'"); }
| '!' yBINSOF '(' bins_expression ')'
{ $$ = nullptr; BBUNSUP($1, "Unsupported: coverage select expression 'binsof'"); }
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression 'binsof'"); }
| yBINSOF '(' bins_expression ')' yINTERSECT '{' covergroup_range_list '}'
{ $$ = nullptr; BBUNSUP($5, "Unsupported: coverage select expression 'intersect'"); }
{ $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverage select expression 'intersect'"); }
| '!' yBINSOF '(' bins_expression ')' yINTERSECT '{' covergroup_range_list '}' { }
{ $$ = nullptr; BBUNSUP($5, "Unsupported: coverage select expression 'intersect'"); }
{ $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverage select expression 'intersect'"); }
| yWITH__PAREN '(' cgexpr ')'
{ $$ = nullptr; BBUNSUP($1, "Unsupported: coverage select expression with"); }
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression with"); }
| '!' yWITH__PAREN '(' cgexpr ')'
{ $$ = nullptr; BBUNSUP($1, "Unsupported: coverage select expression with"); }
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression with"); }
// // IEEE-2012: Need clarification as to precedence
//UNSUP yWITH__PAREN '(' cgexpr ')' yMATCHES cgexpr { }
// // IEEE-2012: Need clarification as to precedence
@ -6881,13 +6897,13 @@ select_expression<nodep>: // ==IEEE: select_expression
//
| '(' select_expression ')' { $$ = $2; }
| select_expression yP_ANDAND select_expression
{ $$ = nullptr; BBUNSUP($2, "Unsupported: coverage select expression '&&'"); }
{ $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: coverage select expression '&&'"); }
| select_expression yP_OROR select_expression
{ $$ = nullptr; BBUNSUP($2, "Unsupported: coverage select expression '||'"); }
{ $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: coverage select expression '||'"); }
// // IEEE-2012: cross_identifier
// // Part of covergroup_expression - generic identifier
// // IEEE-2012: Need clarification as to precedence
//UNSUP cgexpr { $$ = nullptr; BBUNSUP($1, "Unsupported: coverage select expression"); }
//UNSUP cgexpr { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression"); }
//
// // Need precedence fix
//UNSUP cgexpr yMATCHES cgexpr {..}
@ -6904,11 +6920,11 @@ bins_expression<nodep>: // ==IEEE: bins_expression
coverage_eventE<nodep>: // IEEE: [ coverage_event ]
/* empty */ { $$ = nullptr; }
| clocking_event
{ $$ = nullptr; BBUNSUP($<fl>1, "Unsupported: coverage clocking event"); }
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: coverage clocking event"); }
| yWITH__ETC yFUNCTION idAny/*"sample"*/ '(' tf_port_listE ')'
{ $$ = nullptr; BBUNSUP($<fl>1, "Unsupported: coverage 'with' 'function'"); }
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: coverage 'with' 'function'"); }
| yP_ATAT '(' block_event_expression ')'
{ $$ = nullptr; BBUNSUP($<fl>1, "Unsupported: coverage '@@' events"); }
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: coverage '@@' events"); }
;
block_event_expression<nodep>: // ==IEEE: block_event_expression
@ -7348,11 +7364,10 @@ class_item<nodep>: // ==IEEE: class_item
| class_method { $$ = $1; }
| class_constraint { $$ = $1; }
//
| class_declaration
{ $$ = nullptr; BBUNSUP($1, "Unsupported: class within class"); }
| class_declaration { $$ = $1; }
| timeunits_declaration { $$ = $1; }
| covergroup_declaration
{ $$ = nullptr; BBUNSUP($1, "Unsupported: covergroup within class"); }
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup within class"); }
// // local_parameter_declaration under parameter_declaration
| parameter_declaration ';' { $$ = $1; }
| ';' { $$ = nullptr; }
@ -7650,6 +7665,8 @@ vltItem:
{ V3Config::addCaseParallel(*$2, 0); }
| yVLT_PARALLEL_CASE vltDFile yVLT_D_LINES yaINTNUM
{ V3Config::addCaseParallel(*$2, $4->toUInt()); }
| yVLT_PROFILE_DATA vltDHierDpi vltDCost
{ V3Config::addProfileData($<fl>1, *$2, $3->toUQuad()); }
| yVLT_PROFILE_DATA vltDModel vltDMtask vltDCost
{ V3Config::addProfileData($<fl>1, *$2, *$3, $4->toUQuad()); }
;
@ -7694,6 +7711,10 @@ vltDFile<strp>: // --file <arg>
yVLT_D_FILE str { $$ = $2; }
;
vltDHierDpi<strp>: // --hier-dpi <arg>
yVLT_D_HIER_DPI str { $$ = $2; }
;
vltDLevels<nump>: // --levels <arg>
yVLT_D_LEVELS yaINTNUM { $$ = $2; }
;

View File

@ -1417,8 +1417,7 @@ class VlTest:
pli_opt = "-pli " + self.obj_dir + "/libvpi.so"
cmd = [
"echo q | " + run_env + VtOs.getenv_def('VERILATOR_MODELSIM', "vsim"),
' '.join(param['ms_run_flags']), ' '.join(param['all_run_flags']), pli_opt,
(" top")
' '.join(param['ms_run_flags']), ' '.join(param['all_run_flags']), pli_opt, (" t")
]
self.run(cmd=cmd,
check_finished=param['check_finished'],

View File

@ -17,11 +17,12 @@
class TestSimulator {
private:
struct SimTypes {
int verilator;
int icarus;
int mti;
int ncsim;
int vcs;
bool verilator = false;
bool icarus = false;
bool mti = false;
bool ncsim = false;
bool vcs = false;
bool questa = false;
};
s_vpi_vlog_info m_info;
SimTypes m_simulators;
@ -37,6 +38,8 @@ public:
== strncmp(m_info.product, "Chronologic Simulation VCS",
std::strlen("Chronologic Simulation VCS"))) {
m_simulators.vcs = true;
} else if (0 == strcmp(m_info.product, "ModelSim for Questa-64")) {
m_simulators.questa = true;
} else {
printf("%%Warning: %s:%d: Unknown simulator in TestSimulator.h: %s\n", __FILE__,
__LINE__, m_info.product);
@ -59,12 +62,13 @@ public:
static bool is_mti() { return simulators().mti; }
static bool is_ncsim() { return simulators().ncsim; }
static bool is_vcs() { return simulators().vcs; }
static bool is_questa() { return simulators().questa; }
// Simulator properties
static bool is_event_driven() { return !simulators().verilator; }
static bool has_get_scalar() { return !simulators().icarus; }
// return test level scope
static const char* top() {
if (simulators().verilator || simulators().icarus) {
if (simulators().verilator || simulators().icarus || simulators().questa) {
return "t";
} else {
return "top.t";

View File

@ -12,6 +12,8 @@
#include "sv_vpi_user.h"
#include "vpi_user.h"
#include <iostream>
// Avoid C++11 in this file as not all simulators allow it
//======================================================================

View File

@ -1,7 +1,7 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Copyright 2024 by Andrew Nolte. This program is free software; you can
// Copyright 2024-2025 by Andrew Nolte. 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.

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. This program is free software; you
# Copyright 2025 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.

View File

@ -13,11 +13,12 @@
// please note it here, otherwise:**
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Wilson Snyder.
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
`define checks(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
module t(/*AUTOARG*/
// Inputs

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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
import vltest_bootstrap
test.scenarios('simulator_st')
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,54 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
`define stop $stop
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
class Cls;
int m_index;
function automatic int get_index();
int rtn;
rtn = m_index;
++m_index;
`ifdef VERILATOR
return $c(rtn); // Avoid optimizations
`else
return rtn;
`endif
endfunction
endclass
module t (/*AUTOARG*/);
Cls cls;
int array[10];
initial begin
cls = new;
// Common UVM construct 'id_cnt[get_id()]++;'
// Properly avoid/handle SIDEEFF warnings
cls.m_index = 5;
array[5] = 50;
array[6] = 60;
array[7] = 70;
array[8] = 80;
array[cls.get_index()]++;
`checkd(array[5], 51);
array[cls.get_index()]++;
`checkd(array[6], 61);
++array[cls.get_index()];
`checkd(array[7], 71);
++array[cls.get_index()];
`checkd(array[8], 81);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -1,5 +0,0 @@
%Error-UNSUPPORTED: t/t_class_class.v:12:4: Unsupported: class within class
12 | class SubCls;
| ^~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: Exiting due to

View File

@ -11,6 +11,8 @@ import vltest_bootstrap
test.scenarios('simulator')
test.compile(fails=test.vlt_all, expect_filename=test.golden_filename)
test.compile()
test.execute()
test.passes()

View File

@ -31,5 +31,8 @@ module t (/*AUTOARG*/);
if (c.imemberb != 20) $stop;
if (c.sc.smembera != 30) $stop;
if (c.sc.smemberb != 40) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -11,6 +11,9 @@
`define check_ne(lhs, rhs) `check_comp(lhs, rhs, ==, 1'b0) `check_comp(lhs, rhs, !=, 1'b1)
class Cls;
class InnerCls;
int j;
endclass
int i;
endclass
@ -22,14 +25,25 @@ module t;
Cls a = new;
Cls b = new;
ExtendCls ext = new;
Cls::InnerCls ia = new;
Cls::InnerCls ib = new;
ExtendCls::InnerCls iext = new;
`check_ne(a, b)
`check_ne(a, ext)
`check_ne(ext, a)
`check_ne(ia, ib)
`check_ne(ia, iext)
`check_ne(iext, ia)
a = b;
ia = ib;
`check_eq(a, b)
`check_eq(ia, ib)
a = ext;
ia = iext;
`check_eq(a, ext)
`check_eq(ext, a)
`check_eq(ia, iext)
`check_eq(iext, ia)
$write("*-* All Finished *-*\n");
$finish;
end

18
test_regress/t/t_class_eq.py Executable file
View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 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
import vltest_bootstrap
test.scenarios('simulator')
test.compile(verilator_flags2=['--binary'])
test.execute()
test.passes()

View File

@ -0,0 +1,32 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
module t();
class A;
int num;
function new(int num);
this.num = num;
endfunction
endclass
class B;
static A obj = new(2);
endclass
class C;
static A obj = new(5);
endclass
initial begin
#1;
$display("Bobj=%p Cobj=%p eq=%p", B::obj, C::obj, (B::obj == C::obj));
if (B::obj == C::obj) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -5,21 +5,55 @@
// SPDX-License-Identifier: CC0-1.0
class Base0;
class BaseInnerOnly;
int inneronly;
function new();
inneronly = 10;
if (inneronly != 10) $stop;
endfunction
endclass
class BaseInnerOver;
int innerover;
function new();
innerover = 10;
if (innerover != 10) $stop;
endfunction
endclass
int baseonly;
int baseover;
BaseInnerOnly inneronly = new;
BaseInnerOver innerover = new;
function void b_set_bo(int v); baseover = v; endfunction
function int b_get_bo(); return baseover; endfunction
function int get_bo(); return baseover; endfunction
function void b_set_io(int v); innerover.innerover = v; endfunction
function int b_get_io(); return innerover.innerover; endfunction
function int get_io(); return innerover.innerover; endfunction
endclass
class Ext extends Base0;
class BaseInnerOver;
int innerover;
function new();
innerover = 20;
if (innerover != 20) $stop;
endfunction
endclass
int baseover;
int extonly;
BaseInnerOnly inneronly = new;
BaseInnerOver innerover = new;
function void e_set_bo(int v); baseover = v; endfunction
function int e_get_bo(); return baseover; endfunction
function int get_bo(); return baseover; endfunction
function void e_set_io(int v); innerover.innerover = v; endfunction
function int e_get_io(); return innerover.innerover; endfunction
function int get_io(); return innerover.innerover; endfunction
endclass
module t (/*AUTOARG*/);
@ -29,15 +63,24 @@ module t (/*AUTOARG*/);
c.baseonly = 10;
c.baseover = 20;
c.extonly = 30;
c.inneronly.inneronly = 40;
c.innerover.innerover = 50;
if (c.baseonly != 10) $stop;
if (c.baseover != 20) $stop;
if (c.extonly != 30) $stop;
if (c.inneronly.inneronly != 40) $stop;
if (c.innerover.innerover != 50) $stop;
c.b_set_bo(100);
c.e_set_bo(200);
c.b_set_io(300);
c.e_set_io(400);
if (c.b_get_bo() != 100) $stop;
if (c.e_get_bo() != 200) $stop;
if (c.get_bo() != 200) $stop;
if (c.b_get_io() != 300) $stop;
if (c.e_get_io() != 400) $stop;
if (c.get_io() != 400) $stop;
$write("*-* All Finished *-*\n");
$finish;

View File

@ -6,22 +6,59 @@
package Pkg;
class Base0;
class BaseInnerOnly;
int inneronly;
function new();
inneronly = 10;
if (inneronly != 10) $stop;
endfunction
endclass
class BaseInnerOver;
int innerover;
function new();
innerover = 10;
if (innerover != 10) $stop;
endfunction
endclass
int baseonly;
int baseover;
BaseInnerOnly inneronly = new;
BaseInnerOver innerover = new;
function void b_set_bo(int v); baseover = v; endfunction
function int b_get_bo(); return baseover; endfunction
function int get_bo(); return baseover; endfunction
function void b_set_io(int v); innerover.innerover = v; endfunction
function int b_get_io(); return innerover.innerover; endfunction
function int get_io(); return innerover.innerover; endfunction
endclass
endpackage
// We need to import Base0, as verilator currently doesn't support
// multiple `::` references, but we would need to do that to reference
// `BaseInnerOnly` class inside `Ext` class.
import Pkg::Base0;
class Ext extends Pkg::Base0;
class BaseInnerOver;
int innerover;
function new();
innerover = 20;
if (innerover != 20) $stop;
endfunction
endclass
int baseover;
int extonly;
BaseInnerOnly inneronly = new;
BaseInnerOver innerover = new;
function void e_set_bo(int v); baseover = v; endfunction
function int e_get_bo(); return baseover; endfunction
function int get_bo(); return baseover; endfunction
function void e_set_io(int v); innerover.innerover = v; endfunction
function int e_get_io(); return innerover.innerover; endfunction
function int get_io(); return innerover.innerover; endfunction
endclass
module t (/*AUTOARG*/);
@ -31,15 +68,24 @@ module t (/*AUTOARG*/);
c.baseonly = 10;
c.baseover = 20;
c.extonly = 30;
c.inneronly.inneronly = 40;
c.innerover.innerover = 50;
if (c.baseonly != 10) $stop;
if (c.baseover != 20) $stop;
if (c.extonly != 30) $stop;
if (c.inneronly.inneronly != 40) $stop;
if (c.innerover.innerover != 50) $stop;
c.b_set_bo(100);
c.e_set_bo(200);
c.b_set_io(300);
c.e_set_io(400);
if (c.b_get_bo() != 100) $stop;
if (c.e_get_bo() != 200) $stop;
if (c.get_bo() != 200) $stop;
if (c.b_get_io() != 300) $stop;
if (c.e_get_io() != 400) $stop;
if (c.get_io() != 400) $stop;
$write("*-* All Finished *-*\n");
$finish;

View File

@ -8,26 +8,53 @@ module t (/*AUTOARG*/
);
class Foo;
class InnerFoo;
int y = 10;
function int get_y;
return y;
endfunction
function int get_30;
return 30;
endfunction
endclass
int x = 1;
InnerFoo foo = new;
function int get_x;
return x;
endfunction
function int get_3;
return 3;
endfunction
function InnerFoo get_foo;
return foo;
endfunction
endclass
class Bar #(type T=Foo) extends T;
endclass
class Baz;
class InnerFoo;
int y = 20;
function int get_y;
return y;
endfunction
function int get_40;
return 40;
endfunction
endclass
int x = 2;
InnerFoo foo = new;
function int get_x;
return x;
endfunction
function int get_4;
return 4;
endfunction
function InnerFoo get_foo;
return foo;
endfunction
endclass
class ExtendBar extends Bar#();
@ -71,6 +98,9 @@ module t (/*AUTOARG*/
function int get_x_of_item(int i);
return q[i].x;
endfunction
function int get_y_of_item(int i);
return q[i].get_foo().get_y();
endfunction
endclass
Bar #() bar_foo_i;
@ -93,8 +123,12 @@ module t (/*AUTOARG*/
if (bar_foo_i.get_x() != 1) $stop;
if (bar_foo_i.get_3() != 3) $stop;
if (bar_foo_i.get_foo().get_y() != 10) $stop;
if (bar_foo_i.get_foo().get_30() != 30) $stop;
if (bar_baz_i.get_x() != 2) $stop;
if (bar_baz_i.get_4() != 4) $stop;
if (bar_baz_i.get_foo().get_y() != 20) $stop;
if (bar_baz_i.get_foo().get_40() != 40) $stop;
if (extend_bar_i.get_x() != 1) $stop;
if (extend_bar_i.get_6() != 6) $stop;
if (extend_bar_i.get_x() != 1) $stop;
@ -106,6 +140,7 @@ module t (/*AUTOARG*/
if (extend_extend_bar_i.get_x() != 1) $stop;
if (extend_extend_bar_i.get_12() != 12) $stop;
if (extend_foo_dict_i.get_x_of_item(1) != 1) $stop;
if (extend_foo_dict_i.get_y_of_item(1) != 10) $stop;
$write("*-* All Finished *-*\n");
$finish;

View File

@ -7,7 +7,20 @@
typedef class Cls;
class Base;
class BaseInner;
int value = 10;
function void test;
if (value != 10) $stop;
if (this.value != 10) $stop;
value = 20;
if (value != 20) $stop;
this.value = 30;
if (value != 30) $stop;
endfunction
endclass
int value = 1;
BaseInner inner = new;
function void test;
if (value != 1) $stop;
if (this.value != 1) $stop;
@ -19,7 +32,25 @@ class Base;
endclass
class Cls extends Base;
class BaseInner extends Base::BaseInner;
int value = 100;
function void test;
if (value != 100) $stop;
if (this.value != 100) $stop;
if (super.value != 10) $stop;
super.test();
if (value != 100) $stop;
if (this.value != 100) $stop;
if (super.value != 30) $stop;
value = 200;
if (value != 200) $stop;
this.value = 300;
if (value != 300) $stop;
endfunction
endclass
int value = 20;
BaseInner inner = new;
function void test;
if (value != 20) $stop;
if (this.value != 20) $stop;
@ -31,7 +62,9 @@ class Cls extends Base;
super.value = 9;
this.value = 29;
if (super.value != 9) $stop;
if (value != 29) $stop;;
if (value != 29) $stop;
inner.test();
endfunction
endclass

View File

@ -7,15 +7,24 @@
typedef class Cls;
class Base;
class Inner;
int value = 10;
function void testBaseInner;
if (value != 10) $stop;
endfunction
endclass
int value = 1;
Inner inner = new;
function void testBase;
if (value != 1) $stop;
if (inner.value != 10) $stop;
endfunction
endclass
class Cls extends Base;
function void testDerived;
if (value != 1) $stop;
if (inner.value != 10) $stop;
endfunction
endclass
@ -25,6 +34,7 @@ module t (/*AUTOARG*/);
c = new;
c.testBase();
c.testDerived();
c.inner.testBaseInner();
$write("*-* All Finished *-*\n");
$finish;
end

Some files were not shown because too many files have changed in this diff Show More