Merge from master for release.
This commit is contained in:
commit
ac3f30ed6c
|
|
@ -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?
|
||||
|
||||
|
|
|
|||
|
|
@ -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
59
Changes
|
|
@ -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
|
||||
==========================
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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]";
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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("}");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
271
src/V3EmitV.cpp
271
src/V3EmitV.cpp
|
|
@ -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} {}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)};
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
155
src/verilog.y
155
src/verilog.y
|
|
@ -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; }
|
||||
;
|
||||
|
|
|
|||
|
|
@ -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'],
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
//======================================================================
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue