diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 850a53bfb..968103dec 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -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? diff --git a/CMakeLists.txt b/CMakeLists.txt index 911751a8a..ee1260857 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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( diff --git a/Changes b/Changes index d2121e117..f1bfb1c31 100644 --- a/Changes +++ b/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 ========================== diff --git a/Makefile.in b/Makefile.in index ea0310369..ec395ef6a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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: diff --git a/bin/verilator b/bin/verilator index 6f8f5ae35..ee917f7a9 100755 --- a/bin/verilator +++ b/bin/verilator @@ -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 Tune convergence settle time --coverage Enable all coverage + --coverage-expr Enable expression coverage + --coverage-expr-max Maximum permutations allowed for an expression --coverage-line Enable line coverage --coverage-max-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 Get environment variable with defaults --get-supported Get if feature is supported + --getenv Get environment variable with defaults --help Show this help --hierarchical Enable hierarchical Verilation --hierarchical-params-file Internal option that specifies parameters file for hier blocks @@ -386,6 +387,11 @@ detailed descriptions of these arguments. --inline-mult Tune module inlining --instr-count-dpi Assumed dynamic instruction count of DPI imports -j 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 Verilog scope name of the top module --language Default language standard to parse -LDFLAGS Linker pre-object arguments for makefile @@ -422,6 +428,8 @@ detailed descriptions of these arguments. --pipe-filter Filter all input through a script --pp-comments Show preprocessor comments with -E --prefix 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 Create a DPI protected library --public Mark signals as public; see docs --public-depth 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+= 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 Assign non-initial Xs to this value --x-initial 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 Directory to search for modules diff --git a/bin/verilator_gantt b/bin/verilator_gantt index 2f5fb5ec1..94f52870d 100755 --- a/bin/verilator_gantt +++ b/bin/verilator_gantt @@ -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 diff --git a/bin/verilator_profcfunc b/bin/verilator_profcfunc index 434fe2823..60c5a428a 100755 --- a/bin/verilator_profcfunc +++ b/bin/verilator_profcfunc @@ -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 diff --git a/configure.ac b/configure.ac index a7e1b8976..26a0ba45c 100644 --- a/configure.ac +++ b/configure.ac @@ -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) diff --git a/docs/CONTRIBUTING.rst b/docs/CONTRIBUTING.rst index c7a4de79d..061394812 100644 --- a/docs/CONTRIBUTING.rst +++ b/docs/CONTRIBUTING.rst @@ -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 `__. @@ -28,8 +28,8 @@ Did you find a bug? `__. -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 `__ 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 `__, or `Verilator diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index fa06407a2..6911b9e86 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -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 diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 7afdb5df6..d337d6795 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -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 + + 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+ - 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 @@ -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 + + 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 + + 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 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 + + 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 + + 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 +.. 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+= 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 - - 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 - - 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 "" -cost + + 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 "" -cost Feeds profile-guided optimization data into the Verilator algorithms in diff --git a/docs/guide/exe_verilator_coverage.rst b/docs/guide/exe_verilator_coverage.rst index c29f9d329..23caac867 100644 --- a/docs/guide/exe_verilator_coverage.rst +++ b/docs/guide/exe_verilator_coverage.rst @@ -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. diff --git a/docs/guide/simulating.rst b/docs/guide/simulating.rst index 1048199b4..49ba5c720 100644 --- a/docs/guide/simulating.rst +++ b/docs/guide/simulating.rst @@ -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 diff --git a/docs/guide/warnings.rst b/docs/guide/warnings.rst index bd8fb5b40..387530491 100644 --- a/docs/guide/warnings.rst +++ b/docs/guide/warnings.rst @@ -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) diff --git a/docs/spelling.txt b/docs/spelling.txt index 9270968b7..ac86ee4df 100644 --- a/docs/spelling.txt +++ b/docs/spelling.txt @@ -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 diff --git a/include/gtkwave/fstapi.c b/include/gtkwave/fstapi.c index ed818bfb8..9845edacd 100644 --- a/include/gtkwave/fstapi.c +++ b/include/gtkwave/fstapi.c @@ -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; diff --git a/include/verilated.cpp b/include/verilated.cpp index 15f3b10a9..02333a810 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -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(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(vlflags), dims, isParam); + VerilatedVar var(namep, datap, vltype, static_cast(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); diff --git a/include/verilated.h b/include/verilated.h index cee813c31..d832eb7ab 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -175,6 +175,9 @@ enum class VerilatedAssertDirectiveType : uint8_t { using VerilatedAssertType_t = std::underlying_type::type; using VerilatedAssertDirectiveType_t = std::underlying_type::type; +// Type trait for custom struct +template +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; } diff --git a/include/verilated_dpi.cpp b/include/verilated_dpi.cpp index 04a926646..37b8a878e 100644 --- a/include/verilated_dpi.cpp +++ b/include/verilated_dpi.cpp @@ -289,7 +289,7 @@ static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, } case VLVT_WDATA: { WDataOutP wdatap = (reinterpret_cast(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(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(datap)) = VL_SET_QII(s[1], s[0]); break; case VLVT_WDATA: { WDataOutP wdatap = (reinterpret_cast(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(datap)) = VL_SET_QII(s[1].aval, s[0].aval); break; case VLVT_WDATA: { WDataOutP wdatap = (reinterpret_cast(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 diff --git a/include/verilated_random.cpp b/include/verilated_random.cpp index 3dc3b24b0..30e45d64d 100644 --- a/include/verilated_random.cpp +++ b/include/verilated_random.cpp @@ -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()); } diff --git a/include/verilated_random.h b/include/verilated_random.h index 90d92aa9e..05389240f 100644 --- a/include/verilated_random.h +++ b/include/verilated_random.h @@ -27,8 +27,10 @@ #include "verilated.h" +#include #include #include +#include //============================================================================= // 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 m_indices; // Multi-dimensional indices of the array element + const std::vector m_indices; // Multi-dimensional indices of the array element const std::vector m_idxWidths; // Multi-dimensional indices' bit widths - ArrayInfo(const std::string& name, void* datap, int index, const std::vector& indices, + ArrayInfo(const std::string& name, void* datap, int index, const std::vector& indices, const std::vector& idxWidths) : m_name(name) , m_datap(datap) @@ -52,21 +54,22 @@ public: using ArrayInfoMap = std::map>; 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 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& indices, + void emitHexs(std::ostream& s, const std::vector& 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& indices, const std::vector& 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& indices = it->second->m_indices; + const std::vector& indices = it->second->m_indices; const std::vector& 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& indices = it->second->m_indices; + const std::vector& indices = it->second->m_indices; const std::vector& 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 m_constraints; // Solver-dependent constraints @@ -200,48 +217,93 @@ public: bool next(VlRNG& rngr); template - typename std::enable_if::value>::type - process_key(const T_Key& key, std::string& indexed_name, size_t& integral_index, + typename std::enable_if::value && (sizeof(T_Key) <= 4)>::type + process_key(const T_Key& key, std::string& indexed_name, std::vector& integral_index, const std::string& base_name, size_t& idx_width) { - integral_index = static_cast(key); - indexed_name = base_name + "[" + std::to_string(integral_index) + "]"; + integral_index.push_back(static_cast(key)); + indexed_name + = base_name + "[" + std::to_string(integral_index[integral_index.size() - 1]) + "]"; idx_width = sizeof(T_Key) * 8; } template - typename std::enable_if::value>::type - process_key(const T_Key& key, std::string& indexed_name, size_t& integral_index, + typename std::enable_if::value && (sizeof(T_Key) > 4)>::type + process_key(const T_Key& key, std::string& indexed_name, std::vector& 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(1) << segment_bits) - 1; + integral_index.push_back(static_cast(key >> segment_bits)); + integral_index.push_back(static_cast(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 std::enable_if::value>::type + process_key(const T_Key& key, std::string& indexed_name, std::vector& 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 std::enable_if::value>::type + process_key(const T_Key& key, std::string& indexed_name, std::vector& 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(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 std::enable_if::value - && !std::is_same::value>::type - process_key(const T_Key& key, std::string& indexed_name, size_t& integral_index, + && !std::is_same::value + && !VlIsVlWide::value>::type + process_key(const T_Key& key, std::string& indexed_name, std::vector& 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(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 - void write_var(T& var, int width, const char* name, int dimension, - std::uint32_t randmodeIdx = std::numeric_limits::max()) { + typename std::enable_if::value, void>::type + write_var(T& var, int width, const char* name, int dimension, + std::uint32_t randmodeIdx = std::numeric_limits::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 + void modifyMembers(T& obj, std::index_sequence, std::string baseName) { + // Use the indices to access each member via std::get + (void)std::initializer_list{ + (write_var(std::get(obj.getMembers(obj)), obj.memberWidth()[I], + (baseName + "." + obj.memberNames()[I]).c_str(), obj.memberDimension()[I]), + 0)...}; + } + + template + typename std::enable_if::value, void>::type + write_var(T& var, int width, const char* name, int dimension, + std::uint32_t randmodeIdx = std::numeric_limits::max()) { + modifyMembers(var, var.memberIndices(), name); + } int idx; std::string generateKey(const std::string& name, int idx) { @@ -295,15 +372,16 @@ public: } template - void record_arr_table(T& var, const std::string name, int dimension, - std::vector indices, std::vector idxWidths) { + typename std::enable_if::value, void>::type + record_arr_table(T& var, const std::string name, int dimension, std::vector indices, + std::vector idxWidths) { const std::string key = generateKey(name, idx); m_arr_vars[key] = std::make_shared(name, &var, idx, indices, idxWidths); ++idx; } template void record_arr_table(VlQueue& var, const std::string name, int dimension, - std::vector indices, std::vector idxWidths) { + std::vector indices, std::vector 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 void record_arr_table(VlUnpacked& var, const std::string name, int dimension, - std::vector indices, std::vector idxWidths) { + std::vector indices, std::vector 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 void record_arr_table(VlAssocArray& var, const std::string name, int dimension, - std::vector indices, std::vector idxWidths) { + std::vector indices, std::vector 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 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()); } } } diff --git a/include/verilated_sym_props.h b/include/verilated_sym_props.h index 5298787b0..53600e7a1 100644 --- a/include/verilated_sym_props.h +++ b/include/verilated_sym_props.h @@ -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 m_unpacked; // Unpacked array ranges - void initUnpacked(const int* ulims) { - for (int i = 0; i < m_udims; ++i) { + std::vector 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(static_cast(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& packedRanges() const VL_MT_SAFE { return m_packed; } + const std::vector& 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; } }; diff --git a/include/verilated_types.h b/include/verilated_types.h index 84caafa27..cd5ea552e 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -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& 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 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& 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 { diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index 6d3a3eaf7..7654146a1 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -85,7 +85,7 @@ public: // To simplify our free list, we use a size large enough for all derived types // We reserve word zero for the next pointer, as that's safer in case a // dangling reference to the original remains around. - static constexpr size_t CHUNK_SIZE = 96; + static constexpr size_t CHUNK_SIZE = 128; if (VL_UNCOVERABLE(size > CHUNK_SIZE)) VL_FATAL_MT(__FILE__, __LINE__, "", "increase CHUNK_SIZE"); if (VL_LIKELY(t_freeHeadp)) { @@ -171,10 +171,8 @@ protected: const VerilatedVar* m_varp = nullptr; const VerilatedScope* m_scopep = nullptr; std::string m_fullname; - const VerilatedRange& get_range() const { - // Determine number of dimensions and return outermost - return (m_varp->dims() > 1) ? m_varp->unpacked() : m_varp->packed(); - } + int32_t m_indexedDim = -1; + const VerilatedRange* get_range() const { return m_varp->range(m_indexedDim + 1); } public: VerilatedVpioVarBase(const VerilatedVar* varp, const VerilatedScope* scopep) @@ -186,6 +184,7 @@ public: m_varp = varp->m_varp; m_scopep = varp->m_scopep; m_fullname = varp->m_fullname; + m_indexedDim = varp->m_indexedDim; } } static VerilatedVpioVarBase* castp(vpiHandle h) { @@ -193,10 +192,64 @@ public: } const VerilatedVar* varp() const { return m_varp; } const VerilatedScope* scopep() const { return m_scopep; } - uint32_t size() const override { return get_range().elements(); } - const VerilatedRange* rangep() const override { return &get_range(); } + // Returns the number of the currently indexed dimension (starting at -1 for none). + int32_t indexedDim() const { return m_indexedDim; } + // Returns whether the currently indexed dimension is unpacked. + bool isIndexedDimUnpacked() const { return indexedDim() + 1 < varp()->udims(); } + // Returns a maximum accessible dimension number, counting only unpacked dimensions + // (if onlyUnpacked == true), or both unpacked + packed. + int32_t maxDim(bool onlyUnpacked) const { + return onlyUnpacked ? varp()->udims() - 1 : varp()->dims() - 1; + } + // Returns a number of elements in the array, stopping at the unpacked-packed boundary. + uint32_t size() const override { + const int maxDimNum = maxDim(isIndexedDimUnpacked()); + int size = 1; + for (int dim = indexedDim() + 1; dim <= maxDimNum; dim++) + size *= varp()->range(dim)->elements(); + return size; + } + // If the array is unpacked, returns the bitsize of a single underlying packed element. + // If the array is packed, returns the bitsize of the whole array. + uint32_t bitSize() const { + if (isIndexedDimUnpacked()) + return varp()->entBits(); + else + return size(); + } + const VerilatedRange* rangep() const override { return get_range(); } const char* name() const override { return m_varp->name(); } const char* fullname() const override { return m_fullname.c_str(); } + virtual void* varDatap() const { return m_varp->datap(); } + CData* varCDatap() const { + VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT8);); + return reinterpret_cast(varDatap()); + } + SData* varSDatap() const { + VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT16);); + return reinterpret_cast(varDatap()); + } + IData* varIDatap() const { + VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT32);); + return reinterpret_cast(varDatap()); + } + QData* varQDatap() const { + VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT64);); + return reinterpret_cast(varDatap()); + } + EData* varEDatap() const { + VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_WDATA);); + return reinterpret_cast(varDatap()); + } + double* varRealDatap() const { + VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_REAL);); + return reinterpret_cast(varDatap()); + } + std::string* varStringDatap() const { + VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_STRING);); + return reinterpret_cast(varDatap()); + } + virtual uint32_t bitOffset() const { return 0; } }; class VerilatedVpioParam final : public VerilatedVpioVarBase { @@ -221,7 +274,6 @@ public: default: return vpiUndefined; } } - void* varDatap() const { return m_varp->datap(); } }; class VerilatedVpioRange final : public VerilatedVpio { @@ -240,25 +292,27 @@ public: }; class VerilatedVpioRangeIter final : public VerilatedVpio { - // Only supports 1 dimension - const VerilatedRange* const m_rangep; - bool m_done = false; + const std::vector m_ranges; + std::vector::const_iterator m_iter; public: - explicit VerilatedVpioRangeIter(const VerilatedRange* rangep) - : m_rangep{rangep} {} + explicit VerilatedVpioRangeIter(const std::vector& ranges) + : m_ranges{ranges} { + m_iter = m_ranges.begin(); + } ~VerilatedVpioRangeIter() override = default; static VerilatedVpioRangeIter* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); } uint32_t type() const override { return vpiIterator; } vpiHandle dovpi_scan() override { - if (VL_UNLIKELY(m_done)) { + if (VL_UNLIKELY(m_iter == m_ranges.end())) { delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle return nullptr; } - m_done = true; - return ((new VerilatedVpioRange{m_rangep})->castVpiHandle()); + VerilatedRange* const rangep = new VerilatedRange(*m_iter); + ++m_iter; + return ((new VerilatedVpioRange{rangep})->castVpiHandle()); } }; @@ -297,14 +351,16 @@ class VerilatedVpioVar VL_NOT_FINAL : public VerilatedVpioVarBase { uint32_t u32; } m_mask; // memoized variable mask uint32_t m_entSize = 0; // memoized variable size + uint32_t m_bitOffset = 0; + protected: void* m_varDatap = nullptr; // varp()->datap() adjusted for array entries - int32_t m_index = 0; + std::vector m_index; public: VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep) : VerilatedVpioVarBase{varp, scopep} { - m_mask.u32 = VL_MASK_I(varp->packed().elements()); + m_mask.u32 = VL_MASK_I(varp->entBits()); m_entSize = varp->entSize(); m_varDatap = varp->datap(); } @@ -326,10 +382,30 @@ public: static VerilatedVpioVar* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); } + uint32_t bitOffset() const override { return m_bitOffset; } uint32_t mask() const { return m_mask.u32; } uint8_t mask_byte(int idx) const { return m_mask.u8[idx & 3]; } uint32_t entSize() const { return m_entSize; } - uint32_t index() const { return m_index; } + const std::vector& index() const { return m_index; } + VerilatedVpioVar* withIndex(int32_t index) const { + if (VL_UNLIKELY(indexedDim() + 1 >= varp()->dims())) return nullptr; + + auto ret = new VerilatedVpioVar{this}; + ret->m_index.push_back(index); + ret->m_indexedDim++; + + int chunkSize = 1; + for (int dim = maxDim(isIndexedDimUnpacked()); dim > indexedDim() + 1; dim--) + chunkSize *= varp()->range(dim)->elements(); + + if (isIndexedDimUnpacked()) + ret->m_varDatap = (static_cast(ret->m_varDatap)) + + entSize() * chunkSize * (index - get_range()->low()); + else + ret->m_bitOffset += chunkSize * (index - get_range()->low()); + + return ret; + } uint32_t type() const override { uint32_t type = vpiReg; switch (varp()->vltype()) { @@ -337,10 +413,19 @@ public: case VLVT_STRING: type = vpiStringVar; break; default: break; } - return (varp()->dims() > 1) ? vpiMemory : type; // but might be wire, logic + if (isIndexedDimUnpacked()) + return vpiRegArray; + else + return type; + } + const char* fullname() const override { + static thread_local std::string t_out; + t_out = std::string{scopep()->name()} + "." + name(); + for (auto idx : index()) { t_out += "[" + std::to_string(idx) + "]"; } + return t_out.c_str(); } void* prevDatap() const { return m_prevDatap; } - void* varDatap() const { return m_varDatap; } + void* varDatap() const override { return m_varDatap; } void createPrevDatap() { if (VL_UNLIKELY(!m_prevDatap)) { m_prevDatap = new uint8_t[entSize()]; @@ -349,31 +434,6 @@ public: } }; -class VerilatedVpioMemoryWord final : public VerilatedVpioVar { -public: - VerilatedVpioMemoryWord(const VerilatedVar* varp, const VerilatedScope* scopep, int32_t index, - int offset) - : VerilatedVpioVar{varp, scopep} { - m_index = index; - m_varDatap = (static_cast(varp->datap())) + entSize() * offset; - } - ~VerilatedVpioMemoryWord() override = default; - static VerilatedVpioMemoryWord* castp(vpiHandle h) { - return dynamic_cast(reinterpret_cast(h)); - } - uint32_t type() const override { return vpiMemoryWord; } - uint32_t size() const override { return varp()->packed().elements(); } - const VerilatedRange* rangep() const override { return &(varp()->packed()); } - const char* fullname() const override { - static thread_local std::string t_out; - constexpr size_t LEN_MAX_INDEX = 25; - char num[LEN_MAX_INDEX]; - VL_SNPRINTF(num, LEN_MAX_INDEX, "%d", m_index); - t_out = std::string{scopep()->name()} + "." + name() + "[" + num + "]"; - return t_out.c_str(); - } -}; - class VerilatedVpioVarIter final : public VerilatedVpio { const VerilatedScope* const m_scopep; VerilatedVarNameMap::const_iterator m_it; @@ -433,35 +493,52 @@ public: } }; -class VerilatedVpioMemoryWordIter final : public VerilatedVpio { - const vpiHandle m_handle; - const VerilatedVar* const m_varp; - int32_t m_iteration; - const int32_t m_direction; - bool m_done = false; +class VerilatedVpioRegIter final : public VerilatedVpio { + VerilatedVpioVar* m_var; + std::vector m_ranges; + std::vector m_nextIndex; + const int32_t m_maxDim; public: - VerilatedVpioMemoryWordIter(const vpiHandle handle, const VerilatedVar* varp) - : m_handle{handle} - , m_varp{varp} - , m_iteration{varp->unpacked().right()} - , m_direction{VL_LIKELY(varp->unpacked().left() > varp->unpacked().right()) ? 1 : -1} {} - ~VerilatedVpioMemoryWordIter() override = default; - static VerilatedVpioMemoryWordIter* castp(vpiHandle h) { - return dynamic_cast(reinterpret_cast(h)); + explicit VerilatedVpioRegIter(const VerilatedVpioVar* vop) + : m_var{new VerilatedVpioVar(vop)} + , m_maxDim{vop->varp()->udims() - 1} { + for (auto it = vop->indexedDim() + 1; it <= m_maxDim; it++) + m_ranges.push_back(*vop->varp()->range(it)); + for (auto it : m_ranges) m_nextIndex.push_back(it.right()); + } + ~VerilatedVpioRegIter() override = default; + static VerilatedVpioRegIter* castp(vpiHandle h) { + return dynamic_cast(reinterpret_cast(h)); } uint32_t type() const override { return vpiIterator; } - void iterationInc() { - if (!(m_done = (m_iteration == m_varp->unpacked().left()))) m_iteration += m_direction; - } vpiHandle dovpi_scan() override { - if (VL_UNLIKELY(m_done)) { - delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle + if (VL_UNLIKELY(m_var->indexedDim() >= m_maxDim)) { + // Trying to iterate over a non-array object + delete this; return nullptr; } - const vpiHandle result = vpi_handle_by_index(m_handle, m_iteration); - iterationInc(); - return result; + if (m_nextIndex.front() > m_ranges.front().high() + || m_nextIndex.front() < m_ranges.front().low()) { + // Finished iterating + delete this; + return nullptr; + } + + VerilatedVpioVar* ret = m_var; + for (auto it : m_nextIndex) ret = ret->withIndex(it); + + // Increase the index, pretending the dimensions are flattened + for (int32_t it = m_ranges.size() - 1; it >= 0; it--) { + m_nextIndex.at(it) += m_ranges.at(it).increment(); + if (m_nextIndex.at(it) <= m_ranges.at(it).high() + && m_nextIndex.at(it) >= m_ranges.at(it).low()) + break; + else if (it > 0) + m_nextIndex.at(it) = m_ranges.at(it).right(); + } + + return ret->castVpiHandle(); } }; @@ -686,7 +763,7 @@ public: break; } case VLVT_WDATA: { - words = VL_WORDS_I(vop->varp()->packed().elements()); + words = VL_WORDS_I(vop->varp()->entBits()); break; } default: break; @@ -2047,26 +2124,16 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) { VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle_by_index %p %d\n", object, indx);); VerilatedVpiImp::assertOneCheck(); VL_VPI_ERROR_RESET_(); - // Memory words are not indexable - const VerilatedVpioMemoryWord* const vop = VerilatedVpioMemoryWord::castp(object); - if (VL_UNLIKELY(vop)) return nullptr; const VerilatedVpioVar* const varop = VerilatedVpioVar::castp(object); if (VL_LIKELY(varop)) { - if (varop->varp()->dims() < 2) return nullptr; - if (VL_LIKELY(varop->varp()->unpacked().left() >= varop->varp()->unpacked().right())) { - if (VL_UNLIKELY(indx > varop->varp()->unpacked().left() - || indx < varop->varp()->unpacked().right())) - return nullptr; - return (new VerilatedVpioMemoryWord{varop->varp(), varop->scopep(), indx, - indx - varop->varp()->unpacked().right()}) - ->castVpiHandle(); - } - if (VL_UNLIKELY(indx < varop->varp()->unpacked().left() - || indx > varop->varp()->unpacked().right())) + // Case: no dimensions left to index + if (VL_UNLIKELY(varop->indexedDim() + 1 > varop->varp()->dims() - 1)) return nullptr; + + // Case: index out of range + if (VL_UNLIKELY(indx < varop->rangep()->low() || indx > varop->rangep()->high())) return nullptr; - return (new VerilatedVpioMemoryWord{varop->varp(), varop->scopep(), indx, - indx - varop->varp()->unpacked().left()}) - ->castVpiHandle(); + + return varop->withIndex(indx)->castVpiHandle(); } VL_VPI_INTERNAL_(__FILE__, __LINE__, "%s : can't resolve handle", __func__); return nullptr; @@ -2108,7 +2175,7 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) { case vpiIndex: { const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); if (VL_UNLIKELY(!vop)) return nullptr; - const int32_t val = vop->index(); + const int32_t val = vop->index().back(); return (new VerilatedVpioConst{val})->castVpiHandle(); } case vpiScope: { @@ -2117,7 +2184,7 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) { return (new VerilatedVpioScope{vop->scopep()})->castVpiHandle(); } case vpiParent: { - const VerilatedVpioMemoryWord* const vop = VerilatedVpioMemoryWord::castp(object); + const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); if (VL_UNLIKELY(!vop)) return nullptr; return (new VerilatedVpioVar{vop->varp(), vop->scopep()})->castVpiHandle(); } @@ -2139,35 +2206,25 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) { VerilatedVpiImp::assertOneCheck(); VL_VPI_ERROR_RESET_(); switch (type) { - case vpiMemoryWord: { - const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); - if (VL_UNLIKELY(!vop)) return nullptr; - if (vop->varp()->dims() < 2) return nullptr; - if (vop->varp()->dims() > 2) { - VL_VPI_WARNING_(__FILE__, __LINE__, - "%s: %s, object %s has unsupported number of indices (%d)", __func__, - VerilatedVpiError::strFromVpiMethod(type), vop->fullname(), - vop->varp()->dims()); - } - return (new VerilatedVpioMemoryWordIter{object, vop->varp()})->castVpiHandle(); - } case vpiRange: { const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); if (VL_UNLIKELY(!vop)) return nullptr; - if (vop->varp()->dims() < 2) return nullptr; - // Unsupported is multidim list - if (vop->varp()->dims() > 2) { - VL_VPI_WARNING_(__FILE__, __LINE__, - "%s: %s, object %s has unsupported number of indices (%d)", __func__, - VerilatedVpiError::strFromVpiMethod(type), vop->fullname(), - vop->varp()->dims()); - } - return ((new VerilatedVpioRangeIter{vop->rangep()})->castVpiHandle()); + + std::vector ranges; + const int maxDim = vop->maxDim(vop->isIndexedDimUnpacked()); + for (int dim = vop->indexedDim() + 1; dim <= maxDim; dim++) + ranges.emplace_back(*vop->varp()->range(dim)); + + // allow one more range layer (regbit) + if (ranges.empty()) ranges.emplace_back(VerilatedRange(0, 0)); + return ((new VerilatedVpioRangeIter{ranges})->castVpiHandle()); } case vpiReg: { - const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object); - if (VL_UNLIKELY(!vop)) return nullptr; - return ((new VerilatedVpioVarIter{vop})->castVpiHandle()); + const VerilatedVpioScope* const vscopep = VerilatedVpioScope::castp(object); + if (vscopep) return ((new VerilatedVpioVarIter{vscopep, false})->castVpiHandle()); + const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); + if (vop) return ((new VerilatedVpioRegIter{vop})->castVpiHandle()); + return nullptr; } case vpiParameter: { const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object); @@ -2249,7 +2306,7 @@ PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) { case vpiVector: { const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object); if (VL_UNLIKELY(!vop)) return vpiUndefined; - return (property == vpiVector) ^ (vop->varp()->dims() == 0); + return (property == vpiVector) ^ (vop->varp()->packedRanges().empty() || !vop->rangep()); } case vpiSize: { const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object); @@ -2347,7 +2404,9 @@ bool vl_check_format(const VerilatedVar* varp, const p_vpi_value valuep, const c switch (varp->vltype()) { case VLVT_UINT8: case VLVT_UINT16: - case VLVT_UINT32: return status; + case VLVT_UINT32: + case VLVT_UINT64: + case VLVT_WDATA: return status; default: status = false; } } else if (valuep->format == vpiRealVal) { @@ -2384,11 +2443,140 @@ static void vl_strprintf(std::string& buffer, char const* fmt, ...) { va_end(args); } -void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, - const char* fullname) { +// Information about how to access packed array data. +// If underlying type is multi-word (VLVT_WDATA), the packed element might straddle word +// boundaries, in which case m_maskHi != 0. +template +struct VarAccessInfo final { + T* m_datap; // Typed pointer to packed array base address + size_t m_bitOffset; // Data start location (bit offset) + size_t m_wordOffset; // Data start location (word offset, VLVT_WDATA only) + T m_maskLo; // Access mask for m_datap[m_wordOffset] + T m_maskHi; // Access mask for m_datap[m_wordOffset + 1] (VLVT_WDATA only) +}; + +template +VarAccessInfo vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t bitCount, + size_t addOffset) { + // VarAccessInfo generation + // vop - variable to access (already indexed) + // bitCount - how many bits to write/read + // addOffset - additional offset to apply (within the packed array element) + + const size_t wordBits = sizeof(T) * 8; + uint32_t varBits = vop->bitSize(); + + if (vop->varp()->vltype() == VLVT_REAL) varBits *= sizeof(double) * 8; + + // make sure we're not trying to write outside var bounds + assert(varBits > addOffset); + bitCount = std::min(bitCount, varBits - addOffset); + + VarAccessInfo info; + info.m_datap = reinterpret_cast(vop->varDatap()); + if (vop->varp()->vltype() == VLVT_WDATA) { + assert(sizeof(T) == sizeof(EData)); + assert(bitCount <= wordBits); + info.m_wordOffset = (vop->bitOffset() + addOffset) / wordBits; + info.m_bitOffset = (vop->bitOffset() + addOffset) % wordBits; + if (bitCount + info.m_bitOffset <= wordBits) { + // within single word + if (bitCount == wordBits) + info.m_maskLo = ~static_cast(0); + else + info.m_maskLo = (static_cast(1) << bitCount) - 1; + info.m_maskLo = info.m_maskLo << info.m_bitOffset; + info.m_maskHi = 0; + } else { + // straddles word boundary + info.m_maskLo = (static_cast(1) << (wordBits - info.m_bitOffset)) - 1; + info.m_maskLo = info.m_maskLo << info.m_bitOffset; + info.m_maskHi = (static_cast(1) << (bitCount + info.m_bitOffset - wordBits)) - 1; + } + } else { + info.m_wordOffset = 0; + info.m_bitOffset = vop->bitOffset() + addOffset; + assert(bitCount + info.m_bitOffset <= wordBits); + if (bitCount < wordBits) { + info.m_maskLo = (static_cast(1) << bitCount) - 1; + info.m_maskLo = info.m_maskLo << info.m_bitOffset; + } else { + info.m_maskLo = ~static_cast(0); + } + info.m_maskHi = 0; + } + return info; +} + +template +T vl_vpi_get_word_gen(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) { + const size_t wordBits = sizeof(T) * 8; + const VarAccessInfo info = vl_vpi_var_access_info(vop, bitCount, addOffset); + if (info.m_maskHi) + return ((info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset) + | ((info.m_datap[info.m_wordOffset + 1] & info.m_maskHi) + << (wordBits - info.m_bitOffset)); + else + return (info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset; +} + +template +void vl_vpi_put_word_gen(const VerilatedVpioVar* vop, T word, size_t bitCount, size_t addOffset) { + const size_t wordBits = sizeof(T) * 8; + const VarAccessInfo info = vl_vpi_var_access_info(vop, bitCount, addOffset); + + if (info.m_maskHi) { + info.m_datap[info.m_wordOffset + 1] + = (info.m_datap[info.m_wordOffset + 1] & ~info.m_maskHi) + | ((word >> (wordBits - info.m_bitOffset)) & info.m_maskHi); + } + info.m_datap[info.m_wordOffset] = (info.m_datap[info.m_wordOffset] & ~info.m_maskLo) + | ((word << info.m_bitOffset) & info.m_maskLo); +} + +// bitCount: maximum number of bits to read, will stop earlier if it reaches the var bounds +// addOffset: additional read bitoffset +QData vl_vpi_get_word(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) { + switch (vop->varp()->vltype()) { + case VLVT_UINT8: return vl_vpi_get_word_gen(vop, bitCount, addOffset); + case VLVT_UINT16: return vl_vpi_get_word_gen(vop, bitCount, addOffset); + case VLVT_UINT32: return vl_vpi_get_word_gen(vop, bitCount, addOffset); + case VLVT_UINT64: return vl_vpi_get_word_gen(vop, bitCount, addOffset); + case VLVT_WDATA: return vl_vpi_get_word_gen(vop, bitCount, addOffset); + default: + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vltype (%d)", __func__, + vop->varp()->vltype()); + return 0; + } +} + +// word: data to be written +// bitCount: maximum number of bits to write, will stop earlier if it reaches the var bounds +// addOffset: additional write bitoffset +void vl_vpi_put_word(const VerilatedVpioVar* vop, QData word, size_t bitCount, size_t addOffset) { + switch (vop->varp()->vltype()) { + case VLVT_UINT8: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; + case VLVT_UINT16: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; + case VLVT_UINT32: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; + case VLVT_UINT64: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; + case VLVT_WDATA: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; + default: + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vltype (%d)", __func__, + vop->varp()->vltype()); + } +} + +void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) { + const VerilatedVar* varp = vop->varp(); + void* varDatap = vop->varDatap(); + const char* fullname = vop->fullname(); + if (!vl_check_format(varp, valuep, fullname, true)) return; // string data type is dynamic and may vary in size during simulation static thread_local std::string t_outDynamicStr; + + const int varBits = vop->bitSize(); + // We used to presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal // This may cause backward compatibility issues with older code. if (valuep->format == vpiVectorVal) { @@ -2396,27 +2584,8 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, // It only needs to persist until the next vpi_get_value static thread_local t_vpi_vecval t_out[VL_VALUE_STRING_MAX_WORDS * 2]; valuep->value.vector = t_out; - if (varp->vltype() == VLVT_UINT8) { - t_out[0].aval = *(reinterpret_cast(varDatap)); - t_out[0].bval = 0; - return; - } else if (varp->vltype() == VLVT_UINT16) { - t_out[0].aval = *(reinterpret_cast(varDatap)); - t_out[0].bval = 0; - return; - } else if (varp->vltype() == VLVT_UINT32) { - t_out[0].aval = *(reinterpret_cast(varDatap)); - t_out[0].bval = 0; - return; - } else if (varp->vltype() == VLVT_UINT64) { - const QData data = *(reinterpret_cast(varDatap)); - t_out[1].aval = static_cast(data >> 32ULL); - t_out[1].bval = 0; - t_out[0].aval = static_cast(data); - t_out[0].bval = 0; - return; - } else if (varp->vltype() == VLVT_WDATA) { - const int words = VL_WORDS_I(varp->packed().elements()); + if (varp->vltype() == VLVT_WDATA) { + const int words = VL_WORDS_I(varBits); if (VL_UNCOVERABLE(words >= VL_VALUE_STRING_MAX_WORDS)) { VL_VPI_ERROR_( __FILE__, __LINE__, @@ -2424,82 +2593,63 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, "recompile"); return; } - const WDataInP datap = (reinterpret_cast(varDatap)); for (int i = 0; i < words; ++i) { - t_out[i].aval = datap[i]; + t_out[i].aval = vl_vpi_get_word(vop, 32, i * 32); t_out[i].bval = 0; } return; + } else if (varp->vltype() == VLVT_UINT64 && varBits > 32) { + const QData data = vl_vpi_get_word(vop, 64, 0); + t_out[1].aval = static_cast(data >> 32ULL); + t_out[1].bval = 0; + t_out[0].aval = static_cast(data); + t_out[0].bval = 0; + return; + } else { + t_out[0].aval = vl_vpi_get_word(vop, 32, 0); + t_out[0].bval = 0; + return; } } else if (valuep->format == vpiBinStrVal) { - int bits = varp->packed().elements(); - t_outDynamicStr.resize(bits); - const CData* datap = (reinterpret_cast(varDatap)); - for (size_t i = 0; i < bits; ++i) { - const char val = (datap[i >> 3] >> (i & 7)) & 1; - t_outDynamicStr[bits - i - 1] = val ? '1' : '0'; + t_outDynamicStr.resize(varBits); + const CData* datap = reinterpret_cast(varDatap); + for (size_t i = 0; i < varBits; ++i) { + const size_t pos = i + vop->bitOffset(); + const char val = (datap[pos >> 3] >> (pos & 7)) & 1; + t_outDynamicStr[varBits - i - 1] = val ? '1' : '0'; } valuep->value.str = const_cast(t_outDynamicStr.c_str()); return; } else if (valuep->format == vpiOctStrVal) { - int chars = (varp->packed().elements() + 2) / 3; + const int chars = (varBits + 2) / 3; t_outDynamicStr.resize(chars); - const int bytes = VL_BYTES_I(varp->packed().elements()); - const CData* datap = (reinterpret_cast(varDatap)); for (size_t i = 0; i < chars; ++i) { - const div_t idx = div(i * 3, 8); - int val = datap[idx.quot]; - if ((idx.quot + 1) < bytes) { - // if the next byte is valid or that in - // for when the required 3 bits straddle adjacent bytes - val |= datap[idx.quot + 1] << 8; - } - // align so least significant 3 bits represent octal char - val >>= idx.rem; - if (i == (chars - 1)) { - // most significant char, mask off nonexistent bits when vector - // size is not a multiple of 3 - const unsigned int rem = varp->packed().elements() % 3; - if (rem) { - // generate bit mask & zero nonexistent bits - val &= (1 << rem) - 1; - } - } - t_outDynamicStr[chars - i - 1] = '0' + (val & 7); + const char val = vl_vpi_get_word(vop, 3, i * 3); + t_outDynamicStr[chars - i - 1] = '0' + val; } valuep->value.str = const_cast(t_outDynamicStr.c_str()); return; } else if (valuep->format == vpiDecStrVal) { if (varp->vltype() == VLVT_UINT8) { vl_strprintf(t_outDynamicStr, "%hhu", - static_cast(*(reinterpret_cast(varDatap)))); + static_cast(vl_vpi_get_word(vop, 8, 0))); } else if (varp->vltype() == VLVT_UINT16) { vl_strprintf(t_outDynamicStr, "%hu", - static_cast(*(reinterpret_cast(varDatap)))); + static_cast(vl_vpi_get_word(vop, 16, 0))); } else if (varp->vltype() == VLVT_UINT32) { vl_strprintf(t_outDynamicStr, "%u", - static_cast(*(reinterpret_cast(varDatap)))); + static_cast(vl_vpi_get_word(vop, 32, 0))); } else if (varp->vltype() == VLVT_UINT64) { vl_strprintf(t_outDynamicStr, "%llu", // lintok-format-ll - static_cast(*(reinterpret_cast(varDatap)))); + static_cast(vl_vpi_get_word(vop, 64, 0))); } valuep->value.str = const_cast(t_outDynamicStr.c_str()); return; } else if (valuep->format == vpiHexStrVal) { - int chars = (varp->packed().elements() + 3) >> 2; + const int chars = (varBits + 3) >> 2; t_outDynamicStr.resize(chars); - const CData* datap = (reinterpret_cast(varDatap)); for (size_t i = 0; i < chars; ++i) { - char val = (datap[i >> 1] >> ((i & 1) << 2)) & 15; - if (i == (chars - 1)) { - // most significant char, mask off nonexistent bits when vector - // size is not a multiple of 4 - const unsigned int rem = varp->packed().elements() & 3; - if (rem) { - // generate bit mask & zero nonexistent bits - val &= (1 << rem) - 1; - } - } + const char val = vl_vpi_get_word(vop, 4, i * 4); t_outDynamicStr[chars - i - 1] = "0123456789abcdef"[static_cast(val)]; } valuep->value.str = const_cast(t_outDynamicStr.c_str()); @@ -2510,35 +2660,26 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, valuep->value.str = reinterpret_cast(varDatap); return; } else { - t_outDynamicStr = *(reinterpret_cast(varDatap)); + t_outDynamicStr = *(vop->varStringDatap()); valuep->value.str = const_cast(t_outDynamicStr.c_str()); return; } } else { - int bytes = VL_BYTES_I(varp->packed().elements()); - t_outDynamicStr.resize(bytes); - const CData* datap = (reinterpret_cast(varDatap)); - for (size_t i = 0; i < bytes; ++i) { - const char val = datap[bytes - i - 1]; + const int chars = VL_BYTES_I(varBits); + t_outDynamicStr.resize(chars); + for (size_t i = 0; i < chars; ++i) { + const char val = vl_vpi_get_word(vop, 8, i * 8); // other simulators replace [leading?] zero chars with spaces, replicate here. - t_outDynamicStr[i] = val ? val : ' '; + t_outDynamicStr[chars - i - 1] = val ? val : ' '; } valuep->value.str = const_cast(t_outDynamicStr.c_str()); return; } } else if (valuep->format == vpiIntVal) { - if (varp->vltype() == VLVT_UINT8) { - valuep->value.integer = *(reinterpret_cast(varDatap)); - return; - } else if (varp->vltype() == VLVT_UINT16) { - valuep->value.integer = *(reinterpret_cast(varDatap)); - return; - } else if (varp->vltype() == VLVT_UINT32) { - valuep->value.integer = *(reinterpret_cast(varDatap)); - return; - } + valuep->value.integer = vl_vpi_get_word(vop, 32, 0); + return; } else if (valuep->format == vpiRealVal) { - valuep->value.real = *(reinterpret_cast(varDatap)); + valuep->value.real = *(vop->varRealDatap()); return; } else if (valuep->format == vpiSuppressVal) { return; @@ -2554,10 +2695,10 @@ void vpi_get_value(vpiHandle object, p_vpi_value valuep) { if (VL_UNLIKELY(!valuep)) return; if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) { - vl_get_value(vop->varp(), vop->varDatap(), valuep, vop->fullname()); + vl_vpi_get_value(vop, valuep); return; } else if (const VerilatedVpioParam* const vop = VerilatedVpioParam::castp(object)) { - vl_get_value(vop->varp(), vop->varDatap(), valuep, vop->fullname()); + vl_vpi_get_value(vop, valuep); return; } else if (const VerilatedVpioConst* const vop = VerilatedVpioConst::castp(object)) { if (valuep->format == vpiIntVal) { @@ -2580,8 +2721,21 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ VL_VPI_WARNING_(__FILE__, __LINE__, "Ignoring vpi_put_value with nullptr value pointer"); return nullptr; } - PLI_INT32 delay_mode = flags & 0xfff; + const PLI_INT32 delay_mode = flags & 0xfff; if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) { + VL_DEBUG_IF_PLI( + VL_DBG_MSGF("- vpi: vpi_put_value name=%s fmt=%d vali=%d\n", vop->fullname(), + valuep->format, valuep->value.integer); + VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n", vop->varp()->datap(), vop->varDatap());); + + if (VL_UNLIKELY(!vop->varp()->isPublicRW())) { + VL_VPI_ERROR_(__FILE__, __LINE__, + "vpi_put_value was used on signal marked read-only," + " use public_flat_rw instead for %s : %s", + vop->fullname(), vop->scopep()->defname()); + return nullptr; + } + if (!vl_check_format(vop->varp(), valuep, vop->fullname(), false)) return nullptr; if (delay_mode == vpiInertialDelay) { if (!VerilatedVpiPutHolder::canInertialDelay(valuep)) { VL_VPI_WARNING_( @@ -2593,111 +2747,51 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ VerilatedVpiImp::inertialDelay(vop, valuep); return object; } - VL_DEBUG_IF_PLI( - VL_DBG_MSGF("- vpi: vpi_put_value name=%s fmt=%d vali=%d\n", vop->fullname(), - valuep->format, valuep->value.integer); - VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n", vop->varp()->datap(), vop->varDatap());); VerilatedVpiImp::evalNeeded(true); - - if (VL_UNLIKELY(!vop->varp()->isPublicRW())) { - VL_VPI_WARNING_(__FILE__, __LINE__, - "Ignoring vpi_put_value to signal marked read-only," - " use public_flat_rw instead: %s", - vop->fullname()); - return nullptr; - } - if (!vl_check_format(vop->varp(), valuep, vop->fullname(), false)) return nullptr; + const int varBits = vop->bitSize(); if (valuep->format == vpiVectorVal) { if (VL_UNLIKELY(!valuep->value.vector)) return nullptr; - if (vop->varp()->vltype() == VLVT_UINT8) { - *(reinterpret_cast(vop->varDatap())) - = valuep->value.vector[0].aval & vop->mask(); + if (vop->varp()->vltype() == VLVT_WDATA) { + const int words = VL_WORDS_I(varBits); + for (int i = 0; i < words; ++i) + vl_vpi_put_word(vop, valuep->value.vector[i].aval, 32, i * 32); return object; - } else if (vop->varp()->vltype() == VLVT_UINT16) { - *(reinterpret_cast(vop->varDatap())) - = valuep->value.vector[0].aval & vop->mask(); + } else if (vop->varp()->vltype() == VLVT_UINT64 && varBits > 32) { + const QData val = (static_cast(valuep->value.vector[1].aval) << 32) + | static_cast(valuep->value.vector[0].aval); + vl_vpi_put_word(vop, val, 64, 0); return object; - } else if (vop->varp()->vltype() == VLVT_UINT32) { - *(reinterpret_cast(vop->varDatap())) - = valuep->value.vector[0].aval & vop->mask(); - return object; - } else if (vop->varp()->vltype() == VLVT_UINT64) { - *(reinterpret_cast(vop->varDatap())) = VL_SET_QII( - valuep->value.vector[1].aval & vop->mask(), valuep->value.vector[0].aval); - return object; - } else if (vop->varp()->vltype() == VLVT_WDATA) { - const int words = VL_WORDS_I(vop->varp()->packed().elements()); - WDataOutP datap = (reinterpret_cast(vop->varDatap())); - for (int i = 0; i < words; ++i) { - datap[i] = valuep->value.vector[i].aval; - if (i == (words - 1)) datap[i] &= vop->mask(); - } + } else { + vl_vpi_put_word(vop, valuep->value.vector[0].aval, 32, 0); return object; } } else if (valuep->format == vpiBinStrVal) { - const int bits = vop->varp()->packed().elements(); const int len = std::strlen(valuep->value.str); - CData* const datap = (reinterpret_cast(vop->varDatap())); - for (int i = 0; i < bits; ++i) { - const char set = (i < len) ? (valuep->value.str[len - i - 1] == '1') : 0; - // zero bits 7:1 of byte when assigning to bit 0, else - // or in 1 if bit set - if (i & 7) { - datap[i >> 3] |= set << (i & 7); - } else { - datap[i >> 3] = set; - } + CData* const datap = reinterpret_cast(vop->varDatap()); + for (int i = 0; i < varBits; ++i) { + const bool set = (i < len) && (valuep->value.str[len - i - 1] == '1'); + const size_t pos = vop->bitOffset() + i; + + if (set) + datap[pos >> 3] |= 1 << (pos & 7); + else + datap[pos >> 3] &= ~(1 << (pos & 7)); } return object; } else if (valuep->format == vpiOctStrVal) { - const int chars = (vop->varp()->packed().elements() + 2) / 3; - const int bytes = VL_BYTES_I(vop->varp()->packed().elements()); const int len = std::strlen(valuep->value.str); - CData* const datap = (reinterpret_cast(vop->varDatap())); - div_t idx; - datap[0] = 0; // reset zero'th byte - for (int i = 0; i < chars; ++i) { - union { - char byte[2]; - uint16_t half; - } val; - idx = div(i * 3, 8); - if (i < len) { - // ignore illegal chars - const char digit = valuep->value.str[len - i - 1]; - if (digit >= '0' && digit <= '7') { - val.half = digit - '0'; - } else { - VL_VPI_WARNING_(__FILE__, __LINE__, - "%s: Non octal character '%c' in '%s' as value %s for %s", - __func__, digit, valuep->value.str, - VerilatedVpiError::strFromVpiVal(valuep->format), - vop->fullname()); - val.half = 0; - } - } else { - val.half = 0; - } - // align octal character to position within vector, note that - // the three bits may straddle a byte boundary so two byte wide - // assignments are made to adjacent bytes - but not if the least - // significant byte of the aligned value is the most significant - // byte of the destination. - val.half <<= idx.rem; - datap[idx.quot] |= val.byte[0]; // or in value - if ((idx.quot + 1) < bytes) { - datap[idx.quot + 1] = val.byte[1]; // this also resets - // all bits to 0 prior to or'ing above + for (int i = 0; i < len; ++i) { + char digit = valuep->value.str[len - i - 1] - '0'; + if (digit < 0 || digit > 7) { + VL_VPI_WARNING_(__FILE__, __LINE__, + "%s: Non octal character '%c' in '%s' as value %s for %s", + __func__, digit + '0', valuep->value.str, + VerilatedVpiError::strFromVpiVal(valuep->format), + vop->fullname()); + digit = 0; } + vl_vpi_put_word(vop, digit, 3, i * 3); } - // mask off non-existent bits in the most significant byte - if (idx.quot == (bytes - 1)) { - datap[idx.quot] &= vop->mask_byte(idx.quot); - } else if (idx.quot + 1 == (bytes - 1)) { - datap[idx.quot + 1] &= vop->mask_byte(idx.quot + 1); - } - // zero off remaining top bytes - for (int i = idx.quot + 2; i < bytes; ++i) datap[i] = 0; return object; } else if (valuep->format == vpiDecStrVal) { char remainder[16]; @@ -2716,23 +2810,10 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ remainder, valuep->value.str, VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname()); } - if (vop->varp()->vltype() == VLVT_UINT8) { - *(reinterpret_cast(vop->varDatap())) = val & vop->mask(); - return object; - } else if (vop->varp()->vltype() == VLVT_UINT16) { - *(reinterpret_cast(vop->varDatap())) = val & vop->mask(); - return object; - } else if (vop->varp()->vltype() == VLVT_UINT32) { - *(reinterpret_cast(vop->varDatap())) = val & vop->mask(); - return object; - } else if (vop->varp()->vltype() == VLVT_UINT64) { - *(reinterpret_cast(vop->varDatap())) = val; - (reinterpret_cast(vop->varDatap()))[1] &= vop->mask(); - return object; - } + vl_vpi_put_word(vop, val, 64, 0); + return object; } else if (valuep->format == vpiHexStrVal) { - const int chars = (vop->varp()->packed().elements() + 3) >> 2; - CData* const datap = (reinterpret_cast(vop->varDatap())); + const int chars = (varBits + 3) >> 2; const char* val = valuep->value.str; // skip hex ident if one is detected at the start of the string if (val[0] == '0' && (val[1] == 'x' || val[1] == 'X')) val += 2; @@ -2760,44 +2841,29 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ hex = 0; } // assign hex digit value to destination - if (i & 1) { - datap[i >> 1] |= hex << 4; - } else { - datap[i >> 1] = hex; // this also resets all - // bits to 0 prior to or'ing above of the msb - } + vl_vpi_put_word(vop, hex, 4, i * 4); } - // apply bit mask to most significant byte - datap[(chars - 1) >> 1] &= vop->mask_byte((chars - 1) >> 1); return object; } else if (valuep->format == vpiStringVal) { if (vop->varp()->vltype() == VLVT_STRING) { - *(reinterpret_cast(vop->varDatap())) = valuep->value.str; + *(vop->varStringDatap()) = valuep->value.str; return object; } else { - const int bytes = VL_BYTES_I(vop->varp()->packed().elements()); + const int chars = VL_BYTES_I(varBits); const int len = std::strlen(valuep->value.str); - CData* const datap = (reinterpret_cast(vop->varDatap())); - for (int i = 0; i < bytes; ++i) { + for (int i = 0; i < chars; ++i) { // prepend with 0 values before placing string the least significant bytes - datap[i] = (i < len) ? valuep->value.str[len - i - 1] : 0; + const char c = (i < len) ? valuep->value.str[len - i - 1] : 0; + vl_vpi_put_word(vop, c, 8, i * 8); } } return object; } else if (valuep->format == vpiIntVal) { - if (vop->varp()->vltype() == VLVT_UINT8) { - *(reinterpret_cast(vop->varDatap())) = vop->mask() & valuep->value.integer; - return object; - } else if (vop->varp()->vltype() == VLVT_UINT16) { - *(reinterpret_cast(vop->varDatap())) = vop->mask() & valuep->value.integer; - return object; - } else if (vop->varp()->vltype() == VLVT_UINT32) { - *(reinterpret_cast(vop->varDatap())) = vop->mask() & valuep->value.integer; - return object; - } + vl_vpi_put_word(vop, valuep->value.integer, 64, 0); + return object; } else if (valuep->format == vpiRealVal) { if (vop->varp()->vltype() == VLVT_REAL) { - *(reinterpret_cast(vop->varDatap())) = valuep->value.real; + *(vop->varRealDatap()) = valuep->value.real; return object; } } @@ -2817,13 +2883,634 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ return nullptr; } -void vpi_get_value_array(vpiHandle /*object*/, p_vpi_arrayvalue /*arrayvalue_p*/, - PLI_INT32* /*index_p*/, PLI_UINT32 /*num*/) { - VL_VPI_UNIMP_(); +bool vl_check_array_format(const VerilatedVar* varp, const p_vpi_arrayvalue arrayvalue_p, + const char* fullname) { + if (arrayvalue_p->format == vpiVectorVal) { + switch (varp->vltype()) { + case VLVT_UINT8: + case VLVT_UINT16: + case VLVT_UINT32: + case VLVT_UINT64: + case VLVT_WDATA: return true; + default:; + } + } else if (arrayvalue_p->format == vpiIntVal) { + switch (varp->vltype()) { + case VLVT_UINT8: + case VLVT_UINT16: + case VLVT_UINT32: return true; + default:; + } + } else if ((arrayvalue_p->format == vpiRawTwoStateVal) + || (arrayvalue_p->format == vpiRawFourStateVal)) { + switch (varp->vltype()) { + case VLVT_UINT8: + case VLVT_UINT16: + case VLVT_UINT32: + case VLVT_UINT64: + case VLVT_WDATA: return true; + default:; + } + } else if (arrayvalue_p->format == vpiShortIntVal) { + switch (varp->vltype()) { + case VLVT_UINT8: + case VLVT_UINT16: return true; + default:; + } + } else if (arrayvalue_p->format == vpiLongIntVal) { + switch (varp->vltype()) { + case VLVT_UINT8: + case VLVT_UINT16: + case VLVT_UINT32: + case VLVT_UINT64: return true; + default:; + } + } + + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", __func__, + VerilatedVpiError::strFromVpiVal(arrayvalue_p->format), fullname); + + return false; } -void vpi_put_value_array(vpiHandle /*object*/, p_vpi_arrayvalue /*arrayvalue_p*/, - PLI_INT32* /*index_p*/, PLI_UINT32 /*num*/) { - VL_VPI_UNIMP_(); + +template +void vl_get_value_array_integrals(unsigned index, const unsigned num, const unsigned size, + const unsigned packedSize, const bool leftIsLow, const T* src, + K* dst) { + static_assert(sizeof(K) >= sizeof(T), "size of type K is less than size of type T"); + for (int i = 0; i < num; i++) { + dst[i] = src[index]; + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } +} + +template +void vl_put_value_array_integrals(unsigned index, const unsigned num, const unsigned size, + const unsigned packedSize, const bool leftIsLow, const T* src, + K* dst) { + static_assert(std::is_integral::value, "type T is not an integral type"); + static_assert(std::is_unsigned::value, "type T is not unsigned"); + static_assert(sizeof(T) >= sizeof(K), "size of type T is less than size of type K"); + const unsigned element_size_bytes = VL_BYTES_I(packedSize); + const T mask = element_size_bytes == sizeof(T) + ? static_cast(-1) + : ~(static_cast(-1) << (element_size_bytes * 8)); + for (unsigned i = 0; i < num; i++) { + dst[index] = src[i] & static_cast(mask); + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } +} + +template +void vl_get_value_array_vectors(unsigned index, const unsigned num, const unsigned size, + const unsigned packedSize, const bool leftIsLow, const T* src, + p_vpi_vecval dst) { + static_assert(std::is_unsigned::value, + "type T is not unsigned"); // ensure logical right shift + const unsigned element_size_bytes = VL_BYTES_I(packedSize); + const unsigned element_size_words = VL_WORDS_I(packedSize); + const unsigned element_size_repr = (element_size_bytes + sizeof(T) - 1) / sizeof(T); + if (sizeof(T) == sizeof(QData)) { + for (unsigned i = 0; i < num; i++) { + dst[i * 2].aval = static_cast(src[index]); + dst[i * 2].bval = 0; + dst[(i * 2) + 1].aval = static_cast(src[index]) >> 32; + dst[(i * 2) + 1].bval = 0; + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } + } else { + for (unsigned i = 0; i < num; i++) { + const size_t dst_index = i * element_size_words; + const size_t src_index = index * element_size_words; + for (unsigned j = 0; j < element_size_words; j++) { + dst[dst_index + j].aval = src[src_index + j]; + dst[dst_index + j].bval = 0; + } + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } + } +} + +template +void vl_put_value_array_vectors(unsigned index, const unsigned num, const unsigned size, + const unsigned packedSize, const bool leftIsLow, + const bool fourState, const p_vpi_vecval src, T* dst) { + static_assert(std::is_unsigned::value, "type T is not unsigned"); + static_assert(std::is_integral::value, "type T is not an integral type"); + const unsigned element_size_bytes VL_BYTES_I(packedSize); + const unsigned element_size_words VL_WORDS_I(packedSize); + if (sizeof(T) == sizeof(QData)) { //destination is QDATA + const QData mask = element_size_bytes == sizeof(T) + ? static_cast(-1) + : ~(static_cast(-1) << (element_size_bytes * 8)); + for (unsigned i = 0; i < num; i++) { + dst[index] = src[i * 2].aval; + dst[index] + |= (static_cast(src[(i * 2) + 1].aval) << (sizeof(PLI_UINT32) * 8)) & mask; + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } + } else { + for (unsigned i = 0; i < num; i++) { + unsigned bytes_stored = 0; + for (unsigned j = 0; j < element_size_words; j++) { + if (bytes_stored >= element_size_bytes) break; + const T mask + = (element_size_bytes - bytes_stored) >= sizeof(PLI_UINT32) + ? static_cast(-1) + : ~(static_cast(-1) << ((element_size_bytes - bytes_stored) * 8)); + dst[(index * element_size_words) + j] + = static_cast(src[(i * element_size_words) + j].aval) & mask; + bytes_stored += sizeof(PLI_UINT32); + } + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } + } +} + +template +void vl_get_value_array_rawvals(unsigned index, unsigned num, const unsigned size, + const unsigned packedSize, const bool leftIsLow, + const bool fourState, const T* src, PLI_BYTE8* dst) { + static_assert(std::is_unsigned::value, + "type T is not unsigned"); //ensure loigcal right shift + const unsigned element_size_bytes VL_BYTES_I(packedSize); + const unsigned element_size_repr = (element_size_bytes + sizeof(T) - 1) / sizeof(T); + size_t dst_index = 0; + while (num-- > 0) { + const size_t src_offset = index * element_size_repr; + unsigned bytes_copied = 0; + for (unsigned j = 0; j < element_size_repr; j++) { + const T& src_data = src[src_offset + j]; + for (unsigned k = 0; k < sizeof(T); k++) { + if (bytes_copied++ == element_size_bytes) break; + dst[dst_index++] = src_data >> (k * 8); + } + } + if (fourState) { + std::fill(dst + dst_index, dst + dst_index + element_size_bytes, 0); + dst_index += element_size_bytes; + } + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } +} + +template +void vl_put_value_array_rawvals(unsigned index, const unsigned num, const unsigned size, + const unsigned packedSize, const bool leftIsLow, + const bool fourState, const PLI_UBYTE8* src, T* dst) { + const unsigned element_size_bytes VL_BYTES_I(packedSize); + const unsigned element_size_repr = (element_size_bytes + sizeof(T) - 1) / sizeof(T); + for (unsigned i = 0; i < num; i++) { + unsigned bytes_copied = 0; + const size_t dst_offset = index * element_size_repr; + const size_t src_offset = i * element_size_bytes; + for (unsigned j = 0; j < element_size_repr; j++) { + T& dst_data = dst[dst_offset + j]; + for (unsigned k = 0; k < sizeof(T); k++) { + if (bytes_copied == element_size_bytes) break; + const unsigned src_index + = fourState ? (src_offset * 2) + bytes_copied : (src_offset) + bytes_copied; + dst_data &= ~((static_cast(0xFF) & 0xFF) << (k * 8)); + dst_data |= ((static_cast(src[src_index]) & 0xFF) << (k * 8)); + bytes_copied++; + } + } + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } +} + +void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT32* index_p, + PLI_UINT32 num) { + const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); + if (!vl_check_array_format(vop->varp(), arrayvalue_p, vop->fullname())) return; + + const VerilatedVar* const varp = vop->varp(); + + static thread_local EData out_data[VL_VALUE_STRING_MAX_WORDS * 2]; + + const unsigned size = vop->size(); + if (VL_UNCOVERABLE(num > size)) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: requested elements (%u) exceed array size (%u)", + __func__, num, size); + return; + } + + const bool leftIsLow = vop->rangep()->left() == vop->rangep()->low(); + int index + = leftIsLow ? index_p[0] - vop->rangep()->left() : vop->rangep()->left() - index_p[0]; + + if (arrayvalue_p->format == vpiShortIntVal) { + if (VL_UNCOVERABLE((sizeof(PLI_INT16) * num) >= VL_VALUE_STRING_MAX_CHARS)) { + VL_FATAL_MT(__FILE__, __LINE__, "", + "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; " + "increase and recompile"); + } + + PLI_INT16* shortintsp = (PLI_INT16*)out_data; + arrayvalue_p->value.shortints = shortintsp; + + if (varp->vltype() == VLVT_UINT8) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varCDatap(), shortintsp); + } else if (varp->vltype() == VLVT_UINT16) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varSDatap(), shortintsp); + } + + return; + } else if (arrayvalue_p->format == vpiIntVal) { + if (VL_UNCOVERABLE(num >= VL_VALUE_STRING_MAX_WORDS)) { + VL_FATAL_MT(__FILE__, __LINE__, "", + "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; " + "increase and recompile"); + } + + PLI_INT32* integersp = (PLI_INT32*)out_data; + arrayvalue_p->value.integers = integersp; + + if (varp->vltype() == VLVT_UINT8) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varCDatap(), integersp); + } else if (varp->vltype() == VLVT_UINT16) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varSDatap(), integersp); + } else if (varp->vltype() == VLVT_UINT32) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varIDatap(), integersp); + } + + return; + } else if (arrayvalue_p->format == vpiLongIntVal) { + if (VL_UNCOVERABLE((sizeof(PLI_INT64) * num) >= VL_VALUE_STRING_MAX_CHARS)) { + VL_FATAL_MT(__FILE__, __LINE__, "", + "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; " + "increase and recompile"); + } + + PLI_INT64* longintsp = (PLI_INT64*)out_data; + arrayvalue_p->value.longints = longintsp; + + if (varp->vltype() == VLVT_UINT8) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varCDatap(), longintsp); + } else if (varp->vltype() == VLVT_UINT16) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varSDatap(), longintsp); + } else if (varp->vltype() == VLVT_UINT32) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varIDatap(), longintsp); + } else if (varp->vltype() == VLVT_UINT64) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varQDatap(), longintsp); + } + + return; + } else if (arrayvalue_p->format == vpiVectorVal) { + if (VL_UNCOVERABLE((VL_WORDS_I(varp->entBits()) * 2 * num) >= VL_VALUE_STRING_MAX_WORDS)) { + VL_FATAL_MT(__FILE__, __LINE__, "", + "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; " + "increase and recompile"); + } + + p_vpi_vecval vectorsp = (p_vpi_vecval)out_data; + arrayvalue_p->value.vectors = vectorsp; + + if (varp->vltype() == VLVT_UINT8) { + vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, + vop->varCDatap(), vectorsp); + } else if (varp->vltype() == VLVT_UINT16) { + vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, + vop->varSDatap(), vectorsp); + } else if (varp->vltype() == VLVT_UINT32) { + vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, + vop->varIDatap(), vectorsp); + } else if (varp->vltype() == VLVT_UINT64) { + vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, + vop->varQDatap(), vectorsp); + } else if (varp->vltype() == VLVT_WDATA) { + vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, + vop->varEDatap(), vectorsp); + } + + return; + } else if (arrayvalue_p->format == vpiRawFourStateVal) { + if (VL_UNCOVERABLE((VL_BYTES_I(varp->entBits()) * 2 * num) >= VL_VALUE_STRING_MAX_CHARS)) { + VL_FATAL_MT(__FILE__, __LINE__, "", + "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; " + "increase and recompile"); + } + + PLI_BYTE8* valuep = (PLI_BYTE8*)out_data; + arrayvalue_p->value.rawvals = valuep; + + if (varp->vltype() == VLVT_UINT8) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, + vop->varCDatap(), valuep); + } else if (varp->vltype() == VLVT_UINT16) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, + vop->varSDatap(), valuep); + } else if (varp->vltype() == VLVT_UINT32) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, + vop->varIDatap(), valuep); + } else if (varp->vltype() == VLVT_UINT64) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, + vop->varQDatap(), valuep); + } else if (varp->vltype() == VLVT_WDATA) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, + vop->varEDatap(), valuep); + } + + return; + } else if (arrayvalue_p->format == vpiRawTwoStateVal) { + if (VL_UNCOVERABLE((VL_BYTES_I(varp->entBits()) * num) >= VL_VALUE_STRING_MAX_CHARS)) { + VL_FATAL_MT(__FILE__, __LINE__, "", + "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; " + "increase and recompile"); + } + + PLI_BYTE8* valuep = (PLI_BYTE8*)out_data; + arrayvalue_p->value.rawvals = valuep; + + if (varp->vltype() == VLVT_UINT8) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, + vop->varCDatap(), valuep); + } else if (varp->vltype() == VLVT_UINT16) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, + vop->varSDatap(), valuep); + } else if (varp->vltype() == VLVT_UINT32) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, + vop->varIDatap(), valuep); + } else if (varp->vltype() == VLVT_UINT64) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, + vop->varQDatap(), valuep); + } else if (varp->vltype() == VLVT_WDATA) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, + vop->varEDatap(), valuep); + } + + return; + } + + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", __func__, + VerilatedVpiError::strFromVpiVal(arrayvalue_p->format), vop->fullname()); +} + +void vpi_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT32* index_p, + PLI_UINT32 num) { + VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get_value_array %p\n", object);); + VerilatedVpiImp::assertOneCheck(); + + VL_VPI_ERROR_RESET_(); + if (VL_UNLIKELY(!object)) return; + + if (VL_UNLIKELY(!arrayvalue_p)) { + VL_VPI_WARNING_(__FILE__, __LINE__, + "Ignoring vpi_get_value_array with null value pointer"); + return; + } + + if (VL_UNLIKELY(!index_p)) { + VL_VPI_WARNING_(__FILE__, __LINE__, + "Ignoring vpi_get_value_array with null index pointer"); + return; + } + + const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); + if (VL_UNLIKELY(!vop)) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object); + return; + } + + if (vop->type() != vpiRegArray) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%p, %s)", __func__, object, + VerilatedVpiError::strFromVpiObjType(vop->type())); + return; + } + + int lowRange = vop->rangep()->low(); + int highRange = vop->rangep()->high(); + + if ((index_p[0] > highRange) || (index_p[0] < lowRange)) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: index %u for object %s is out of bounds [%u,%u]", + __func__, index_p[0], vop->fullname(), lowRange, highRange); + return; + } + + if (arrayvalue_p->flags & vpiUserAllocFlag) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiUserAllocFlag (%x)", __func__, + arrayvalue_p->flags); + return; + } + + vl_get_value_array(object, arrayvalue_p, index_p, num); +} + +void vl_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT32* index_p, + PLI_UINT32 num) { + const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); + if (!vl_check_array_format(vop->varp(), arrayvalue_p, vop->fullname())) return; + + const VerilatedVar* const varp = vop->varp(); + + int size = vop->size(); + if (VL_UNCOVERABLE(num > size)) { + VL_VPI_ERROR_(__FILE__, __LINE__, + "%s: requested elements to set (%u) exceed array size (%u)", __func__, num, + size); + return; + } + + const bool leftIsLow = vop->rangep()->left() == vop->rangep()->low(); + int index + = leftIsLow ? index_p[0] - vop->rangep()->left() : vop->rangep()->left() - index_p[0]; + + if (arrayvalue_p->format == vpiShortIntVal) { + const PLI_UINT16* shortintsp + = reinterpret_cast(arrayvalue_p->value.shortints); + + if (varp->vltype() == VLVT_UINT8) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp, + vop->varCDatap()); + } else if (varp->vltype() == VLVT_UINT16) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp, + vop->varSDatap()); + } + + return; + } else if (arrayvalue_p->format == vpiIntVal) { + const PLI_UINT32* integersp = reinterpret_cast(arrayvalue_p->value.integers); + + if (varp->vltype() == VLVT_UINT8) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp, + vop->varCDatap()); + } else if (varp->vltype() == VLVT_UINT16) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp, + vop->varSDatap()); + } else if (varp->vltype() == VLVT_UINT32) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp, + vop->varIDatap()); + } + + return; + } else if (arrayvalue_p->format == vpiLongIntVal) { + const PLI_UINT64* longintsp = reinterpret_cast(arrayvalue_p->value.longints); + + if (varp->vltype() == VLVT_UINT8) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp, + vop->varCDatap()); + } else if (varp->vltype() == VLVT_UINT16) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp, + vop->varSDatap()); + } else if (varp->vltype() == VLVT_UINT32) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp, + vop->varIDatap()); + } else if (varp->vltype() == VLVT_UINT64) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp, + vop->varQDatap()); + } + + return; + } else if (arrayvalue_p->format == vpiVectorVal) { + const p_vpi_vecval vectorsp = arrayvalue_p->value.vectors; + + if (varp->vltype() == VLVT_UINT8) { + vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, + vectorsp, vop->varCDatap()); + } else if (varp->vltype() == VLVT_UINT16) { + vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, + vectorsp, vop->varSDatap()); + } else if (varp->vltype() == VLVT_UINT32) { + vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, + vectorsp, vop->varIDatap()); + } else if (varp->vltype() == VLVT_UINT64) { + vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, + vectorsp, vop->varQDatap()); + } else if (varp->vltype() == VLVT_WDATA) { + vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, + vectorsp, vop->varEDatap()); + } + + return; + } else if (arrayvalue_p->format == vpiRawFourStateVal) { + const PLI_UBYTE8* valuep = reinterpret_cast(arrayvalue_p->value.rawvals); + + if (varp->vltype() == VLVT_UINT8) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, + vop->varCDatap()); + } else if (varp->vltype() == VLVT_UINT16) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, + vop->varSDatap()); + } else if (varp->vltype() == VLVT_UINT32) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, + vop->varIDatap()); + } else if (varp->vltype() == VLVT_UINT64) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, + vop->varQDatap()); + } else if (varp->vltype() == VLVT_WDATA) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, + vop->varEDatap()); + } + + return; + } else if (arrayvalue_p->format == vpiRawTwoStateVal) { + const PLI_UBYTE8* valuep = reinterpret_cast(arrayvalue_p->value.rawvals); + + if (varp->vltype() == VLVT_UINT8) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, + vop->varCDatap()); + } else if (varp->vltype() == VLVT_UINT16) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, + vop->varSDatap()); + } else if (varp->vltype() == VLVT_UINT32) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, + vop->varIDatap()); + } else if (varp->vltype() == VLVT_UINT64) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, + vop->varQDatap()); + } else if (varp->vltype() == VLVT_WDATA) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, + vop->varEDatap()); + } + + return; + } + + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", __func__, + VerilatedVpiError::strFromVpiVal(arrayvalue_p->format), vop->fullname()); +} + +void vpi_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT32* index_p, + PLI_UINT32 num) { + VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_put_value_array %p\n", object);); + VerilatedVpiImp::assertOneCheck(); + VL_VPI_ERROR_RESET_(); + + if (VL_UNLIKELY(!arrayvalue_p)) { + VL_VPI_WARNING_(__FILE__, __LINE__, + "Ignoring vpi_put_value_array with null value pointer"); + return; + } + + if (VL_UNLIKELY(!index_p)) { + VL_VPI_WARNING_(__FILE__, __LINE__, + "Ignoring vpi_put_value_array with null index pointer"); + return; + } + + const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); + if (VL_UNLIKELY(!vop)) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object); + return; + } + + if (vop->type() != vpiRegArray) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%p, %s)", __func__, object, + VerilatedVpiError::strFromVpiObjType(vop->type())); + return; + } + + const VerilatedVar* const varp = vop->varp(); + + int lowRange = vop->rangep()->low(); + int highRange = vop->rangep()->high(); + + if ((index_p[0] > highRange) || (index_p[0] < lowRange)) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: index %u for object %s is out of bounds [%u,%u]", + __func__, index_p[0], vop->fullname(), lowRange, highRange); + return; + } + + if (VL_UNLIKELY(!vop->varp()->isPublicRW())) { + VL_VPI_ERROR_(__FILE__, __LINE__, + "Ignoring vpi_put_value_array to signal marked read-only," + " use public_flat_rw instead: %s", + vop->fullname()); + return; + } + + if (arrayvalue_p->flags & (vpiPropagateOff | vpiOneValue)) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported flags (%x)", __func__, + arrayvalue_p->flags); + return; + } + + vl_put_value_array(object, arrayvalue_p, index_p, num); } // time processing diff --git a/include/verilatedos.h b/include/verilatedos.h index 4022ac2b0..be0cd59a2 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -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 diff --git a/src/V3Active.cpp b/src/V3Active.cpp index f9c419314..9352737a6 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -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(nodep->fileline()) : oldsensesp ? m_namer.getActive(nodep->fileline(), oldsensesp) - : m_namer.getSpecialActive(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(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}; diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index 85d65a561..9ca686a7b 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -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 { diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index c7d926a3e..8767add80 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -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 diff --git a/src/V3Ast.h b/src/V3Ast.h index 7d6ddf3dc..ba483fa94 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -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); diff --git a/src/V3AstInlines.h b/src/V3AstInlines.h index 90ebccfd8..3f6b06c7e 100644 --- a/src/V3AstInlines.h +++ b/src/V3AstInlines.h @@ -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()); } diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index 83b80717f..1cc96fdbe 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -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 diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 13382f64b..5074373aa 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -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); } diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 811850d71..524c57bc9 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -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; diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index b792de2b0..9e20b8b07 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -570,15 +570,28 @@ string AstVar::vlEnumDir() const { string AstVar::vlPropDecl(const string& propName) const { string out; + std::vector plims; // Packed dimension limits std::vector 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]"; diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index 4817bd0f1..f07de3594 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -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 { diff --git a/src/V3CCtors.cpp b/src/V3CCtors.cpp index 045628e6a..30b5d5f69 100644 --- a/src/V3CCtors.cpp +++ b/src/V3CCtors.cpp @@ -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); diff --git a/src/V3Config.cpp b/src/V3Config.cpp index 9b8d38a61..f637cacd3 100644 --- a/src/V3Config.cpp +++ b/src/V3Config.cpp @@ -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> 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; diff --git a/src/V3Config.h b/src/V3Config.h index 32ee079c0..df7287c07 100644 --- a/src/V3Config.h +++ b/src/V3Config.h @@ -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); }; diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 406f4c815..782b7a00b 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -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; } } } diff --git a/src/V3Coverage.cpp b/src/V3Coverage.cpp index afb674551..51881bee0 100644 --- a/src/V3Coverage.cpp +++ b/src/V3Coverage.cpp @@ -28,6 +28,9 @@ #include "V3Coverage.h" +#include "V3EmitV.h" + +#include #include VL_DEFINE_DEBUG_FUNCTIONS; @@ -39,6 +42,18 @@ class CoverageVisitor final : public VNVisitor { // TYPES using LinenoSet = std::set; + 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; + using CoverExprs = std::list; + 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(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, 2> varps; + std::array, 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 + 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::value ? 0x1 << width : width + 1; + if (checkMaxExprs(expected)) return; + AstNodeExpr* unrolledp = new AstSel{fl, lhsp->cloneTree(true), + new AstConst{fl, static_cast(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(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(nodep); } + void visit(AstRedAnd* nodep) override { exprReduce(nodep); } + void visit(AstRedXor* nodep) override { exprReduce(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); diff --git a/src/V3Dead.cpp b/src/V3Dead.cpp index d96a7d54c..fedf41048 100644 --- a/src/V3Dead.cpp +++ b/src/V3Dead.cpp @@ -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 m_varsp; - std::vector m_dtypesp; + std::vector m_dtypeElimsp; // Data types might eliminate + std::map m_dtypePkgsp; // Data type's containing package std::vector m_vscsp; std::vector m_scopesp; std::vector 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::iterator it = m_dtypesp.begin(); it != m_dtypesp.end(); ++it) { + for (std::vector::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 diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index 6b885b1d9..92b383642 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -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 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(); diff --git a/src/V3EmitCBase.cpp b/src/V3EmitCBase.cpp index 547a17884..2cb45c20b 100644 --- a/src/V3EmitCBase.cpp +++ b/src/V3EmitCBase.cpp @@ -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"); } diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index d39c8c4b0..97335c62e 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -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); diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 40c7e5423..00374ae1f 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -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)) works } iterateAndNextConstNull(nodep->condp()); - if (!nodep->branchPred().unknown()) puts(")"); + if (!nodep->branchPred().unknown()) puts("))"); puts(") {\n"); iterateAndNextConstNull(nodep->thensp()); puts("}"); diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index 5abde202e..b4acd5d48 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -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 + 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 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 memberWidth(void) const {\n"); + emitMemberVector(sdtypep); + + putns(sdtypep, "\nstd::vector memberDimension(void) const {\n"); + emitMemberVector(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 "); + 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); diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 926342257..b92b0dcef 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -106,7 +106,8 @@ class EmitCSyms final : EmitCBaseVisitorConst { std::vector m_modVars; // Each public {mod,var} std::map m_scopeFuncs; // Each {scope,dpi-export-func} std::map 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::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, "", 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(), "", - timeunit, "SCOPE_OTHER"}); + m_dpiScopeNames.emplace(name, ScopeData{nodep, name, nodep->scopePrettySymName(), "", + 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(), "", timeunit, - "SCOPE_OTHER"}); + m_dpiScopeNames.emplace(nodep->scopeDpiName(), + ScopeData{nodep, nodep->scopeDpiName(), + nodep->scopePrettyDpiName(), "", 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(static_cast("); 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; diff --git a/src/V3EmitMk.cpp b/src/V3EmitMk.cpp index f87463f34..71d1979a6 100644 --- a/src/V3EmitMk.cpp +++ b/src/V3EmitMk.cpp @@ -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(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(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{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& 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 diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index b5da1b091..a9c75d9f5 100644 --- a/src/V3EmitV.cpp +++ b/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 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(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 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} {} diff --git a/src/V3Error.cpp b/src/V3Error.cpp index 23c70a013..e9ad855cb 100644 --- a/src/V3Error.cpp +++ b/src/V3Error.cpp @@ -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); diff --git a/src/V3Error.h b/src/V3Error.h index c381ddfe7..dd69654f6 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -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", diff --git a/src/V3ExecGraph.cpp b/src/V3ExecGraph.cpp index 5ccc71da5..49c890034 100644 --- a/src/V3ExecGraph.cpp +++ b/src/V3ExecGraph.cpp @@ -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"); + } } } } diff --git a/src/V3FileLine.h b/src/V3FileLine.h index 5b934416a..13bbb8472 100644 --- a/src/V3FileLine.h +++ b/src/V3FileLine.h @@ -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); } diff --git a/src/V3Global.cpp b/src/V3Global.cpp index 08bab7a2d..357f910b0 100644 --- a/src/V3Global.cpp +++ b/src/V3Global.cpp @@ -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); } diff --git a/src/V3LanguageWords.h b/src/V3LanguageWords.h index ba0f302de..c04603efa 100644 --- a/src/V3LanguageWords.h +++ b/src/V3LanguageWords.h @@ -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"); diff --git a/src/V3Life.cpp b/src/V3Life.cpp index 47a7468b8..556fc23c0 100644 --- a/src/V3Life.cpp +++ b/src/V3Life.cpp @@ -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; diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index c747239f4..b1b0b9223 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -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); diff --git a/src/V3LinkInc.cpp b/src/V3LinkInc.cpp index 0dd3fc3b4..a72ff6e1c 100644 --- a/src/V3LinkInc.cpp +++ b/src/V3LinkInc.cpp @@ -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); diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index ebd9c98f1..26017f4e4 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -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 diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index cb268fa1e..9d5630141 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -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}); } } diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 7a7714b1e..0ef5207f7 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -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; } //====================================================================== diff --git a/src/V3Options.h b/src/V3Options.h index eb3d14194..cae2e8cfa 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -219,6 +219,7 @@ private: V3StringSet m_fDfgPeepholeDisabled; // argument: -f[no-]dfg-peephole- 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; } diff --git a/src/V3OrderParallel.cpp b/src/V3OrderParallel.cpp index 67fe62bd3..3c410d1bb 100644 --- a/src/V3OrderParallel.cpp +++ b/src/V3OrderParallel.cpp @@ -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); diff --git a/src/V3Os.cpp b/src/V3Os.cpp index cd38daf44..342c01f85 100644 --- a/src/V3Os.cpp +++ b/src/V3Os.cpp @@ -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()); diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 17ecb785b..5cea41c8b 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -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(); } } diff --git a/src/V3ParseImp.cpp b/src/V3ParseImp.cpp index c2cc2cf6f..4225a9c21 100644 --- a/src/V3ParseImp.cpp +++ b/src/V3ParseImp.cpp @@ -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(); diff --git a/src/V3PreProc.cpp b/src/V3PreProc.cpp index 63894e72a..435fe64c2 100644 --- a/src/V3PreProc.cpp +++ b/src/V3PreProc.cpp @@ -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 diff --git a/src/V3PreProc.h b/src/V3PreProc.h index da6c0d8d8..608447d4b 100644 --- a/src/V3PreProc.h +++ b/src/V3PreProc.h @@ -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. diff --git a/src/V3ProtectLib.cpp b/src/V3ProtectLib.cpp index ec66dba2d..7ca088113 100644 --- a/src/V3ProtectLib.cpp +++ b/src/V3ProtectLib.cpp @@ -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); } diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 96c3f6945..1f1842e05 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -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)}; diff --git a/src/V3Sched.cpp b/src/V3Sched.cpp index 61a7f21d1..a166fcc0f 100644 --- a/src/V3Sched.cpp +++ b/src/V3Sched.cpp @@ -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}; } diff --git a/src/V3SenExprBuilder.h b/src/V3SenExprBuilder.h index f837a960c..0149a2a56 100644 --- a/src/V3SenExprBuilder.h +++ b/src/V3SenExprBuilder.h @@ -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 diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 4e727cd21..c98bfeb53 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -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 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 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> - m_constps; ///< Lists of all AstConst* allocated per dtype + // Allocators for constants of various data types + std::unordered_map m_constps; + size_t m_constGeneration = 0; std::vector 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& 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(); diff --git a/src/V3SplitVar.cpp b/src/V3SplitVar.cpp index 26b2cd208..8d29a90ef 100644 --- a/src/V3SplitVar.cpp +++ b/src/V3SplitVar.cpp @@ -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 splitPlan(bool skipUnused) const { + std::vector splitPlan(const AstVar* varp, bool skipUnused) const { UASSERT(m_dedupDone, "dedup() must be called before"); + AstNodeDType* const dtypep = varp->dtypeSkipRefp(); std::vector plan; std::vector> points; // 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 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 diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 0fe3624d3..eb8716133 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -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(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); + } }; //###################################################################### diff --git a/src/V3Unroll.cpp b/src/V3Unroll.cpp index ac707874b..d36083c5a 100644 --- a/src/V3Unroll.cpp +++ b/src/V3Unroll.cpp @@ -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; diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 969209609..7a47de1a7 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -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(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"); diff --git a/src/V3WidthCommit.cpp b/src/V3WidthCommit.cpp index 5050fda8d..0318294e8 100644 --- a/src/V3WidthCommit.cpp +++ b/src/V3WidthCommit.cpp @@ -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) { diff --git a/src/V3WidthSel.cpp b/src/V3WidthSel.cpp index 7073cf8d4..4bb38eb1f 100644 --- a/src/V3WidthSel.cpp +++ b/src/V3WidthSel.cpp @@ -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}; diff --git a/src/verilog.l b/src/verilog.l index b1a536557..7e96bdf44 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -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, diff --git a/src/verilog.y b/src/verilog.y index 08ac1a969..d41af1d6f 100644 --- a/src/verilog.y +++ b/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 yVLT_D_COST "--cost" %token yVLT_D_FILE "--file" %token yVLT_D_FUNCTION "--function" +%token yVLT_D_HIER_DPI "--hier-dpi" %token yVLT_D_LEVELS "--levels" %token yVLT_D_LINES "--lines" %token yVLT_D_MATCH "--match" @@ -1477,13 +1479,13 @@ portsStarE: // IEEE: .* + list_of_ports + list_of_port_decla ; list_of_portsE: // IEEE: [ list_of_ports + list_of_port_declarations ] - portAndTagE { $$ = $1; } + portAndTagE { $$ = $1; } | list_of_portsE ',' portAndTagE { $$ = addNextNull($1, $3); } ; list_of_ports: // 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: @@ -1582,33 +1584,43 @@ port: // ==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: // ==IEEE: inc_or_dec_expression // // Need fexprScope instead of variable_lvalue to prevent conflict ~l~exprScope yP_PLUSPLUS { $$ = $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 { $$ = $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 { $$ = $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 { $$ = $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: // ==IEEE: inc_or_dec_expression @@ -3947,16 +3963,16 @@ value_range: // ==IEEE: value_range/open_value_range covergroup_value_range: // ==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: // IEEE: part of case_item @@ -6173,13 +6189,13 @@ concurrent_assertion_statement: // ==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: // ==IEEE: covergroup_declaration covergroup_declarationFront: // IEEE: part of covergroup_declaration yCOVERGROUP idAny { $$ = new AstConstraint{$2, *$2, nullptr}; - BBUNSUP($1, "Unsupported: covergroup"); + BBCOVERIGN($1, "Ignoring unsupported: covergroup"); SYMP->pushNew($$); } ; @@ -6686,27 +6702,27 @@ coverage_option: // ==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($1, "Unsupported: coverage option"); } + $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage option"); } ; cover_point: // ==IEEE: cover_point // // [ [ data_type_or_implicit ] cover_point_identifier ':' ] yCOVERPOINT yCOVERPOINT expr iffE bins_or_empty - { $$ = nullptr; BBUNSUP($1, "Unsupported: cover point"); } + { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverpoint"); } // // IEEE-2012: class_scope before an ID | id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty - { $$ = nullptr; BBUNSUP($3, "Unsupported: cover point"); } + { $$ = nullptr; BBCOVERIGN($3, "Ignoring unsupported: coverpoint"); } // // data_type_or_implicit expansion | data_type id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty - { $$ = nullptr; BBUNSUP($4, "Unsupported: cover point"); } + { $$ = nullptr; BBCOVERIGN($4, "Ignoring unsupported: coverpoint"); } | yVAR data_type id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty - { $$ = nullptr; BBUNSUP($5, "Unsupported: cover point"); } + { $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverpoint"); } | yVAR implicit_typeE id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty - { $$ = nullptr; BBUNSUP($5, "Unsupported: cover point"); } + { $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverpoint"); } | signingE rangeList id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty - { $$ = nullptr; BBUNSUP($5, "Unsupported: cover point"); } + { $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverpoint"); } | signing id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty - { $$ = nullptr; BBUNSUP($4, "Unsupported: cover point"); } + { $$ = nullptr; BBCOVERIGN($4, "Ignoring unsupported: coverpoint"); } // // IEEE-2012: | bins_or_empty { $$ = $1; } ; @@ -6714,7 +6730,7 @@ cover_point: // ==IEEE: cover_point iffE: // IEEE: part of cover_point, others /* empty */ { $$ = nullptr; } | yIFF '(' expr ')' - { $$ = nullptr; BBUNSUP($1, "Unsupported: cover 'iff'"); } + { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: cover 'iff'"); } ; bins_or_empty: // ==IEEE: bins_or_empty @@ -6739,24 +6755,24 @@ bins_or_options: // ==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($4, "Unsupported: cover bin specification"); } + { $$ = nullptr; BBCOVERIGN($4, "Ignoring unsupported: cover bin specification"); } | bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__CUR '{' cgexpr '}' iffE - { $$ = nullptr; BBUNSUP($8, "Unsupported: cover bin 'with' specification"); } + { $$ = nullptr; BBCOVERIGN($8, "Ignoring unsupported: cover bin 'with' specification"); } | yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE - { $$ = nullptr; BBUNSUP($5, "Unsupported: cover bin 'wildcard' specification"); } + { $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: cover bin 'wildcard' specification"); } | yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__CUR '{' cgexpr '}' iffE - { $$ = nullptr; BBUNSUP($9, "Unsupported: cover bin 'wildcard' 'with' specification"); } + { $$ = nullptr; BBCOVERIGN($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($4, "Unsupported: cover bin trans list"); } + { $$ = nullptr; BBCOVERIGN($4, "Ignoring unsupported: cover bin trans list"); } | yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE - { $$ = nullptr; BBUNSUP($1, "Unsupported: cover bin 'wildcard' trans list"); } + { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: cover bin 'wildcard' trans list"); } // | bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE - { $$ = nullptr; BBUNSUP($5, "Unsupported: cover bin 'default'"); } + { $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: cover bin 'default'"); } | bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE - { $$ = nullptr; BBUNSUP($6, "Unsupported: cover bin 'default' 'sequence'"); } + { $$ = nullptr; BBCOVERIGN($6, "Ignoring unsupported: cover bin 'default' 'sequence'"); } ; bins_orBraE: // IEEE: part of bins_or_options: @@ -6780,23 +6796,23 @@ trans_set: // ==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($2, "Unsupported: cover trans set '=>'"); } + { $$ = $1; BBCOVERIGN($2, "Ignoring unsupported: cover trans set '=>'"); } ; trans_range_list: // ==IEEE: trans_range_list trans_item { $$ = $1; } | trans_item yP_BRASTAR cgexpr ']' - { $$ = nullptr; BBUNSUP($2, "Unsupported: cover '[*'"); } + { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover '[*'"); } | trans_item yP_BRASTAR cgexpr ':' cgexpr ']' - { $$ = nullptr; BBUNSUP($2, "Unsupported: cover '[*'"); } + { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover '[*'"); } | trans_item yP_BRAMINUSGT cgexpr ']' - { $$ = nullptr; BBUNSUP($2, "Unsupported: cover '[->'"); } + { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover '[->'"); } | trans_item yP_BRAMINUSGT cgexpr ':' cgexpr ']' - { $$ = nullptr; BBUNSUP($2, "Unsupported: cover '[->'"); } + { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover '[->'"); } | trans_item yP_BRAEQ cgexpr ']' - { $$ = nullptr; BBUNSUP($2, "Unsupported: cover '[='"); } + { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover '[='"); } | trans_item yP_BRAEQ cgexpr ':' cgexpr ']' - { $$ = nullptr; BBUNSUP($2, "Unsupported: cover '[='"); } + { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover '[='"); } ; trans_item: // ==IEEE: range_list @@ -6811,9 +6827,9 @@ covergroup_range_list: // ==IEEE: covergroup_range_list cover_cross: // ==IEEE: cover_cross id/*cover_point_identifier*/ ':' yCROSS list_of_cross_items iffE cross_body - { $$ = nullptr; BBUNSUP($3, "Unsupported: cross"); } + { $$ = nullptr; BBCOVERIGN($3, "Ignoring unsupported: cover cross"); } | yCROSS list_of_cross_items iffE cross_body - { $$ = nullptr; BBUNSUP($1, "Unsupported: cross"); } + { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: cover cross"); } ; list_of_cross_items: // ==IEEE: list_of_cross_items @@ -6855,25 +6871,25 @@ cross_body_item: // ==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: // ==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: // ==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: // ==IEEE: bins_expression coverage_eventE: // IEEE: [ coverage_event ] /* empty */ { $$ = nullptr; } | clocking_event - { $$ = nullptr; BBUNSUP($1, "Unsupported: coverage clocking event"); } + { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage clocking event"); } | yWITH__ETC yFUNCTION idAny/*"sample"*/ '(' tf_port_listE ')' - { $$ = nullptr; BBUNSUP($1, "Unsupported: coverage 'with' 'function'"); } + { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage 'with' 'function'"); } | yP_ATAT '(' block_event_expression ')' - { $$ = nullptr; BBUNSUP($1, "Unsupported: coverage '@@' events"); } + { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage '@@' events"); } ; block_event_expression: // ==IEEE: block_event_expression @@ -7348,11 +7364,10 @@ class_item: // ==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($1, *$2, $3->toUQuad()); } | yVLT_PROFILE_DATA vltDModel vltDMtask vltDCost { V3Config::addProfileData($1, *$2, *$3, $4->toUQuad()); } ; @@ -7694,6 +7711,10 @@ vltDFile: // --file yVLT_D_FILE str { $$ = $2; } ; +vltDHierDpi: // --hier-dpi + yVLT_D_HIER_DPI str { $$ = $2; } + ; + vltDLevels: // --levels yVLT_D_LEVELS yaINTNUM { $$ = $2; } ; diff --git a/test_regress/driver.py b/test_regress/driver.py index 86ff9f340..f9864103e 100755 --- a/test_regress/driver.py +++ b/test_regress/driver.py @@ -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'], diff --git a/test_regress/t/TestSimulator.h b/test_regress/t/TestSimulator.h index b99537a78..22fc34cd4 100644 --- a/test_regress/t/TestSimulator.h +++ b/test_regress/t/TestSimulator.h @@ -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"; diff --git a/test_regress/t/TestVpi.h b/test_regress/t/TestVpi.h index 4a87b8e51..d2428ba4a 100644 --- a/test_regress/t/TestVpi.h +++ b/test_regress/t/TestVpi.h @@ -12,6 +12,8 @@ #include "sv_vpi_user.h" #include "vpi_user.h" +#include + // Avoid C++11 in this file as not all simulators allow it //====================================================================== diff --git a/test_regress/t/TestVpiMain.cpp b/test_regress/t/TestVpiMain.cpp index ab368cb24..3d3c4c4b6 100644 --- a/test_regress/t/TestVpiMain.cpp +++ b/test_regress/t/TestVpiMain.cpp @@ -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. diff --git a/test_regress/t/t_EXAMPLE.py b/test_regress/t/t_EXAMPLE.py index d4f986441..f989a35fb 100755 --- a/test_regress/t/t_EXAMPLE.py +++ b/test_regress/t/t_EXAMPLE.py @@ -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. diff --git a/test_regress/t/t_EXAMPLE.v b/test_regress/t/t_EXAMPLE.v index c3eb28a02..b00361778 100644 --- a/test_regress/t/t_EXAMPLE.v +++ b/test_regress/t/t_EXAMPLE.v @@ -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 diff --git a/test_regress/t/t_array_index_side.py b/test_regress/t/t_array_index_side.py new file mode 100755 index 000000000..dbdaf4551 --- /dev/null +++ b/test_regress/t/t_array_index_side.py @@ -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() diff --git a/test_regress/t/t_array_index_side.v b/test_regress/t/t_array_index_side.v new file mode 100644 index 000000000..679edbe70 --- /dev/null +++ b/test_regress/t/t_array_index_side.v @@ -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 diff --git a/test_regress/t/t_class_class.out b/test_regress/t/t_class_class.out deleted file mode 100644 index 2ec79ad39..000000000 --- a/test_regress/t/t_class_class.out +++ /dev/null @@ -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 diff --git a/test_regress/t/t_class_class.py b/test_regress/t/t_class_class.py index 710a094ab..d4f986441 100755 --- a/test_regress/t/t_class_class.py +++ b/test_regress/t/t_class_class.py @@ -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() diff --git a/test_regress/t/t_class_class.v b/test_regress/t/t_class_class.v index bf1ae7da4..14177f768 100644 --- a/test_regress/t/t_class_class.v +++ b/test_regress/t/t_class_class.v @@ -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 diff --git a/test_regress/t/t_class_compare.v b/test_regress/t/t_class_compare.v index bcbb1842a..b20d435cd 100644 --- a/test_regress/t/t_class_compare.v +++ b/test_regress/t/t_class_compare.v @@ -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 diff --git a/test_regress/t/t_class_eq.py b/test_regress/t/t_class_eq.py new file mode 100755 index 000000000..bd059b0f2 --- /dev/null +++ b/test_regress/t/t_class_eq.py @@ -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() diff --git a/test_regress/t/t_class_eq.v b/test_regress/t/t_class_eq.v new file mode 100644 index 000000000..d6738ae57 --- /dev/null +++ b/test_regress/t/t_class_eq.v @@ -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 diff --git a/test_regress/t/t_class_extends1.v b/test_regress/t/t_class_extends1.v index c0f8ceeec..afe7878d9 100644 --- a/test_regress/t/t_class_extends1.v +++ b/test_regress/t/t_class_extends1.v @@ -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; diff --git a/test_regress/t/t_class_extends2.v b/test_regress/t/t_class_extends2.v index 8c30f0c6b..14d50907b 100644 --- a/test_regress/t/t_class_extends2.v +++ b/test_regress/t/t_class_extends2.v @@ -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; diff --git a/test_regress/t/t_class_extends_param.v b/test_regress/t/t_class_extends_param.v index f84fe1fa2..f8544aa80 100644 --- a/test_regress/t/t_class_extends_param.v +++ b/test_regress/t/t_class_extends_param.v @@ -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; diff --git a/test_regress/t/t_class_extends_this.v b/test_regress/t/t_class_extends_this.v index fb185267d..00fd653b9 100644 --- a/test_regress/t/t_class_extends_this.v +++ b/test_regress/t/t_class_extends_this.v @@ -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 diff --git a/test_regress/t/t_class_extends_this3.v b/test_regress/t/t_class_extends_this3.v index 20a66e469..908276e58 100644 --- a/test_regress/t/t_class_extends_this3.v +++ b/test_regress/t/t_class_extends_this3.v @@ -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 diff --git a/test_regress/t/t_class_extern.v b/test_regress/t/t_class_extern.v index 0cddfdc3f..3afa91045 100644 --- a/test_regress/t/t_class_extern.v +++ b/test_regress/t/t_class_extern.v @@ -13,6 +13,16 @@ class Cls; extern task ext_t_np; extern task ext_t_p(); extern task ext_t_i(int in); + class SubCls; + int value; + extern function int ext_f_np; + extern function int ext_f_p(); + extern function int ext_f_i(int in); + extern static function int get_10(); + extern task ext_t_np; + extern task ext_t_p(); + extern task ext_t_i(int in); + endclass endclass function int Cls::ext_f_np; @@ -42,15 +52,51 @@ task Cls::ext_t_i(int in); value = in; endtask +function int Cls::SubCls::ext_f_np; + return 10; +endfunction + +function int Cls::SubCls::ext_f_p(); + return value; +endfunction + +function int Cls::SubCls::ext_f_i(int in); + return in+10; +endfunction + +function int Cls::SubCls::get_10(); + return 10; +endfunction + +task Cls::SubCls::ext_t_np(); + $write("Cls::SubCls::ext_t_np\n"); +endtask +task Cls::SubCls::ext_t_p(); + $write("Cls::SubCls::ext_t_p\n"); +endtask +task Cls::SubCls::ext_t_i(int in); + if (in != 20) $stop; + value = in; +endtask + + module t (/*AUTOARG*/); initial begin Cls c = new; + Cls::SubCls subc = new; c.ext_t_i(2); - c.ext_t_np(); - c.ext_t_p(); if (c.ext_f_np() != 1) $stop; if (c.ext_f_p() != 2) $stop; if (c.ext_f_i(10) != 11) $stop; if (Cls::get_1() != 1) $stop; + subc.ext_t_i(20); + if (subc.ext_f_np() != 10) $stop; + if (subc.ext_f_p() != 20) $stop; + if (subc.ext_f_i(11) != 21) $stop; + if (Cls::SubCls::get_10() != 10) $stop; + subc.ext_t_np(); + subc.ext_t_p(); + c.ext_t_np(); + c.ext_t_p(); end endmodule diff --git a/test_regress/t/t_class_format.out b/test_regress/t/t_class_format.out index dd9e17d2a..4fef2ba25 100644 --- a/test_regress/t/t_class_format.out +++ b/test_regress/t/t_class_format.out @@ -1,4 +1,4 @@ -''{b:'h1, i:'h2a, carray4:'{'h11, 'h22, 'h33, 'h44} , cwide:'{'h0, 'h0} , name:"object_name"}' -''{b:'h1, i:'h2a, carray4:'{'h911, 'h922, 'h933, 'h944} , cwide:'{'h0, 'h0} , name:"object_name"}' +''{b:'h1, i:'h2a, carray4:'{'h11, 'h22, 'h33, 'h44} , cwide:'{'h0, 'h0} , name:"object_name", r:2.2}' +''{b:'h1, i:'h2a, carray4:'{'h911, 'h922, 'h933, 'h944} , cwide:'{'h0, 'h0} , name:"object_name", r:2.2}' DEBUG: object_name (@0) message *-* All Finished *-* diff --git a/test_regress/t/t_class_format.v b/test_regress/t/t_class_format.v index ac11b68c0..8075e5bc0 100644 --- a/test_regress/t/t_class_format.v +++ b/test_regress/t/t_class_format.v @@ -17,6 +17,7 @@ class Cls; bit [15:0] carray4 [4]; bit [64:0] cwide[2]; string name; + real r; task debug(); $display("DEBUG: %s (@%0t) %s", this.name, $realtime, "message"); endtask @@ -28,6 +29,7 @@ module t (/*AUTOARG*/); c = new; c.b = '1; c.i = 42; + c.r = 2.2; c.name = "object_name"; c.carray4[0] = 16'h11; diff --git a/test_regress/t/t_class_func_nvoid_bad.out b/test_regress/t/t_class_func_nvoid_bad.out new file mode 100644 index 000000000..52690482f --- /dev/null +++ b/test_regress/t/t_class_func_nvoid_bad.out @@ -0,0 +1,25 @@ +%Error: t/t_class_func_nvoid_bad.v:47:11: Cannot call a task/void-function as a function: 'mod_fv' + : ... note: In instance 't' + 47 | if (mod_fv() == 10) $stop; + | ^~~~~~ +%Error: t/t_class_func_nvoid_bad.v:50:11: Cannot call a task/void-function as a function: 'mod_t' + : ... note: In instance 't' + 50 | if (mod_t() == 10) $stop; + | ^~~~~ +%Error: t/t_class_func_nvoid_bad.v:58:13: Cannot call a task/void-function as a member function: 'fv' + : ... note: In instance 't' + 58 | if (c.fv() == 10) $stop; + | ^~ +%Error: t/t_class_func_nvoid_bad.v:61:13: Cannot call a task/void-function as a member function: 't' + : ... note: In instance 't' + 61 | if (c.t() == 10) $stop; + | ^ +%Error: t/t_class_func_nvoid_bad.v:69:13: Cannot call a task/void-function as a function: 'sfv' + : ... note: In instance 't' + 69 | if (c.sfv() == 10) $stop; + | ^~~ +%Error: t/t_class_func_nvoid_bad.v:72:13: Cannot call a task/void-function as a function: 'st' + : ... note: In instance 't' + 72 | if (c.st() == 10) $stop; + | ^~ +%Error: Exiting due to diff --git a/test_regress/t/t_class_func_nvoid_bad.py b/test_regress/t/t_class_func_nvoid_bad.py new file mode 100755 index 000000000..31228c9a7 --- /dev/null +++ b/test_regress/t/t_class_func_nvoid_bad.py @@ -0,0 +1,16 @@ +#!/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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_class_func_nvoid_bad.v b/test_regress/t/t_class_func_nvoid_bad.v new file mode 100644 index 000000000..f8180e69b --- /dev/null +++ b/test_regress/t/t_class_func_nvoid_bad.v @@ -0,0 +1,76 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2020 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + + +class Cls; + function int fi(); + return 10; + endfunction + function void fv(); + endfunction + task t(); + endtask + static function int sfi(); + return 10; + endfunction + static function void sfv(); + endfunction + static task st(); + endtask +endclass + +module t (/*AUTOARG*/); + + function int mod_fi(); + return 10; + endfunction + function void mod_fv(); + endfunction + task mod_t(); + endtask + + initial begin + Cls c; + c = new; + + // For test of calling function in void context, see t_func_void_bad.v + + // Module + if (mod_fi() != 10) $stop; // OK + void'(mod_fi()); // OK + + mod_fv(); // Warn IGNOREDRETURN + void'(mod_fv()); // OK + if (mod_fv() == 10) $stop; // Bad call of task as function + + mod_t(); // OK + if (mod_t() == 10) $stop; // Bad call of task as function + + // Member functions + if (c.fi() != 10) $stop; // OK + void'(c.fi()); // OK + + c.fv(); // Ok + void'(c.fv()); // OK + if (c.fv() == 10) $stop; // Bad + + c.t(); // OK + if (c.t() == 10) $stop; // Bad + + // Static member functions + if (c.sfi() != 10) $stop; // OK + void'(c.sfi()); // OK + + c.sfv(); // Ok + void'(c.sfv()); // OK + if (c.sfv() == 10) $stop; // Bad + + c.st(); // OK + if (c.st() == 10) $stop; // Bad + + $stop; + end +endmodule diff --git a/test_regress/t/t_class_local.v b/test_regress/t/t_class_local.v index 0a5b6cf78..db9d9e975 100644 --- a/test_regress/t/t_class_local.v +++ b/test_regress/t/t_class_local.v @@ -31,6 +31,34 @@ class Cls; Cls::s_loc(); // Ok Cls::s_prot(); // Ok endtask + class InnerCls; + typedef enum {A = 10, B = 20, C = 30} en_t; + + int m_pub = 1; + local int m_loc = 2; + protected int m_prot = B; + task f_pub; endtask + local task f_loc; endtask + protected task f_prot; endtask + static task s_pub; endtask + static local task s_loc; endtask + static protected task s_prot; endtask + task check; + Cls o; + if (m_pub != 1) $stop; + if (m_loc != 2) $stop; + if (m_prot != 20) $stop; + f_pub(); // Ok + f_loc(); // Ok + f_prot(); // Ok + s_pub(); // Ok + s_loc(); // Ok + s_prot(); // Ok + Cls::s_pub(); // Ok + Cls::s_loc(); // Ok + Cls::s_prot(); // Ok + endtask + endclass endclass class Ext extends Cls; @@ -44,27 +72,52 @@ class Ext extends Cls; Cls::s_pub(); // Ok Cls::s_prot(); // Ok endtask + class ExtInner extends Cls::InnerCls; + task check; + if (m_pub != 1) $stop; + if (m_prot != 20) $stop; + f_pub(); // Ok + f_prot(); // Ok + s_pub(); // Ok + s_prot(); // Ok + Cls::InnerCls::s_pub(); // Ok + Cls::InnerCls::s_prot(); // Ok + endtask + endclass endclass module t (/*AUTOARG*/); const Cls mod_c = new; + const Cls::InnerCls imod_c = new; initial begin Cls c; + Cls::InnerCls i; Ext e; + Ext::ExtInner ei; if (c.A != 10) $stop; + if (i.A != 10) $stop; c = new; + i = new; e = new; + ei = new; if (c.m_pub != 1) $stop; + if (i.m_pub != 1) $stop; // if (mod_c.A != 10) $stop; + if (imod_c.A != 10) $stop; // c.check(); + i.check(); e.check(); + ei.check(); // Cls::s_pub(); // Ok + Cls::InnerCls::s_pub(); // Ok c.s_pub(); // Ok + i.s_pub(); // Ok e.s_pub(); // Ok + ei.s_pub(); // Ok // $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_class_local_nested_bad.out b/test_regress/t/t_class_local_nested_bad.out new file mode 100644 index 000000000..8f4b39314 --- /dev/null +++ b/test_regress/t/t_class_local_nested_bad.out @@ -0,0 +1,9 @@ +%Error-ENCAPSULATED: t/t_class_local_nested_bad.v:14:22: 'name' is hidden as 'local' within this context (IEEE 1800-2023 8.18) + : ... note: In instance 't' + 14 | name = Node::name; + | ^~~~ + t/t_class_local_nested_bad.v:14:22: ... Location of definition + 9 | static local string name; + | ^~~~ + ... For error description see https://verilator.org/warn/ENCAPSULATED?v=latest +%Error: Exiting due to diff --git a/test_regress/t/t_class_local_nested_bad.py b/test_regress/t/t_class_local_nested_bad.py new file mode 100755 index 000000000..710a094ab --- /dev/null +++ b/test_regress/t/t_class_local_nested_bad.py @@ -0,0 +1,16 @@ +#!/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') + +test.compile(fails=test.vlt_all, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_class_local_nested_bad.v b/test_regress/t/t_class_local_nested_bad.v new file mode 100644 index 000000000..f5e7b1155 --- /dev/null +++ b/test_regress/t/t_class_local_nested_bad.v @@ -0,0 +1,25 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class NodeList; + class Node; + static local string name; + endclass + + string name; + function new(); + name = Node::name; + endfunction +endclass + +module t(/*AUTOARG*/); + initial begin + NodeList n = new; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_class_module.v b/test_regress/t/t_class_module.v index 60f043d9c..483026de2 100644 --- a/test_regress/t/t_class_module.v +++ b/test_regress/t/t_class_module.v @@ -7,8 +7,13 @@ module t (/*AUTOARG*/); class Cls; + class Inner; + int imemberinnera; + int imemberinnerb; + endclass int imembera; int imemberb; + Inner innermemberc; endclass : Cls class Dead; @@ -18,10 +23,16 @@ endclass Cls c; if (c != null) $stop; c = new; + if (c.innermemberc != null) $stop; + c.innermemberc = new; c.imembera = 10; c.imemberb = 20; + c.innermemberc.imemberinnera = 30; + c.innermemberc.imemberinnerb = 40; if (c.imembera != 10) $stop; if (c.imemberb != 20) $stop; + if (c.innermemberc.imemberinnera != 30) $stop; + if (c.innermemberc.imemberinnerb != 40) $stop; $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_class_nested.py b/test_regress/t/t_class_nested.py new file mode 100755 index 000000000..49d728ea8 --- /dev/null +++ b/test_regress/t/t_class_nested.py @@ -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') + +test.compile(verilator_flags2=["--exe --main"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_class_nested.v b/test_regress/t/t_class_nested.v new file mode 100644 index 000000000..4a9e2e74d --- /dev/null +++ b/test_regress/t/t_class_nested.v @@ -0,0 +1,90 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class NodeList; + class Node; + string name; + Node link; + + function new(); + name = "node"; + endfunction + endclass + + Node head; +endclass + +class NodeTree; + class Node; + int id; + Node link; + endclass + + Node root; +endclass + +// Based on IEEE 1800-2017 section 8.23 Nested classes +class Outer; + int outerProp; + local int outerLocalProp; + static int outerStaticProp; + static local int outerLocalStaticProp; + + class Inner; + function void innerMethod(Outer h); + outerStaticProp = 1; + outerLocalStaticProp = 1; + h.outerProp = 1; + h.outerLocalProp = 1; + endfunction + endclass +endclass + + +module t(/*AUTOARG*/); + initial begin + NodeList n = new; + NodeList::Node n1 = new; + NodeList::Node n2 = new; + NodeTree tr = new; + NodeTree::Node t1 = new; + NodeTree::Node t2 = new; + Outer o = new; + Outer::Inner i = new; + + i.innerMethod(o); + + if(o.outerProp != 1) $stop; + if(Outer::outerStaticProp != 1) $stop; + + if (n1.name != "node") $stop; + + n1.name = "n1"; + if (n1.name != "n1") $stop; + + n2.name = "n2"; + if (n2.name != "n2") $stop; + + n.head = n1; + n1.link = n2; + if (n.head.name != "n1") $stop; + if (n.head.link.name != "n2") $stop; + + t1.id = 1; + if (t1.id != 1) $stop; + + t2.id = 2; + if (t2.id != 2) $stop; + + tr.root = t1; + t1.link = t2; + if (tr.root.id != 1) $stop; + if (tr.root.link.id != 2) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_class_new.v b/test_regress/t/t_class_new.v index 9d625c5a5..8f4ba17d5 100644 --- a/test_regress/t/t_class_new.v +++ b/test_regress/t/t_class_new.v @@ -11,6 +11,17 @@ class ClsNoArg; imembera = 5; if (other != 6) $stop; endfunction : new + class InnerNoArg; + const int imembera; + function new(); + int other = other_func(); + imembera = 5; + if (other != 6) $stop; + endfunction + function int other_func(); + return 6; + endfunction + endclass function int other_func(); return 6; endfunction @@ -52,6 +63,7 @@ module t (/*AUTOARG*/); ClsArg c2; Cls2Arg c3; Cls2Arg c4; + ClsNoArg::InnerNoArg c5 = new; c1 = new; if (c1.imembera != 5) $stop; @@ -72,6 +84,9 @@ module t (/*AUTOARG*/); if (c4.imembera != 6) $stop; if (c4.imemberb != 9) $stop; + c5 = new; + if (c5.imembera != 5) $stop; + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_class_param.v b/test_regress/t/t_class_param.v index f2510ed1d..62ed7508a 100644 --- a/test_regress/t/t_class_param.v +++ b/test_regress/t/t_class_param.v @@ -42,6 +42,9 @@ class Cls #(parameter PBASE = 12); return PBASE; endfunction typedef enum { E_PBASE = PBASE } enum_t; + class ClsInner; + bit [PBASE-1:0] member; + endclass endclass typedef Cls#(8) Cls8_t; @@ -136,6 +139,8 @@ module t (/*AUTOARG*/); Cls c12; Cls #(.PBASE(4)) c4; Cls8_t c8; + Cls#()::ClsInner ci; + Cls#(8)::ClsInner ci8; Wrap #(.P(16)) w16; Wrap2 #(.P(32)) w32; SelfRefClassTypeParam src_logic; @@ -155,6 +160,8 @@ module t (/*AUTOARG*/); c12 = new; c4 = new; c8 = new; + ci = new; + ci8 = new; w16 = new; w32 = new; src_int = new; @@ -195,6 +202,10 @@ module t (/*AUTOARG*/); c4.member = 32'haaaaaaaa; c8.member = 32'haaaaaaaa; // verilator lint_on WIDTH + ci.member = 12'haaa; + ci8.member = 8'hff; + if (ci.member != 12'haaa) $stop; + if (ci8.member != 8'hff) $stop; if (c12.member != 12'haaa) $stop; if (c4.member != 4'ha) $stop; if (c12.get_member() != 12'haaa) $stop; diff --git a/test_regress/t/t_class_param_rewrite.v b/test_regress/t/t_class_param_rewrite.v index 17664ec71..b88019127 100644 --- a/test_regress/t/t_class_param_rewrite.v +++ b/test_regress/t/t_class_param_rewrite.v @@ -14,10 +14,18 @@ module test; if (enum_item.first().name() != "BAR_0") $stop; endfunction + class Inner1; + static function void print(); + E enum_item; + if (enum_item.first().name() != "BAR_0") + $stop; + endfunction + endclass endclass initial begin baz#(bar_e)::print(); + baz#(bar_e)::Inner1::print(); $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_class_scope_import.out b/test_regress/t/t_class_scope_import.out new file mode 100644 index 000000000..4c1811e42 --- /dev/null +++ b/test_regress/t/t_class_scope_import.out @@ -0,0 +1,4 @@ +%Error: t/t_class_scope_import.v:11:14: Import statement directly within a class scope is illegal + 11 | import pkg::*; + | ^~ +%Error: Exiting due to diff --git a/test_regress/t/t_class_scope_import.py b/test_regress/t/t_class_scope_import.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_class_scope_import.py @@ -0,0 +1,16 @@ +#!/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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_class_scope_import.v b/test_regress/t/t_class_scope_import.v new file mode 100644 index 000000000..0306ffa6a --- /dev/null +++ b/test_regress/t/t_class_scope_import.v @@ -0,0 +1,15 @@ +// 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 + +package pkg; +endpackage + +class genericClass; + import pkg::*; +endclass + +module tb_top(); +endmodule diff --git a/test_regress/t/t_class_static_member.v b/test_regress/t/t_class_static_member.v index 1867b73b0..f6a0be448 100644 --- a/test_regress/t/t_class_static_member.v +++ b/test_regress/t/t_class_static_member.v @@ -8,6 +8,12 @@ `define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); class Cls; + class InnerCls; + static function int f_inner_cs_st(); + ++c_st; return c_st; + endfunction + endclass + int c_no = 2; //automatic int c_au = 2; // automatic not a legal keyword here static int c_st = 22; @@ -22,7 +28,6 @@ class Cls; static function int f_cs_st (); ++c_st; return c_st; endfunction - endclass module t (/*AUTOARG*/); @@ -43,6 +48,7 @@ module t (/*AUTOARG*/); v = b.f_c_st(); `checkh(v, 26); // v = Cls::f_cs_st(); `checkh(v, 27); + v = Cls::InnerCls::f_inner_cs_st(); `checkh(v, 28); $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_class_virtual.v b/test_regress/t/t_class_virtual.v index 5b2e9511d..8c0b80920 100644 --- a/test_regress/t/t_class_virtual.v +++ b/test_regress/t/t_class_virtual.v @@ -8,18 +8,33 @@ virtual class VBase; virtual function int hello; return 1; endfunction + virtual class VNested; + virtual function int hello; + return 10; + endfunction + endclass endclass class VA extends VBase; virtual function int hello; return 2; endfunction + class VNested extends VBase::VNested; + virtual function int hello; + return 20; + endfunction + endclass endclass class VB extends VBase; virtual function int hello; return 3; endfunction + class VNested extends VBase::VNested; + virtual function int hello; + return 30; + endfunction + endclass endclass virtual class uvm_phase; @@ -59,18 +74,27 @@ module t; initial begin VA va = new; VB vb = new; + VA::VNested vna = new; + VB::VNested vnb = new; VBase b; + VBase::VNested bn; uvm_build_phase ph; ExtendsCls ec; if (va.hello() != 2) $stop; if (vb.hello() != 3) $stop; + if (vna.hello() != 20) $stop; + if (vnb.hello() != 30) $stop; b = va; + bn = vna; if (b.hello() != 2) $stop; + if (bn.hello() != 20) $stop; b = vb; + bn = vnb; if (b.hello() != 3) $stop; + if (bn.hello() != 30) $stop; ph = new; if (ph.get1() != 1) $stop; diff --git a/test_regress/t/t_constraint_assoc_arr_bad.out b/test_regress/t/t_constraint_assoc_arr_bad.out index a048591fc..167603034 100644 --- a/test_regress/t/t_constraint_assoc_arr_bad.out +++ b/test_regress/t/t_constraint_assoc_arr_bad.out @@ -1,9 +1,15 @@ -%Warning-CONSTRAINTIGN: t/t_constraint_assoc_arr_bad.v:14:22: Unsupported: Associative array index widths of more than 64 bits during constraint randomization. - 14 | bit_index_arr[79'd66] == 65; - | ^ +%Warning-CONSTRAINTIGN: t/t_constraint_assoc_arr_bad.v:12:20: Unsupported: Constrained randomization of associative array keys of 144bits, limit is 128 bits + 12 | string_arr["a_very_long_string"] == 65; + | ^~~~~~~~~~~~~~~~~~~~ ... For warning description see https://verilator.org/warn/CONSTRAINTIGN?v=latest ... Use "/* verilator lint_off CONSTRAINTIGN */" and lint_on around source to disable this message. -%Warning-CONSTRAINTIGN: t/t_constraint_assoc_arr_bad.v:15:24: Unsupported: Associative array index widths of more than 64 bits during constraint randomization. - 15 | logic_index_arr[65'd3] == 70; - | ^ +%Error: t/t_constraint_assoc_arr_bad.v:30:26: Illegal non-integral expression or subexpression in random constraint. (IEEE 1800-2023 18.3) + 30 | constraint c1 { data[cl] > 0;} + | ^~ +%Error: t/t_constraint_assoc_arr_bad.v:44:44: Illegal non-integral expression or subexpression in random constraint. (IEEE 1800-2023 18.3) + 44 | constraint c2 { foreach (data[i]) data[i] < 100; } + | ^ +%Error: t/t_constraint_assoc_arr_bad.v:58:44: Illegal non-integral expression or subexpression in random constraint. (IEEE 1800-2023 18.3) + 58 | constraint c3 { foreach (data[i]) data[i] > 0; } + | ^ %Error: Exiting due to diff --git a/test_regress/t/t_constraint_assoc_arr_bad.v b/test_regress/t/t_constraint_assoc_arr_bad.v old mode 100644 new mode 100755 index 2050f2643..644b19f30 --- a/test_regress/t/t_constraint_assoc_arr_bad.v +++ b/test_regress/t/t_constraint_assoc_arr_bad.v @@ -4,34 +4,89 @@ // any use, without warranty, 2024 by PlanV GmbH. // SPDX-License-Identifier: CC0-1.0 - -class AssocArrayWarningTest; - - rand int bit_index_arr [bit[78:0]]; - rand int logic_index_arr [logic[64:0]]; +// Long String index associative array +class AssocArrayString; + rand int string_arr [string]; constraint c { - bit_index_arr[79'd66] == 65; - logic_index_arr[65'd3] == 70; + string_arr["a_very_long_string"] == 65; } function new(); - bit_index_arr = '{79'd66:0}; - logic_index_arr = '{65'd3:0}; + string_arr["a_very_long_string"] = 0; endfunction +endclass +class keyClass; + int id; + function new(); + id = 3; + endfunction +endclass +// Class index associative array +class AssocArrayClass; + rand bit [31:0] data [keyClass]; + keyClass cl; + // constraint c4 { foreach (data[i]) data[i] > 0;} Unsupported index type for an associative array in an iterative constraint. + constraint c1 { data[cl] > 0;} // Illegal index expression of unpacked type in constraint. + function new(); + cl = new(); + data[cl] = 32'd77; + endfunction +endclass + +typedef struct { + int a; + int b; +} UnpackedIndexType; +// Struct (unpacked) index associative array +class AssocArrayUnpackedStruct; + rand bit [31:0] data [UnpackedIndexType]; + constraint c2 { foreach (data[i]) data[i] < 100; } // Illegal non-integral expression in random constraint. + + function new(); + UnpackedIndexType idx; + idx.a = 1; + idx.b = 2; + data[idx] = 32'd25; + endfunction +endclass + +// Array (unpacked) index associative array +typedef logic [2:0] IndexArrayType[3]; +class AssocArrayArrayIndex; + rand bit [31:0] data [IndexArrayType]; + constraint c3 { foreach (data[i]) data[i] > 0; } + + function new(); + IndexArrayType idx; + for (int j = 0; j < 4; j++) begin + idx[j] = 3'd0; + end + data[idx] = 32'd75; + endfunction endclass module t_constraint_assoc_arr_bad; - AssocArrayWarningTest test_obj; + AssocArrayString test_str; + AssocArrayClass test_cls; + AssocArrayUnpackedStruct test_unp_struct; + AssocArrayArrayIndex test_unp_arr; + int success = 0; initial begin - test_obj = new(); - repeat(2) begin - int success; - success = test_obj.randomize(); - if (success != 1) $stop; - end + test_str = new(); + test_cls = new(); + test_unp_struct = new(); + test_unp_arr = new(); + + success += test_str.randomize(); + success += test_cls.randomize(); + success += test_unp_struct.randomize(); + success += test_unp_arr.randomize(); + + if(success != 4) $stop; + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_constraint_assoc_arr_basic.v b/test_regress/t/t_constraint_assoc_arr_basic.v index 105cdb96a..1cd43562d 100644 --- a/test_regress/t/t_constraint_assoc_arr_basic.v +++ b/test_regress/t/t_constraint_assoc_arr_basic.v @@ -147,11 +147,33 @@ class constrained_2d_associative_array; /* verilator lint_off SIDEEFFECT */ endclass +class AssocArray64bitMoreTest; + + rand int bit_index_arr [bit[78:0]]; + rand int logic_index_arr [logic[64:0]]; + + constraint c { + bit_index_arr[79'd66] == 65; + logic_index_arr[65'd3] == 70; + } + function new(); + bit_index_arr = '{79'd66:0}; + logic_index_arr = '{65'd3:0}; + endfunction + function void self_check(); + if (bit_index_arr[79'd66] != 65) $stop; + if (logic_index_arr[65'd3] != 70) $stop; + endfunction + +endclass + module t_constraint_assoc_arr_basic; constrained_associative_array_basic my_array; constrained_1d_associative_array my_1d_array; constrained_2d_associative_array my_2d_array; + + AssocArray64bitMoreTest test_obj; int success; initial begin @@ -165,10 +187,16 @@ module t_constraint_assoc_arr_basic; if (success == 0) $stop; my_1d_array.self_check(); - my_1d_array = new(); - success = my_1d_array.randomize(); + my_2d_array = new(); + success = my_2d_array.randomize(); if (success == 0) $stop; - my_1d_array.self_check(); + my_2d_array.self_check(); + + test_obj = new(); + repeat(2) begin + success = test_obj.randomize(); + if (success != 1) $stop; + end // my_1d_array.debug_display(); // my_2d_array.debug_display(); diff --git a/test_regress/t/t_constraint_assoc_arr_others.py b/test_regress/t/t_constraint_assoc_arr_others.py new file mode 100755 index 000000000..a2b131082 --- /dev/null +++ b/test_regress/t/t_constraint_assoc_arr_others.py @@ -0,0 +1,21 @@ +#!/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') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_constraint_assoc_arr_others.v b/test_regress/t/t_constraint_assoc_arr_others.v new file mode 100755 index 000000000..badbc3387 --- /dev/null +++ b/test_regress/t/t_constraint_assoc_arr_others.v @@ -0,0 +1,100 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by PlanV GmbH. +// SPDX-License-Identifier: CC0-1.0 + +// Enum-based associative array +typedef enum { RED, GREEN, YELLOW } color_t; + +class AssocArrayEnum; + rand bit [7:0] colors [color_t]; + constraint c1 { foreach (colors[i]) colors[i] == 4; } + + function new(); + colors[RED] = 8'd5; + colors[GREEN] = 8'd10; + colors[YELLOW] = 8'd15; + endfunction + + function void self_check(); + foreach (colors[i]) begin + if (colors[i] != 4) $stop; + end + endfunction +endclass + +// Struct (packed) index associative array +typedef struct packed { + bit [2:0] high; + bit [1:0] low; +} PackedIndexType; + +class AssocArrayPackedStruct; + rand bit [31:0] data [PackedIndexType]; + constraint c2 { foreach (data[i]) data[i] == 100; } + + function new(); + PackedIndexType idx; + idx.high = 3'd1; + idx.low = 2'd1; + data[idx] = 32'd50; + endfunction + + function void self_check(); + foreach (data[i]) begin + if (data[i] != 100) $stop; + end + endfunction +endclass + +// Array (packed) index associative array +typedef logic [2:0][7:0] IndexArrayType; + +class AssocArrayArrayIndex; + rand bit [31:0] data [IndexArrayType]; + constraint c3 { foreach (data[i]) data[i] == 0; } + + function new(); + IndexArrayType idx; + idx = 0; + data[idx] = 32'd75; + endfunction + + function void self_check(); + foreach (data[i]) begin + if (data[i] != 0) $stop; + end + endfunction +endclass + +module t_constraint_assoc_array_others; + + AssocArrayEnum enum_arr; + AssocArrayPackedStruct packed_arr; + AssocArrayArrayIndex array_arr; + int success = 0; + + initial begin + // Create instances of the classes + enum_arr = new(); + packed_arr = new(); + array_arr = new(); + + // Randomization and self-check + success = enum_arr.randomize(); + if (success != 1) $stop; + enum_arr.self_check(); + + success = packed_arr.randomize(); + if (success != 1) $stop; + packed_arr.self_check(); + + success = array_arr.randomize(); + if (success != 1) $stop; + array_arr.self_check(); + + $write("*-* All Finished *-*"); + $finish; + end +endmodule diff --git a/test_regress/t/t_constraint_assoc_arr_wide.py b/test_regress/t/t_constraint_assoc_arr_wide.py new file mode 100755 index 000000000..a2b131082 --- /dev/null +++ b/test_regress/t/t_constraint_assoc_arr_wide.py @@ -0,0 +1,21 @@ +#!/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') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_constraint_assoc_arr_wide.v b/test_regress/t/t_constraint_assoc_arr_wide.v new file mode 100755 index 000000000..52dca3fc2 --- /dev/null +++ b/test_regress/t/t_constraint_assoc_arr_wide.v @@ -0,0 +1,115 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by PlanV GmbH. +// SPDX-License-Identifier: CC0-1.0 + +class AssocIntegralWide; + + rand bit [31:0] assoc_array[bit[64:0]]; + rand bit [31:0] assoc_array_128[bit[128:0]]; + rand bit [31:0] assoc_array_2d[bit[64:0]][bit[128:0]]; + + constraint valid_entries { + assoc_array[65'd6] == 32'd8; + assoc_array[65'h1FFFFFFFFFFFFFFFF] == 32'hDEADBEEF; + + assoc_array_128[129'd6] == 32'd16; + assoc_array_128[129'h1FFFFFFFFFFFFFFFFFFFFFFFF] == 32'hCAFEBABE; + + assoc_array_2d[65'd6][129'd6] == 32'd32; + assoc_array_2d[65'h1FFFFFFFFFFFFFFFF][129'h1FFFFFFFFFFFFFFFFFFFFFFFF] == 32'hBADF00D; + } + + // Constructor to initialize arrays + function new(); + assoc_array[65'd0] = 32'd0; + assoc_array[65'd6] = 32'd0; + assoc_array[65'h1FFFFFFFFFFFFFFFF] = 32'd0; + + assoc_array_128[129'd0] = 32'd0; + assoc_array_128[129'd6] = 32'd0; + assoc_array_128[129'h1FFFFFFFFFFFFFFFFFFFFFFFF] = 32'd0; + + assoc_array_2d[65'd6][129'd6] = 32'd0; + assoc_array_2d[65'h1FFFFFFFFFFFFFFFF][129'h1FFFFFFFFFFFFFFFFFFFFFFFF] = 32'd0; + endfunction + + // Self-check function to verify constraints + function void self_check(); + if (assoc_array[65'd6] != 32'd8) + $stop; + if (assoc_array[65'h1FFFFFFFFFFFFFFFF] != 32'hDEADBEEF) + $stop; + + if (assoc_array_128[129'd6] != 32'd16) + $stop; + if (assoc_array_128[129'h1FFFFFFFFFFFFFFFFFFFFFFFF] != 32'hCAFEBABE) + $stop; + + if (assoc_array_2d[65'd6][129'd6] != 32'd32) + $stop; + if (assoc_array_2d[65'h1FFFFFFFFFFFFFFFF][129'h1FFFFFFFFFFFFFFFFFFFFFFFF] != 32'hBADF00D) + $stop; + endfunction + +endclass + +class AssocStringWide; + + rand bit [31:0] array_32[string]; + rand bit [31:0] array_64[string]; + rand bit [31:0] array_96[string]; + rand bit [31:0] array_128[string]; + + constraint valid_entries { + // <= 32 bits + array_32["pv"] == 32'd10; + // > 32 and <= 64 bits + array_64["verilog"] == 32'd20; + // > 32 and <= 64 bits + array_96["verilator"] == 32'd30; + // > 64 and <= 96 bits + array_128["systemverilog"] == 32'd40; + } + + function new(); + array_32["pv"] = 32'd0; + array_64["verilog"] = 32'd0; + array_96["verilator"] = 32'd0; + array_128["systemverilog"] = 32'd0; + endfunction + + function void self_check(); + if (array_32["pv"] != 32'd10) $stop; + if (array_64["verilog"] != 32'd20) $stop; + if (array_96["verilator"] != 32'd30) $stop; + if (array_128["systemverilog"] != 32'd40) $stop; + endfunction + +endclass + + +module t_constraint_assoc_arr_wide; + + AssocIntegralWide integral_wide; + AssocStringWide string_wide; + + int success; + initial begin + + integral_wide = new(); + string_wide = new(); + + success = integral_wide.randomize(); + if (success != 1) $stop; + integral_wide.self_check(); + + success = string_wide.randomize(); + if (success != 1) $stop; + string_wide.self_check(); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_constraint_struct.py b/test_regress/t/t_constraint_struct.py new file mode 100755 index 000000000..a2b131082 --- /dev/null +++ b/test_regress/t/t_constraint_struct.py @@ -0,0 +1,21 @@ +#!/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') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_constraint_struct.v b/test_regress/t/t_constraint_struct.v new file mode 100755 index 000000000..4462017bb --- /dev/null +++ b/test_regress/t/t_constraint_struct.v @@ -0,0 +1,87 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by PlanV GmbH. +// SPDX-License-Identifier: CC0-1.0 + +typedef struct packed { + bit [7:0] byte_value; + int int_value; +} PackedStruct; + +typedef struct { + rand bit [7:0] byte_value; + rand int int_value; + int non_rand_value; // Non-randomized member +} UnpackedStruct; + +class PackedStructTest; + rand PackedStruct packed_struct; + + function new(); + packed_struct.byte_value = 8'hA0; + packed_struct.int_value = 0; + endfunction + + // Constraint block for packed struct + constraint packed_struct_constraint { + packed_struct.byte_value == 8'hA0; + packed_struct.int_value inside {[0:100]}; + } + + // Self-check function for packed struct + function void check(); + if (packed_struct.byte_value != 8'hA0) $stop; + if (!(packed_struct.int_value inside {[0:100]})) $stop; + endfunction +endclass + +class UnpackedStructTest; + rand UnpackedStruct unpacked_struct; + + function new(); + unpacked_struct.byte_value = 8'h00; + unpacked_struct.int_value = 0; + unpacked_struct.non_rand_value = 42; + endfunction + + // Constraint block for unpacked struct + constraint unpacked_struct_constraint { + unpacked_struct.byte_value inside {8'hA0, 8'hB0, 8'hC0}; + unpacked_struct.int_value inside {[50:150]}; + } + + // Self-check function for unpacked struct + function void check(); + if (!(unpacked_struct.byte_value inside {8'hA0, 8'hB0, 8'hC0})) $stop; + if (!(unpacked_struct.int_value inside {[50:150]})) $stop; + if (unpacked_struct.non_rand_value != 42) $stop; // Check non-randomized member + endfunction +endclass + +module t_constraint_struct; + PackedStructTest packed_struct_test; + UnpackedStructTest unpacked_struct_test; + int success; + + initial begin + // Test packed struct + packed_struct_test = new(); + repeat(10) begin + success = packed_struct_test.randomize(); + if (success == 0) $stop; + packed_struct_test.check(); // Self-check for packed struct + end + + // Test unpacked struct + unpacked_struct_test = new(); + repeat(10) begin + success = unpacked_struct_test.randomize(); + if (success == 0) $stop; + unpacked_struct_test.check(); // Self-check for unpacked struct + end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_constraint_struct_complex.py b/test_regress/t/t_constraint_struct_complex.py new file mode 100755 index 000000000..466368b3d --- /dev/null +++ b/test_regress/t/t_constraint_struct_complex.py @@ -0,0 +1,21 @@ +#!/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') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_constraint_struct_complex.v b/test_regress/t/t_constraint_struct_complex.v new file mode 100755 index 000000000..b760bd375 --- /dev/null +++ b/test_regress/t/t_constraint_struct_complex.v @@ -0,0 +1,128 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by PlanV GmbH. +// SPDX-License-Identifier: CC0-1.0 + +class ArrayStruct; + /* verilator lint_off SIDEEFFECT */ + // Struct with an unpacked array + typedef int arr_3_t[3]; + typedef int arr_4_t[4]; + typedef struct { + rand arr_3_t arr_3; + arr_4_t arr_4; + rand int arr[3]; + } unpacked_struct_t; + + // Struct with a dynamic array + typedef struct { + rand int arr[]; + } dynamic_struct_t; + + // Struct with a queue + typedef struct { + rand int arr[$]; + } queue_struct_t; + + // Struct with an associative array (string as index) + typedef struct { + rand int arr[string]; + } associative_struct_t; + + // Struct with a multi-dimensional array + typedef struct { + rand int arr[2][3]; + } multi_dim_struct_t; + + // Struct with a mix of dynamic and unpacked arrays + typedef struct { + rand int mix_arr[3][]; + } mixed_struct_t; + + rand unpacked_struct_t s1; + rand dynamic_struct_t s2; + rand queue_struct_t s3; + rand associative_struct_t s4; + rand multi_dim_struct_t s5; + rand mixed_struct_t s6; + + constraint c_unpacked { + foreach (s1.arr[i]) s1.arr[i] inside {1, 2, 3, 4}; + foreach (s1.arr_3[i]) s1.arr_3[i] inside {11, 22, 33, 44, 55}; + } + constraint c_dynamic { foreach (s2.arr[i]) s2.arr[i] inside {[10:20]}; } + constraint c_queue { foreach (s3.arr[i]) s3.arr[i] inside {[100:200]}; } + constraint c_assoc { + s4.arr["one"] inside {[10:50]}; + s4.arr["two"] inside {[51:100]}; + s4.arr["three"] inside {[101:150]}; + } + constraint c_multi_dim { foreach (s5.arr[i, j]) s5.arr[i][j] inside {[0:9]}; } + constraint c_mix { + foreach (s6.mix_arr[i, j]) s6.mix_arr[i][j] inside {[50:100]}; + } + + function new(); + s1.arr = '{1, 2, 3}; + s1.arr_3 = '{1, 2, 3}; + s1.arr_4 = '{0, 2, 3, 4}; + s2.arr = new[3]; + foreach(s2.arr[i]) begin + s2.arr[i] = 'h0 + i; + end + s3.arr.push_back(100); + s3.arr.push_back(200); + s3.arr.push_back(300); + s4.arr["one"] = 1000; + s4.arr["two"] = 2000; + s4.arr["three"] = 3000; + s5.arr = '{ '{default:0}, '{default:0} }; + foreach (s6.mix_arr[i]) begin + s6.mix_arr[i] = new[i + 1]; + end + endfunction + + function void print(); + foreach (s1.arr[i]) $display("s1.arr[%0d] = %0d", i, s1.arr[i]); + foreach (s1.arr_3[i]) $display("s1.arr_3[%0d] = %0d", i, s1.arr_3[i]); + foreach (s1.arr_4[i]) $display("s1.arr_4[%0d] = %0d", i, s1.arr_4[i]); + foreach (s2.arr[i]) $display("s2.arr[%0d] = %0d", i, s2.arr[i]); + foreach (s3.arr[i]) $display("s3.arr[%0d] = %0d", i, s3.arr[i]); + foreach (s4.arr[i]) $display("s4.arr[\"%s\"] = %0d", i, s4.arr[i]); + foreach (s5.arr[i, j]) $display("s5.arr[%0d][%0d] = %0d", i, j, s5.arr[i][j]); + foreach (s6.mix_arr[i, j]) $display("s6.mix_arr[%0d][%0d] = %0d", i, j, s6.mix_arr[i][j]); + endfunction + + // Self-test function to verify constraints + function void self_test(); + foreach (s1.arr[i]) if (!(s1.arr[i] inside {1, 2, 3, 4})) $stop; + foreach (s1.arr_3[i]) if (!(s1.arr_3[i] inside {11, 22, 33, 44, 55})) $stop; + // Note: s1.arr_4[0] is not rand + if ((s1.arr_4[0] != 0) || (s1.arr_4[1] != 2) || (s1.arr_4[2] != 3) || (s1.arr_4[3] != 4)) $stop; + foreach (s2.arr[i]) if (!(s2.arr[i] inside {[10:20]})) $stop; + foreach (s3.arr[i]) if (!(s3.arr[i] inside {[100:200]})) $stop; + if (!(s4.arr["one"] inside {[10:50]})) $stop; + if (!(s4.arr["two"] inside {[51:100]})) $stop; + if (!(s4.arr["three"] inside {[101:150]})) $stop; + foreach (s5.arr[i, j]) if (!(s5.arr[i][j] inside {[0:9]})) $stop; + foreach (s6.mix_arr[i]) if (s6.mix_arr[i].size() == 0) $stop; + foreach (s6.mix_arr[i, j]) if (!(s6.mix_arr[i][j] inside {[50:100]})) $stop; + endfunction + /* verilator lint_off SIDEEFFECT */ +endclass + +module t_constraint_struct_complex; + int success; + ArrayStruct asc; + initial begin + asc = new(); + success = asc.randomize(); + if (success != 1) $stop; + asc.self_test(); + // asc.print(); + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_cover_expr.out b/test_regress/t/t_cover_expr.out new file mode 100644 index 000000000..0a0f3ff76 --- /dev/null +++ b/test_regress/t/t_cover_expr.out @@ -0,0 +1,345 @@ +// // verilator_coverage annotation + // DESCRIPTION: Verilator: Verilog Test module + // + // This file ONLY is placed under the Creative Commons Public Domain, for + // any use, without warranty, 2024 by Wilson Snyder. + // SPDX-License-Identifier: CC0-1.0 + + module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + integer cyc; + initial cyc=1; + + logic [63:32] cyc2; + always_comb cyc2 = cyc; + + integer some_int; + integer other_int; + logic some_bool; + + wire t1 = cyc[0]; + wire t2 = cyc[1]; + wire t3 = cyc[2]; + wire t4 = cyc[3]; + + localparam bit ONE = 1'b1; + localparam bit ZERO = 1'b0; + + function automatic bit invert(bit x); + 000015 return ~x; ++000012 point: comment=(x==0) => 1 hier=top.t ++000015 point: comment=(x==1) => 0 hier=top.t + endfunction + + function automatic bit and_oper(bit a, bit b); +%000005 return a & b; +-000004 point: comment=(a==0) => 0 hier=top.t +-000002 point: comment=(a==1 && b==1) => 1 hier=top.t +-000005 point: comment=(b==0) => 0 hier=top.t + endfunction + + localparam int num_intfs = 4; + intf the_intfs [num_intfs-1:0] (); + genvar intf_i; + for (intf_i = 0; intf_i < num_intfs; intf_i++) begin + always_comb the_intfs[intf_i].t = cyc[intf_i]; + end + + always @ (posedge clk) begin + cyc <= cyc + 1; +%000004 if ((~cyc[0] && cyc[1]) || (~cyc[2] && cyc[3])) $write(""); +-000002 point: comment=(cyc[0]==0 && cyc[1]==1) => 1 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[2]==1) => 0 hier=top.t +-000004 point: comment=(cyc[0]==1 && cyc[3]==0) => 0 hier=top.t +-000002 point: comment=(cyc[1]==0 && cyc[2]==1) => 0 hier=top.t +-000003 point: comment=(cyc[1]==0 && cyc[3]==0) => 0 hier=top.t +-000002 point: comment=(cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +%000004 if ((~cyc2[32] && cyc2[33]) || (~cyc2[34] && cyc2[35])) $write(""); +-000002 point: comment=(cyc2[32]==0 && cyc2[33]==1) => 1 hier=top.t +-000002 point: comment=(cyc2[32]==1 && cyc2[34]==1) => 0 hier=top.t +-000004 point: comment=(cyc2[32]==1 && cyc2[35]==0) => 0 hier=top.t +-000002 point: comment=(cyc2[33]==0 && cyc2[34]==1) => 0 hier=top.t +-000003 point: comment=(cyc2[33]==0 && cyc2[35]==0) => 0 hier=top.t +-000002 point: comment=(cyc2[34]==0 && cyc2[35]==1) => 1 hier=top.t +%000004 if ((~the_intfs[0].t && the_intfs[1].t) || (~the_intfs[2].t && the_intfs[3].t)) $write(""); +-000002 point: comment=(the_intfs[0].t==0 && the_intfs[1].t==1) => 1 hier=top.t +-000002 point: comment=(the_intfs[0].t==1 && the_intfs[2].t==1) => 0 hier=top.t +-000004 point: comment=(the_intfs[0].t==1 && the_intfs[3].t==0) => 0 hier=top.t +-000002 point: comment=(the_intfs[1].t==0 && the_intfs[2].t==1) => 0 hier=top.t +-000003 point: comment=(the_intfs[1].t==0 && the_intfs[3].t==0) => 0 hier=top.t +-000002 point: comment=(the_intfs[2].t==0 && the_intfs[3].t==1) => 1 hier=top.t +%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 +%000005 if (t3 && (t1 == t2)) $write(""); +-000005 point: comment=((t1 == t2)==0) => 0 hier=top.t +-000005 point: comment=(t3==0) => 0 hier=top.t +-000002 point: comment=(t3==1 && (t1 == t2)==1) => 1 hier=top.t +%000005 if (123 == (124 - 32'(t1 || t2))) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000005 point: comment=(t1==1) => 1 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +%000004 some_int <= (t2 || t3) ? 345 : 567; +-000003 point: comment=(t2==0 && t3==0) => 0 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +-000004 point: comment=(t3==1) => 1 hier=top.t +%000005 some_bool <= t1 && t2; +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 & t2) $write(""); +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%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 +%000003 if (t1 ^ t2) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 0 hier=top.t +%000005 if (~(t1 & t2)) $write(""); +-000004 point: comment=(t1==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 0 hier=top.t +-000005 point: comment=(t2==0) => 1 hier=top.t +%000004 if (t1 -> t2) $write(""); +-000004 point: comment=(t1==0) => 1 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 0 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +%000003 if (t1 <-> t2) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 1 hier=top.t +-000002 point: comment=(t1==0 && t2==1) => 0 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +%000005 if (&cyc[2:0]) $write(""); +-000004 point: comment=(cyc[2:0][0]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 1 hier=top.t +-000005 point: comment=(cyc[2:0][1]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][2]==0) => 0 hier=top.t +%000007 if (&cyc[3:2]) $write(""); +-000005 point: comment=(cyc[3:2][0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[3:2][0]==1 && cyc[3:2][1]==1) => 1 hier=top.t +-000007 point: comment=(cyc[3:2][1]==0) => 0 hier=top.t +%000005 if (|cyc[2:0]) $write(""); +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][0]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][1]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][2]==1) => 1 hier=top.t +%000002 if (^cyc[2:0]) $write(""); +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==1) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==1 && cyc[2:0][2]==0) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 0 hier=top.t +-000002 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==0 && cyc[2:0][2]==1) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 1 hier=top.t +%000005 if (|cyc[2:0] || cyc[3]) $write(""); +-000000 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0 && cyc[3]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][0]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][1]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][2]==1) => 1 hier=top.t +-000002 point: comment=(cyc[3]==1) => 1 hier=top.t +%000005 if (t1 & t2 & 1'b1) $write(""); +-000000 point: comment=(1'h1==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1 && 1'h1==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000009 if (t1 & t2 & 1'b0) $write(""); +-000009 point: comment=(1'h0==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && 1'h0==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 & t2 & ONE) $write(""); +-000000 point: comment=(ONE==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1 && ONE==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000009 if (t1 & t2 & ZERO) $write(""); +-000009 point: comment=(ZERO==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && ZERO==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 && t2) begin +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + $write(""); +%000003 end else if (t1 || t2) begin +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000003 point: comment=(t1==1) => 1 hier=top.t +-000002 point: comment=(t2==1) => 1 hier=top.t + $write(""); + end +%000005 if (invert(t1) && t2) $write(""); +-000005 point: comment=(invert(t1)==0) => 0 hier=top.t +-000002 point: comment=(invert(t1)==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + if (and_oper(t1, t2)) $write(""); +%000005 if (t2 && t3) begin +-000005 point: comment=(t2==0) => 0 hier=top.t +-000002 point: comment=(t2==1 && t3==1) => 1 hier=top.t +-000005 point: comment=(t3==0) => 0 hier=top.t +%000001 if (t1 && t2) $write(""); +-000001 point: comment=(t1==0) => 0 hier=top.t +-000001 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000000 point: comment=(t2==0) => 0 hier=top.t + end + if (0 == 1) begin + for (int loop_var = 0; loop_var < 1; loop_var++) begin +%000000 if (cyc[loop_var] && t2) $write(""); +-000000 point: comment=(cyc[loop_var[4:0]+:32'h1]]==0) => 0 hier=top.t +-000000 point: comment=(cyc[loop_var[4:0]+:32'h1]]==1 && t2==1) => 1 hier=top.t +-000000 point: comment=(t2==0) => 0 hier=top.t + end + end + // stop at the first layer even if there's more to find +%000007 if ((cyc[3+32'(t1 && t2)+:2] == cyc[5+32'(t3 || t4)+:2]) || cyc[31]) $write(""); +-000002 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:32'sh2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:32'sh2]])==0 && cyc[31]==0) => 0 hier=top.t +-000007 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:32'sh2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:32'sh2]])==1) => 1 hier=top.t +-000000 point: comment=(cyc[31]==1) => 1 hier=top.t + // impossible branches and redundant terms +%000005 if ((t1 && t2) && ~(t1 && t3) && (t1 || t4)) $write(""); +-000003 point: comment=(t1==0 && t4==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && t3==0 && t4==1) => 1 hier=top.t +-000001 point: comment=(t1==1 && t2==1 && t3==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if ((cyc[0] && cyc[1]) && ~(cyc[0] && cyc[2]) && (cyc[0] || cyc[3])) $write(""); +-000003 point: comment=(cyc[0]==0 && cyc[3]==0) => 0 hier=top.t +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +-000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0) => 1 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[2]==1) => 0 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + // demonstrate current limitations of term matching scheme +%000005 if ((cyc[0] && cyc[1]) && ~(cyc[1-1] && cyc[2]) && (cyc[2-2] || cyc[3])) $write(""); +-000002 point: comment=(cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==1 && cyc[2]==1) => 0 hier=top.t +-000003 point: comment=(cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==0 && cyc[3]==0) => 0 hier=top.t +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==0 && cyc[3]==1) => 1 hier=top.t +-000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + //verilator coverage_off + if (t1 && t2) $write(""); + //verilator coverage_on + if ((~t1 && t2) +%000004 || +-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 + (~t3 && t4)) $write(""); + // intentionally testing wonkified expression terms + if ( + cyc[ + 0 +%000005 ] & +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[1]==1) => 1 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + cyc + [1]) $write(""); + // for now each ternary condition is considered in isolation +%000005 other_int <= t1 ? t2 ? 1 : 2 : 3; +-000004 point: comment=(t1==0) => 0 hier=top.t +-000005 point: comment=(t1==1) => 1 hier=top.t + // no expression coverage for multi-bit expressions + if ((cyc[1:0] & cyc[3:2]) == 2'b11) $write(""); + // truth table is too large + if (^cyc[6:0]) $write(""); + // this one is too big even for t_cover_expr_max + if (^cyc) $write(""); + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + + always_comb begin +%000005 if (t1 && t2) $write(""); +-000005 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + end + + logic ta, tb, tc; + initial begin + int q[5]; + int qv[$]; + + q = '{1, 2, 2, 4, 3}; + // lambas not handled + // NB: there is a bug w/ tracing find_first (maybe lambdas in general?) + // tracing_off does not work around the bug + qv = q.find_first with (item[0] & item[1]); + ta = '1; + tb = '0; + tc = '0; +%000001 while (ta || tb || tc) begin +-000001 point: comment=(ta==0 && tb==0 && tc==0) => 0 hier=top.t +-000000 point: comment=(ta==1) => 1 hier=top.t +-000000 point: comment=(tb==1) => 1 hier=top.t +-000000 point: comment=(tc==1) => 1 hier=top.t + tc = tb; + tb = ta; + ta = '0; + end + end + + sub the_sub_1 (.p(t1), .q(t2)); + sub the_sub_2 (.p(t3), .q(t4)); + // TODO -- non-process expressions + sub the_sub_3 (.p(t1 ? t2 : t3), .q(t4)); + + // TODO + // pragma for expr coverage off / on + // investigate cover point sorting in annotated source + // consider reporting don't care terms + // + // Branches which are statically impossible to reach are still reported. + // E.g. + // -000000 point: comment=(t1=1 && t2=1 && 1'h0=1) => 1 hier=top.t + // These could potentially be pruned, but they currently follow suit for + // what branch coverage does. Perhaps a switch should be added to not + // count statically impossible things. + + endmodule + + module sub ( + input p, + input q + ); + + always_comb begin +~000019 if (p && q) $write(""); ++000017 point: comment=(p==0) => 0 hier=top.t.the_sub_* +-000002 point: comment=(p==1 && q==1) => 1 hier=top.t.the_sub_* ++000019 point: comment=(q==0) => 0 hier=top.t.the_sub_* + end + + endmodule + + interface intf(); + logic t; + endinterface + diff --git a/test_regress/t/t_cover_expr.py b/test_regress/t/t_cover_expr.py new file mode 100755 index 000000000..0f9b3aca8 --- /dev/null +++ b/test_regress/t/t_cover_expr.py @@ -0,0 +1,34 @@ +#!/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 +from pathlib import Path + +test.scenarios('simulator') + +test.compile(verilator_flags2=['--cc', '--coverage-expr']) + +test.execute() + +# Read the input .v file and do any CHECK_COVER requests +test.inline_checks() + +test.run(cmd=[ + os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage", + "--annotate-points", + "--annotate", + test.obj_dir + "/annotated", + test.obj_dir + "/coverage.dat", +], + verilator_run=True) + +top = Path(test.top_filename) +test.files_identical(test.obj_dir + f"/annotated/{top.name}", test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_cover_expr.v b/test_regress/t/t_cover_expr.v new file mode 100644 index 000000000..95cbe881e --- /dev/null +++ b/test_regress/t/t_cover_expr.v @@ -0,0 +1,177 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + integer cyc; + initial cyc=1; + + logic [63:32] cyc2; + always_comb cyc2 = cyc; + + integer some_int; + integer other_int; + logic some_bool; + + wire t1 = cyc[0]; + wire t2 = cyc[1]; + wire t3 = cyc[2]; + wire t4 = cyc[3]; + + localparam bit ONE = 1'b1; + localparam bit ZERO = 1'b0; + + function automatic bit invert(bit x); + return ~x; + endfunction + + function automatic bit and_oper(bit a, bit b); + return a & b; + endfunction + + localparam int num_intfs = 4; + intf the_intfs [num_intfs-1:0] (); + genvar intf_i; + for (intf_i = 0; intf_i < num_intfs; intf_i++) begin + always_comb the_intfs[intf_i].t = cyc[intf_i]; + end + + always @ (posedge clk) begin + cyc <= cyc + 1; + if ((~cyc[0] && cyc[1]) || (~cyc[2] && cyc[3])) $write(""); + if ((~cyc2[32] && cyc2[33]) || (~cyc2[34] && cyc2[35])) $write(""); + if ((~the_intfs[0].t && the_intfs[1].t) || (~the_intfs[2].t && the_intfs[3].t)) $write(""); + if ((~t1 && t2) || (~t3 && t4)) $write(""); + if (t3 && (t1 == t2)) $write(""); + if (123 == (124 - 32'(t1 || t2))) $write(""); + some_int <= (t2 || t3) ? 345 : 567; + some_bool <= t1 && t2; + if (t1 & t2) $write(""); + if ((!t1 && t2) | (~t3 && t4)) $write(""); + if (t1 ^ t2) $write(""); + if (~(t1 & t2)) $write(""); + if (t1 -> t2) $write(""); + if (t1 <-> t2) $write(""); + if (&cyc[2:0]) $write(""); + if (&cyc[3:2]) $write(""); + if (|cyc[2:0]) $write(""); + if (^cyc[2:0]) $write(""); + if (|cyc[2:0] || cyc[3]) $write(""); + if (t1 & t2 & 1'b1) $write(""); + if (t1 & t2 & 1'b0) $write(""); + if (t1 & t2 & ONE) $write(""); + if (t1 & t2 & ZERO) $write(""); + if (t1 && t2) begin + $write(""); + end else if (t1 || t2) begin + $write(""); + end + if (invert(t1) && t2) $write(""); + if (and_oper(t1, t2)) $write(""); + if (t2 && t3) begin + if (t1 && t2) $write(""); + end + if (0 == 1) begin + for (int loop_var = 0; loop_var < 1; loop_var++) begin + if (cyc[loop_var] && t2) $write(""); + end + end + // stop at the first layer even if there's more to find + if ((cyc[3+32'(t1 && t2)+:2] == cyc[5+32'(t3 || t4)+:2]) || cyc[31]) $write(""); + // impossible branches and redundant terms + if ((t1 && t2) && ~(t1 && t3) && (t1 || t4)) $write(""); + if ((cyc[0] && cyc[1]) && ~(cyc[0] && cyc[2]) && (cyc[0] || cyc[3])) $write(""); + // demonstrate current limitations of term matching scheme + if ((cyc[0] && cyc[1]) && ~(cyc[1-1] && cyc[2]) && (cyc[2-2] || cyc[3])) $write(""); + //verilator coverage_off + if (t1 && t2) $write(""); + //verilator coverage_on + if ((~t1 && t2) + || + (~t3 && t4)) $write(""); + // intentionally testing wonkified expression terms + if ( + cyc[ + 0 + ] & + cyc + [1]) $write(""); + // for now each ternary condition is considered in isolation + other_int <= t1 ? t2 ? 1 : 2 : 3; + // no expression coverage for multi-bit expressions + if ((cyc[1:0] & cyc[3:2]) == 2'b11) $write(""); + // truth table is too large + if (^cyc[6:0]) $write(""); + // this one is too big even for t_cover_expr_max + if (^cyc) $write(""); + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + + always_comb begin + if (t1 && t2) $write(""); + end + + logic ta, tb, tc; + initial begin + int q[5]; + int qv[$]; + + q = '{1, 2, 2, 4, 3}; + // lambas not handled + // NB: there is a bug w/ tracing find_first (maybe lambdas in general?) + // tracing_off does not work around the bug + qv = q.find_first with (item[0] & item[1]); + ta = '1; + tb = '0; + tc = '0; + while (ta || tb || tc) begin + tc = tb; + tb = ta; + ta = '0; + end + end + + sub the_sub_1 (.p(t1), .q(t2)); + sub the_sub_2 (.p(t3), .q(t4)); + // TODO -- non-process expressions + sub the_sub_3 (.p(t1 ? t2 : t3), .q(t4)); + + // TODO + // pragma for expr coverage off / on + // investigate cover point sorting in annotated source + // consider reporting don't care terms + // + // Branches which are statically impossible to reach are still reported. + // E.g. + // -000000 point: comment=(t1=1 && t2=1 && 1'h0=1) => 1 hier=top.t + // These could potentially be pruned, but they currently follow suit for + // what branch coverage does. Perhaps a switch should be added to not + // count statically impossible things. + +endmodule + +module sub ( + input p, + input q +); + + always_comb begin + if (p && q) $write(""); + end + +endmodule + +interface intf(); + logic t; +endinterface diff --git a/test_regress/t/t_cover_expr_max.out b/test_regress/t/t_cover_expr_max.out new file mode 100644 index 000000000..d01d1a412 --- /dev/null +++ b/test_regress/t/t_cover_expr_max.out @@ -0,0 +1,473 @@ +// // verilator_coverage annotation + // DESCRIPTION: Verilator: Verilog Test module + // + // This file ONLY is placed under the Creative Commons Public Domain, for + // any use, without warranty, 2024 by Wilson Snyder. + // SPDX-License-Identifier: CC0-1.0 + + module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + integer cyc; + initial cyc=1; + + logic [63:32] cyc2; + always_comb cyc2 = cyc; + + integer some_int; + integer other_int; + logic some_bool; + + wire t1 = cyc[0]; + wire t2 = cyc[1]; + wire t3 = cyc[2]; + wire t4 = cyc[3]; + + localparam bit ONE = 1'b1; + localparam bit ZERO = 1'b0; + + function automatic bit invert(bit x); + 000015 return ~x; ++000012 point: comment=(x==0) => 1 hier=top.t ++000015 point: comment=(x==1) => 0 hier=top.t + endfunction + + function automatic bit and_oper(bit a, bit b); +%000005 return a & b; +-000004 point: comment=(a==0) => 0 hier=top.t +-000002 point: comment=(a==1 && b==1) => 1 hier=top.t +-000005 point: comment=(b==0) => 0 hier=top.t + endfunction + + localparam int num_intfs = 4; + intf the_intfs [num_intfs-1:0] (); + genvar intf_i; + for (intf_i = 0; intf_i < num_intfs; intf_i++) begin + always_comb the_intfs[intf_i].t = cyc[intf_i]; + end + + always @ (posedge clk) begin + cyc <= cyc + 1; +%000004 if ((~cyc[0] && cyc[1]) || (~cyc[2] && cyc[3])) $write(""); +-000002 point: comment=(cyc[0]==0 && cyc[1]==1) => 1 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[2]==1) => 0 hier=top.t +-000004 point: comment=(cyc[0]==1 && cyc[3]==0) => 0 hier=top.t +-000002 point: comment=(cyc[1]==0 && cyc[2]==1) => 0 hier=top.t +-000003 point: comment=(cyc[1]==0 && cyc[3]==0) => 0 hier=top.t +-000002 point: comment=(cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +%000004 if ((~cyc2[32] && cyc2[33]) || (~cyc2[34] && cyc2[35])) $write(""); +-000002 point: comment=(cyc2[32]==0 && cyc2[33]==1) => 1 hier=top.t +-000002 point: comment=(cyc2[32]==1 && cyc2[34]==1) => 0 hier=top.t +-000004 point: comment=(cyc2[32]==1 && cyc2[35]==0) => 0 hier=top.t +-000002 point: comment=(cyc2[33]==0 && cyc2[34]==1) => 0 hier=top.t +-000003 point: comment=(cyc2[33]==0 && cyc2[35]==0) => 0 hier=top.t +-000002 point: comment=(cyc2[34]==0 && cyc2[35]==1) => 1 hier=top.t +%000004 if ((~the_intfs[0].t && the_intfs[1].t) || (~the_intfs[2].t && the_intfs[3].t)) $write(""); +-000002 point: comment=(the_intfs[0].t==0 && the_intfs[1].t==1) => 1 hier=top.t +-000002 point: comment=(the_intfs[0].t==1 && the_intfs[2].t==1) => 0 hier=top.t +-000004 point: comment=(the_intfs[0].t==1 && the_intfs[3].t==0) => 0 hier=top.t +-000002 point: comment=(the_intfs[1].t==0 && the_intfs[2].t==1) => 0 hier=top.t +-000003 point: comment=(the_intfs[1].t==0 && the_intfs[3].t==0) => 0 hier=top.t +-000002 point: comment=(the_intfs[2].t==0 && the_intfs[3].t==1) => 1 hier=top.t +%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 +%000005 if (t3 && (t1 == t2)) $write(""); +-000005 point: comment=((t1 == t2)==0) => 0 hier=top.t +-000005 point: comment=(t3==0) => 0 hier=top.t +-000002 point: comment=(t3==1 && (t1 == t2)==1) => 1 hier=top.t +%000005 if (123 == (124 - 32'(t1 || t2))) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000005 point: comment=(t1==1) => 1 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +%000004 some_int <= (t2 || t3) ? 345 : 567; +-000003 point: comment=(t2==0 && t3==0) => 0 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +-000004 point: comment=(t3==1) => 1 hier=top.t +%000005 some_bool <= t1 && t2; +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 & t2) $write(""); +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%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 +%000003 if (t1 ^ t2) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 0 hier=top.t +%000005 if (~(t1 & t2)) $write(""); +-000004 point: comment=(t1==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 0 hier=top.t +-000005 point: comment=(t2==0) => 1 hier=top.t +%000004 if (t1 -> t2) $write(""); +-000004 point: comment=(t1==0) => 1 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 0 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +%000003 if (t1 <-> t2) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 1 hier=top.t +-000002 point: comment=(t1==0 && t2==1) => 0 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +%000005 if (&cyc[2:0]) $write(""); +-000004 point: comment=(cyc[2:0][0]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 1 hier=top.t +-000005 point: comment=(cyc[2:0][1]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][2]==0) => 0 hier=top.t +%000007 if (&cyc[3:2]) $write(""); +-000005 point: comment=(cyc[3:2][0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[3:2][0]==1 && cyc[3:2][1]==1) => 1 hier=top.t +-000007 point: comment=(cyc[3:2][1]==0) => 0 hier=top.t +%000005 if (|cyc[2:0]) $write(""); +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][0]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][1]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][2]==1) => 1 hier=top.t +%000002 if (^cyc[2:0]) $write(""); +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==1) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==1 && cyc[2:0][2]==0) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 0 hier=top.t +-000002 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==0 && cyc[2:0][2]==1) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 1 hier=top.t +%000005 if (|cyc[2:0] || cyc[3]) $write(""); +-000000 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0 && cyc[3]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][0]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][1]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][2]==1) => 1 hier=top.t +-000002 point: comment=(cyc[3]==1) => 1 hier=top.t +%000005 if (t1 & t2 & 1'b1) $write(""); +-000000 point: comment=(1'h1==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1 && 1'h1==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000009 if (t1 & t2 & 1'b0) $write(""); +-000009 point: comment=(1'h0==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && 1'h0==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 & t2 & ONE) $write(""); +-000000 point: comment=(ONE==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1 && ONE==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000009 if (t1 & t2 & ZERO) $write(""); +-000009 point: comment=(ZERO==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && ZERO==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 && t2) begin +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + $write(""); +%000003 end else if (t1 || t2) begin +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000003 point: comment=(t1==1) => 1 hier=top.t +-000002 point: comment=(t2==1) => 1 hier=top.t + $write(""); + end +%000005 if (invert(t1) && t2) $write(""); +-000005 point: comment=(invert(t1)==0) => 0 hier=top.t +-000002 point: comment=(invert(t1)==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + if (and_oper(t1, t2)) $write(""); +%000005 if (t2 && t3) begin +-000005 point: comment=(t2==0) => 0 hier=top.t +-000002 point: comment=(t2==1 && t3==1) => 1 hier=top.t +-000005 point: comment=(t3==0) => 0 hier=top.t +%000001 if (t1 && t2) $write(""); +-000001 point: comment=(t1==0) => 0 hier=top.t +-000001 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000000 point: comment=(t2==0) => 0 hier=top.t + end + if (0 == 1) begin + for (int loop_var = 0; loop_var < 1; loop_var++) begin +%000000 if (cyc[loop_var] && t2) $write(""); +-000000 point: comment=(cyc[loop_var[4:0]+:32'h1]]==0) => 0 hier=top.t +-000000 point: comment=(cyc[loop_var[4:0]+:32'h1]]==1 && t2==1) => 1 hier=top.t +-000000 point: comment=(t2==0) => 0 hier=top.t + end + end + // stop at the first layer even if there's more to find +%000007 if ((cyc[3+32'(t1 && t2)+:2] == cyc[5+32'(t3 || t4)+:2]) || cyc[31]) $write(""); +-000002 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:32'sh2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:32'sh2]])==0 && cyc[31]==0) => 0 hier=top.t +-000007 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:32'sh2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:32'sh2]])==1) => 1 hier=top.t +-000000 point: comment=(cyc[31]==1) => 1 hier=top.t + // impossible branches and redundant terms +%000005 if ((t1 && t2) && ~(t1 && t3) && (t1 || t4)) $write(""); +-000003 point: comment=(t1==0 && t4==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && t3==0 && t4==1) => 1 hier=top.t +-000001 point: comment=(t1==1 && t2==1 && t3==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if ((cyc[0] && cyc[1]) && ~(cyc[0] && cyc[2]) && (cyc[0] || cyc[3])) $write(""); +-000003 point: comment=(cyc[0]==0 && cyc[3]==0) => 0 hier=top.t +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +-000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0) => 1 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[2]==1) => 0 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + // demonstrate current limitations of term matching scheme +%000005 if ((cyc[0] && cyc[1]) && ~(cyc[1-1] && cyc[2]) && (cyc[2-2] || cyc[3])) $write(""); +-000002 point: comment=(cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==1 && cyc[2]==1) => 0 hier=top.t +-000003 point: comment=(cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==0 && cyc[3]==0) => 0 hier=top.t +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==0 && cyc[3]==1) => 1 hier=top.t +-000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + //verilator coverage_off + if (t1 && t2) $write(""); + //verilator coverage_on + if ((~t1 && t2) +%000004 || +-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 + (~t3 && t4)) $write(""); + // intentionally testing wonkified expression terms + if ( + cyc[ + 0 +%000005 ] & +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[1]==1) => 1 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + cyc + [1]) $write(""); + // for now each ternary condition is considered in isolation +%000005 other_int <= t1 ? t2 ? 1 : 2 : 3; +-000004 point: comment=(t1==0) => 0 hier=top.t +-000005 point: comment=(t1==1) => 1 hier=top.t + // no expression coverage for multi-bit expressions + if ((cyc[1:0] & cyc[3:2]) == 2'b11) $write(""); + // truth table is too large +%000001 if (^cyc[6:0]) $write(""); +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000001 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000001 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000001 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000001 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000001 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000001 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000001 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000001 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000001 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t + // this one is too big even for t_cover_expr_max + if (^cyc) $write(""); + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + + always_comb begin +%000005 if (t1 && t2) $write(""); +-000005 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + end + + logic ta, tb, tc; + initial begin + int q[5]; + int qv[$]; + + q = '{1, 2, 2, 4, 3}; + // lambas not handled + // NB: there is a bug w/ tracing find_first (maybe lambdas in general?) + // tracing_off does not work around the bug + qv = q.find_first with (item[0] & item[1]); + ta = '1; + tb = '0; + tc = '0; +%000001 while (ta || tb || tc) begin +-000001 point: comment=(ta==0 && tb==0 && tc==0) => 0 hier=top.t +-000000 point: comment=(ta==1) => 1 hier=top.t +-000000 point: comment=(tb==1) => 1 hier=top.t +-000000 point: comment=(tc==1) => 1 hier=top.t + tc = tb; + tb = ta; + ta = '0; + end + end + + sub the_sub_1 (.p(t1), .q(t2)); + sub the_sub_2 (.p(t3), .q(t4)); + // TODO -- non-process expressions + sub the_sub_3 (.p(t1 ? t2 : t3), .q(t4)); + + // TODO + // pragma for expr coverage off / on + // investigate cover point sorting in annotated source + // consider reporting don't care terms + // + // Branches which are statically impossible to reach are still reported. + // E.g. + // -000000 point: comment=(t1=1 && t2=1 && 1'h0=1) => 1 hier=top.t + // These could potentially be pruned, but they currently follow suit for + // what branch coverage does. Perhaps a switch should be added to not + // count statically impossible things. + + endmodule + + module sub ( + input p, + input q + ); + + always_comb begin +~000019 if (p && q) $write(""); ++000017 point: comment=(p==0) => 0 hier=top.t.the_sub_* +-000002 point: comment=(p==1 && q==1) => 1 hier=top.t.the_sub_* ++000019 point: comment=(q==0) => 0 hier=top.t.the_sub_* + end + + endmodule + + interface intf(); + logic t; + endinterface + diff --git a/test_regress/t/t_cover_expr_max.py b/test_regress/t/t_cover_expr_max.py new file mode 100755 index 000000000..bc3049c66 --- /dev/null +++ b/test_regress/t/t_cover_expr_max.py @@ -0,0 +1,35 @@ +#!/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 +from pathlib import Path + +test.scenarios('simulator') +test.top_filename = "t/t_cover_expr.v" + +test.compile(verilator_flags2=['--cc', '--coverage-expr', '--coverage-expr-max', '128']) + +test.execute() + +# Read the input .v file and do any CHECK_COVER requests +test.inline_checks() + +test.run(cmd=[ + os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage", + "--annotate-points", + "--annotate", + test.obj_dir + "/annotated", + test.obj_dir + "/coverage.dat", +], + verilator_run=True) + +top = Path(test.top_filename) +test.files_identical(test.obj_dir + f"/annotated/{top.name}", test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_cover_expr_trace.out b/test_regress/t/t_cover_expr_trace.out new file mode 100644 index 000000000..0a0f3ff76 --- /dev/null +++ b/test_regress/t/t_cover_expr_trace.out @@ -0,0 +1,345 @@ +// // verilator_coverage annotation + // DESCRIPTION: Verilator: Verilog Test module + // + // This file ONLY is placed under the Creative Commons Public Domain, for + // any use, without warranty, 2024 by Wilson Snyder. + // SPDX-License-Identifier: CC0-1.0 + + module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + integer cyc; + initial cyc=1; + + logic [63:32] cyc2; + always_comb cyc2 = cyc; + + integer some_int; + integer other_int; + logic some_bool; + + wire t1 = cyc[0]; + wire t2 = cyc[1]; + wire t3 = cyc[2]; + wire t4 = cyc[3]; + + localparam bit ONE = 1'b1; + localparam bit ZERO = 1'b0; + + function automatic bit invert(bit x); + 000015 return ~x; ++000012 point: comment=(x==0) => 1 hier=top.t ++000015 point: comment=(x==1) => 0 hier=top.t + endfunction + + function automatic bit and_oper(bit a, bit b); +%000005 return a & b; +-000004 point: comment=(a==0) => 0 hier=top.t +-000002 point: comment=(a==1 && b==1) => 1 hier=top.t +-000005 point: comment=(b==0) => 0 hier=top.t + endfunction + + localparam int num_intfs = 4; + intf the_intfs [num_intfs-1:0] (); + genvar intf_i; + for (intf_i = 0; intf_i < num_intfs; intf_i++) begin + always_comb the_intfs[intf_i].t = cyc[intf_i]; + end + + always @ (posedge clk) begin + cyc <= cyc + 1; +%000004 if ((~cyc[0] && cyc[1]) || (~cyc[2] && cyc[3])) $write(""); +-000002 point: comment=(cyc[0]==0 && cyc[1]==1) => 1 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[2]==1) => 0 hier=top.t +-000004 point: comment=(cyc[0]==1 && cyc[3]==0) => 0 hier=top.t +-000002 point: comment=(cyc[1]==0 && cyc[2]==1) => 0 hier=top.t +-000003 point: comment=(cyc[1]==0 && cyc[3]==0) => 0 hier=top.t +-000002 point: comment=(cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +%000004 if ((~cyc2[32] && cyc2[33]) || (~cyc2[34] && cyc2[35])) $write(""); +-000002 point: comment=(cyc2[32]==0 && cyc2[33]==1) => 1 hier=top.t +-000002 point: comment=(cyc2[32]==1 && cyc2[34]==1) => 0 hier=top.t +-000004 point: comment=(cyc2[32]==1 && cyc2[35]==0) => 0 hier=top.t +-000002 point: comment=(cyc2[33]==0 && cyc2[34]==1) => 0 hier=top.t +-000003 point: comment=(cyc2[33]==0 && cyc2[35]==0) => 0 hier=top.t +-000002 point: comment=(cyc2[34]==0 && cyc2[35]==1) => 1 hier=top.t +%000004 if ((~the_intfs[0].t && the_intfs[1].t) || (~the_intfs[2].t && the_intfs[3].t)) $write(""); +-000002 point: comment=(the_intfs[0].t==0 && the_intfs[1].t==1) => 1 hier=top.t +-000002 point: comment=(the_intfs[0].t==1 && the_intfs[2].t==1) => 0 hier=top.t +-000004 point: comment=(the_intfs[0].t==1 && the_intfs[3].t==0) => 0 hier=top.t +-000002 point: comment=(the_intfs[1].t==0 && the_intfs[2].t==1) => 0 hier=top.t +-000003 point: comment=(the_intfs[1].t==0 && the_intfs[3].t==0) => 0 hier=top.t +-000002 point: comment=(the_intfs[2].t==0 && the_intfs[3].t==1) => 1 hier=top.t +%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 +%000005 if (t3 && (t1 == t2)) $write(""); +-000005 point: comment=((t1 == t2)==0) => 0 hier=top.t +-000005 point: comment=(t3==0) => 0 hier=top.t +-000002 point: comment=(t3==1 && (t1 == t2)==1) => 1 hier=top.t +%000005 if (123 == (124 - 32'(t1 || t2))) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000005 point: comment=(t1==1) => 1 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +%000004 some_int <= (t2 || t3) ? 345 : 567; +-000003 point: comment=(t2==0 && t3==0) => 0 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +-000004 point: comment=(t3==1) => 1 hier=top.t +%000005 some_bool <= t1 && t2; +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 & t2) $write(""); +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%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 +%000003 if (t1 ^ t2) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 0 hier=top.t +%000005 if (~(t1 & t2)) $write(""); +-000004 point: comment=(t1==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 0 hier=top.t +-000005 point: comment=(t2==0) => 1 hier=top.t +%000004 if (t1 -> t2) $write(""); +-000004 point: comment=(t1==0) => 1 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 0 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +%000003 if (t1 <-> t2) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 1 hier=top.t +-000002 point: comment=(t1==0 && t2==1) => 0 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +%000005 if (&cyc[2:0]) $write(""); +-000004 point: comment=(cyc[2:0][0]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 1 hier=top.t +-000005 point: comment=(cyc[2:0][1]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][2]==0) => 0 hier=top.t +%000007 if (&cyc[3:2]) $write(""); +-000005 point: comment=(cyc[3:2][0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[3:2][0]==1 && cyc[3:2][1]==1) => 1 hier=top.t +-000007 point: comment=(cyc[3:2][1]==0) => 0 hier=top.t +%000005 if (|cyc[2:0]) $write(""); +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][0]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][1]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][2]==1) => 1 hier=top.t +%000002 if (^cyc[2:0]) $write(""); +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==1) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==1 && cyc[2:0][2]==0) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 0 hier=top.t +-000002 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==0 && cyc[2:0][2]==1) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 1 hier=top.t +%000005 if (|cyc[2:0] || cyc[3]) $write(""); +-000000 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0 && cyc[3]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][0]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][1]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][2]==1) => 1 hier=top.t +-000002 point: comment=(cyc[3]==1) => 1 hier=top.t +%000005 if (t1 & t2 & 1'b1) $write(""); +-000000 point: comment=(1'h1==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1 && 1'h1==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000009 if (t1 & t2 & 1'b0) $write(""); +-000009 point: comment=(1'h0==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && 1'h0==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 & t2 & ONE) $write(""); +-000000 point: comment=(ONE==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1 && ONE==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000009 if (t1 & t2 & ZERO) $write(""); +-000009 point: comment=(ZERO==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && ZERO==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 && t2) begin +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + $write(""); +%000003 end else if (t1 || t2) begin +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000003 point: comment=(t1==1) => 1 hier=top.t +-000002 point: comment=(t2==1) => 1 hier=top.t + $write(""); + end +%000005 if (invert(t1) && t2) $write(""); +-000005 point: comment=(invert(t1)==0) => 0 hier=top.t +-000002 point: comment=(invert(t1)==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + if (and_oper(t1, t2)) $write(""); +%000005 if (t2 && t3) begin +-000005 point: comment=(t2==0) => 0 hier=top.t +-000002 point: comment=(t2==1 && t3==1) => 1 hier=top.t +-000005 point: comment=(t3==0) => 0 hier=top.t +%000001 if (t1 && t2) $write(""); +-000001 point: comment=(t1==0) => 0 hier=top.t +-000001 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000000 point: comment=(t2==0) => 0 hier=top.t + end + if (0 == 1) begin + for (int loop_var = 0; loop_var < 1; loop_var++) begin +%000000 if (cyc[loop_var] && t2) $write(""); +-000000 point: comment=(cyc[loop_var[4:0]+:32'h1]]==0) => 0 hier=top.t +-000000 point: comment=(cyc[loop_var[4:0]+:32'h1]]==1 && t2==1) => 1 hier=top.t +-000000 point: comment=(t2==0) => 0 hier=top.t + end + end + // stop at the first layer even if there's more to find +%000007 if ((cyc[3+32'(t1 && t2)+:2] == cyc[5+32'(t3 || t4)+:2]) || cyc[31]) $write(""); +-000002 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:32'sh2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:32'sh2]])==0 && cyc[31]==0) => 0 hier=top.t +-000007 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:32'sh2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:32'sh2]])==1) => 1 hier=top.t +-000000 point: comment=(cyc[31]==1) => 1 hier=top.t + // impossible branches and redundant terms +%000005 if ((t1 && t2) && ~(t1 && t3) && (t1 || t4)) $write(""); +-000003 point: comment=(t1==0 && t4==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && t3==0 && t4==1) => 1 hier=top.t +-000001 point: comment=(t1==1 && t2==1 && t3==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if ((cyc[0] && cyc[1]) && ~(cyc[0] && cyc[2]) && (cyc[0] || cyc[3])) $write(""); +-000003 point: comment=(cyc[0]==0 && cyc[3]==0) => 0 hier=top.t +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +-000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0) => 1 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[2]==1) => 0 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + // demonstrate current limitations of term matching scheme +%000005 if ((cyc[0] && cyc[1]) && ~(cyc[1-1] && cyc[2]) && (cyc[2-2] || cyc[3])) $write(""); +-000002 point: comment=(cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==1 && cyc[2]==1) => 0 hier=top.t +-000003 point: comment=(cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==0 && cyc[3]==0) => 0 hier=top.t +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==0 && cyc[3]==1) => 1 hier=top.t +-000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + //verilator coverage_off + if (t1 && t2) $write(""); + //verilator coverage_on + if ((~t1 && t2) +%000004 || +-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 + (~t3 && t4)) $write(""); + // intentionally testing wonkified expression terms + if ( + cyc[ + 0 +%000005 ] & +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[1]==1) => 1 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + cyc + [1]) $write(""); + // for now each ternary condition is considered in isolation +%000005 other_int <= t1 ? t2 ? 1 : 2 : 3; +-000004 point: comment=(t1==0) => 0 hier=top.t +-000005 point: comment=(t1==1) => 1 hier=top.t + // no expression coverage for multi-bit expressions + if ((cyc[1:0] & cyc[3:2]) == 2'b11) $write(""); + // truth table is too large + if (^cyc[6:0]) $write(""); + // this one is too big even for t_cover_expr_max + if (^cyc) $write(""); + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + + always_comb begin +%000005 if (t1 && t2) $write(""); +-000005 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + end + + logic ta, tb, tc; + initial begin + int q[5]; + int qv[$]; + + q = '{1, 2, 2, 4, 3}; + // lambas not handled + // NB: there is a bug w/ tracing find_first (maybe lambdas in general?) + // tracing_off does not work around the bug + qv = q.find_first with (item[0] & item[1]); + ta = '1; + tb = '0; + tc = '0; +%000001 while (ta || tb || tc) begin +-000001 point: comment=(ta==0 && tb==0 && tc==0) => 0 hier=top.t +-000000 point: comment=(ta==1) => 1 hier=top.t +-000000 point: comment=(tb==1) => 1 hier=top.t +-000000 point: comment=(tc==1) => 1 hier=top.t + tc = tb; + tb = ta; + ta = '0; + end + end + + sub the_sub_1 (.p(t1), .q(t2)); + sub the_sub_2 (.p(t3), .q(t4)); + // TODO -- non-process expressions + sub the_sub_3 (.p(t1 ? t2 : t3), .q(t4)); + + // TODO + // pragma for expr coverage off / on + // investigate cover point sorting in annotated source + // consider reporting don't care terms + // + // Branches which are statically impossible to reach are still reported. + // E.g. + // -000000 point: comment=(t1=1 && t2=1 && 1'h0=1) => 1 hier=top.t + // These could potentially be pruned, but they currently follow suit for + // what branch coverage does. Perhaps a switch should be added to not + // count statically impossible things. + + endmodule + + module sub ( + input p, + input q + ); + + always_comb begin +~000019 if (p && q) $write(""); ++000017 point: comment=(p==0) => 0 hier=top.t.the_sub_* +-000002 point: comment=(p==1 && q==1) => 1 hier=top.t.the_sub_* ++000019 point: comment=(q==0) => 0 hier=top.t.the_sub_* + end + + endmodule + + interface intf(); + logic t; + endinterface + diff --git a/test_regress/t/t_cover_expr_trace.py b/test_regress/t/t_cover_expr_trace.py new file mode 100755 index 000000000..c1fd9df23 --- /dev/null +++ b/test_regress/t/t_cover_expr_trace.py @@ -0,0 +1,35 @@ +#!/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 +from pathlib import Path + +test.scenarios('simulator') +test.top_filename = "t/t_cover_expr.v" + +test.compile(verilator_flags2=['--cc', '--coverage-expr', '--trace']) + +test.execute() + +# Read the input .v file and do any CHECK_COVER requests +test.inline_checks() + +test.run(cmd=[ + os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage", + "--annotate-points", + "--annotate", + test.obj_dir + "/annotated", + test.obj_dir + "/coverage.dat", +], + verilator_run=True) + +top = Path(test.top_filename) +test.files_identical(test.obj_dir + f"/annotated/{top.name}", test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_covergroup_unsup.out b/test_regress/t/t_covergroup_unsup.out index 808facb6d..8c27e0ad6 100644 --- a/test_regress/t/t_covergroup_unsup.out +++ b/test_regress/t/t_covergroup_unsup.out @@ -1,383 +1,384 @@ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:25:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:25:4: Ignoring unsupported: covergroup 25 | covergroup cg_empty; | ^~~~~~~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:28:4: Unsupported: covergroup + ... For warning description see https://verilator.org/warn/COVERIGN?v=latest + ... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message. +%Warning-COVERIGN: t/t_covergroup_unsup.v:28:4: Ignoring unsupported: covergroup 28 | covergroup cg_opt; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:29:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:29:7: Ignoring unsupported: coverage option 29 | type_option.weight = 1; | ^~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:30:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:30:7: Ignoring unsupported: coverage option 30 | type_option.goal = 99; | ^~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:31:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:31:7: Ignoring unsupported: coverage option 31 | type_option.comment = "type_option_comment"; | ^~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:32:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:32:7: Ignoring unsupported: coverage option 32 | type_option.strobe = 0; | ^~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:33:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:33:7: Ignoring unsupported: coverage option 33 | type_option.merge_instances = 1; | ^~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:34:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:34:7: Ignoring unsupported: coverage option 34 | type_option.distribuge_first = 1; | ^~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:35:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:35:7: Ignoring unsupported: coverage option 35 | option.name = "the_name"; | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:36:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:36:7: Ignoring unsupported: coverage option 36 | option.weight = 1; | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:37:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:37:7: Ignoring unsupported: coverage option 37 | option.goal = 98; | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:38:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:38:7: Ignoring unsupported: coverage option 38 | option.comment = "option_comment"; | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:39:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:39:7: Ignoring unsupported: coverage option 39 | option.at_least = 20; | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:40:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:40:7: Ignoring unsupported: coverage option 40 | option.auto_bin_max = 10; | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:41:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:41:7: Ignoring unsupported: coverage option 41 | option.cross_num_print_missing = 2; | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:42:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:42:7: Ignoring unsupported: coverage option 42 | option.detect_overlap = 1; | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:43:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:43:7: Ignoring unsupported: coverage option 43 | option.per_instance = 1; | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:44:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:44:7: Ignoring unsupported: coverage option 44 | option.get_inst_coverage = 1; | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:47:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:47:4: Ignoring unsupported: covergroup 47 | covergroup cg_clockingevent() @(posedge clk); | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:47:34: Unsupported: coverage clocking event +%Warning-COVERIGN: t/t_covergroup_unsup.v:47:34: Ignoring unsupported: coverage clocking event 47 | covergroup cg_clockingevent() @(posedge clk); | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:49:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:49:4: Ignoring unsupported: covergroup 49 | covergroup cg_withfunction() with function sample (a); | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:49:33: Unsupported: coverage 'with' 'function' +%Warning-COVERIGN: t/t_covergroup_unsup.v:49:33: Ignoring unsupported: coverage 'with' 'function' 49 | covergroup cg_withfunction() with function sample (a); | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:51:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:51:4: Ignoring unsupported: covergroup 51 | covergroup cg_atat() @@ (begin funca or end funcb); | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:51:25: Unsupported: coverage '@@' events +%Warning-COVERIGN: t/t_covergroup_unsup.v:51:25: Ignoring unsupported: coverage '@@' events 51 | covergroup cg_atat() @@ (begin funca or end funcb); | ^~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:53:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:53:4: Ignoring unsupported: covergroup 53 | covergroup cg_bracket; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:56:4: Unsupported: covergroup - 56 | covergroup cg_bracket; +%Warning-COVERIGN: t/t_covergroup_unsup.v:56:4: Ignoring unsupported: covergroup + 56 | covergroup cg_bracket2; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:57:9: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:57:9: Ignoring unsupported: coverage option 57 | { option.name = "option"; } | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:59:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:59:4: Ignoring unsupported: covergroup 59 | covergroup cg_cp; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:60:7: Unsupported: cover point +%Warning-COVERIGN: t/t_covergroup_unsup.v:60:7: Ignoring unsupported: coverpoint 60 | coverpoint a; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:62:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:62:4: Ignoring unsupported: covergroup 62 | covergroup cg_cp_iff; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:63:20: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:63:20: Ignoring unsupported: cover 'iff' 63 | coverpoint a iff (b); | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:63:7: Unsupported: cover point +%Warning-COVERIGN: t/t_covergroup_unsup.v:63:7: Ignoring unsupported: coverpoint 63 | coverpoint a iff (b); | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:65:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:65:4: Ignoring unsupported: covergroup 65 | covergroup cg_id_cp_iff; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:66:24: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:66:24: Ignoring unsupported: cover 'iff' 66 | id: coverpoint a iff (b); | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:66:11: Unsupported: cover point +%Warning-COVERIGN: t/t_covergroup_unsup.v:66:11: Ignoring unsupported: coverpoint 66 | id: coverpoint a iff (b); | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:68:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:68:4: Ignoring unsupported: covergroup 68 | covergroup cg_id_cp_id1; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:69:28: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:69:28: Ignoring unsupported: cover 'iff' 69 | int id: coverpoint a iff (b); | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:69:15: Unsupported: cover point +%Warning-COVERIGN: t/t_covergroup_unsup.v:69:15: Ignoring unsupported: coverpoint 69 | int id: coverpoint a iff (b); | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:71:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:71:4: Ignoring unsupported: covergroup 71 | covergroup cg_id_cp_id2; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:72:32: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:72:32: Ignoring unsupported: cover 'iff' 72 | var int id: coverpoint a iff (b); | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:72:19: Unsupported: cover point +%Warning-COVERIGN: t/t_covergroup_unsup.v:72:19: Ignoring unsupported: coverpoint 72 | var int id: coverpoint a iff (b); | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:74:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:74:4: Ignoring unsupported: covergroup 74 | covergroup cg_id_cp_id3; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:75:34: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:75:34: Ignoring unsupported: cover 'iff' 75 | var [3:0] id: coverpoint a iff (b); | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:75:21: Unsupported: cover point +%Warning-COVERIGN: t/t_covergroup_unsup.v:75:21: Ignoring unsupported: coverpoint 75 | var [3:0] id: coverpoint a iff (b); | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:77:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:77:4: Ignoring unsupported: covergroup 77 | covergroup cg_id_cp_id4; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:78:30: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:78:30: Ignoring unsupported: cover 'iff' 78 | [3:0] id: coverpoint a iff (b); | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:78:17: Unsupported: cover point +%Warning-COVERIGN: t/t_covergroup_unsup.v:78:17: Ignoring unsupported: coverpoint 78 | [3:0] id: coverpoint a iff (b); | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:80:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:80:4: Ignoring unsupported: covergroup 80 | covergroup cg_id_cp_id5; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:81:31: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:81:31: Ignoring unsupported: cover 'iff' 81 | signed id: coverpoint a iff (b); | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:81:18: Unsupported: cover point +%Warning-COVERIGN: t/t_covergroup_unsup.v:81:18: Ignoring unsupported: coverpoint 81 | signed id: coverpoint a iff (b); | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:84:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:84:4: Ignoring unsupported: covergroup 84 | covergroup cg_cross; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:85:18: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:85:18: Ignoring unsupported: cover 'iff' 85 | cross a, b iff (!rst); | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:85:7: Unsupported: cross +%Warning-COVERIGN: t/t_covergroup_unsup.v:85:7: Ignoring unsupported: cover cross 85 | cross a, b iff (!rst); | ^~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:87:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:87:4: Ignoring unsupported: covergroup 87 | covergroup cg_cross2; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:88:18: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:88:18: Ignoring unsupported: cover 'iff' 88 | cross a, b iff (!rst) {} | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:88:7: Unsupported: cross +%Warning-COVERIGN: t/t_covergroup_unsup.v:88:7: Ignoring unsupported: cover cross 88 | cross a, b iff (!rst) {} | ^~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:90:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:90:4: Ignoring unsupported: covergroup 90 | covergroup cg_cross3; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:91:20: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:91:20: Ignoring unsupported: coverage option 91 | cross a, b { option.comment = "cross"; option.weight = 12; } | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:91:46: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:91:46: Ignoring unsupported: coverage option 91 | cross a, b { option.comment = "cross"; option.weight = 12; } | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:91:7: Unsupported: cross +%Warning-COVERIGN: t/t_covergroup_unsup.v:91:7: Ignoring unsupported: cover cross 91 | cross a, b { option.comment = "cross"; option.weight = 12; } | ^~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:93:4: Unsupported: covergroup - 93 | covergroup cg_cross3; +%Warning-COVERIGN: t/t_covergroup_unsup.v:93:4: Ignoring unsupported: covergroup + 93 | covergroup cg_cross4; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:94:34: Unsupported: coverage cross 'function' declaration +%Warning-COVERIGN: t/t_covergroup_unsup.v:94:34: Ignoring unsupported: coverage cross 'function' declaration 94 | cross a, b { function void crossfunc; endfunction; } | ^~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:94:7: Unsupported: cross +%Warning-COVERIGN: t/t_covergroup_unsup.v:94:7: Ignoring unsupported: cover cross 94 | cross a, b { function void crossfunc; endfunction; } | ^~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:96:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:96:4: Ignoring unsupported: covergroup 96 | covergroup cg_cross_id; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:97:28: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:97:28: Ignoring unsupported: cover 'iff' 97 | my_cg_id: cross a, b iff (!rst); | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:97:17: Unsupported: cross +%Warning-COVERIGN: t/t_covergroup_unsup.v:97:17: Ignoring unsupported: cover cross 97 | my_cg_id: cross a, b iff (!rst); | ^~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:100:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:100:4: Ignoring unsupported: covergroup 100 | covergroup cg_binsoroptions_bk1; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:102:17: Unsupported: cover bin specification +%Warning-COVERIGN: t/t_covergroup_unsup.v:102:17: Ignoring unsupported: cover bin specification 102 | { bins ba = {a}; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:103:24: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:103:24: Ignoring unsupported: cover 'iff' 103 | { bins bar = {a} iff (!rst); } | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:103:18: Unsupported: cover bin specification +%Warning-COVERIGN: t/t_covergroup_unsup.v:103:18: Ignoring unsupported: cover bin specification 103 | { bins bar = {a} iff (!rst); } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:104:26: Unsupported: cover bin specification +%Warning-COVERIGN: t/t_covergroup_unsup.v:104:26: Ignoring unsupported: cover bin specification 104 | { illegal_bins ila = {a}; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:105:25: Unsupported: cover bin specification +%Warning-COVERIGN: t/t_covergroup_unsup.v:105:25: Ignoring unsupported: cover bin specification 105 | { ignore_bins iga = {a}; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:107:19: Unsupported: cover bin specification +%Warning-COVERIGN: t/t_covergroup_unsup.v:107:19: Ignoring unsupported: cover bin specification 107 | { bins ba[] = {a}; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:108:20: Unsupported: cover bin specification +%Warning-COVERIGN: t/t_covergroup_unsup.v:108:20: Ignoring unsupported: cover bin specification 108 | { bins ba[2] = {a}; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:110:23: Unsupported: cover bin 'with' specification +%Warning-COVERIGN: t/t_covergroup_unsup.v:110:23: Ignoring unsupported: cover bin 'with' specification 110 | { bins ba = {a} with { b }; } | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:112:27: Unsupported: cover bin 'wildcard' specification +%Warning-COVERIGN: t/t_covergroup_unsup.v:112:27: Ignoring unsupported: cover bin 'wildcard' specification 112 | { wildcard bins bwa = {a}; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:113:34: Unsupported: cover bin 'wildcard' 'with' specification +%Warning-COVERIGN: t/t_covergroup_unsup.v:113:34: Ignoring unsupported: cover bin 'wildcard' 'with' specification 113 | { wildcard bins bwaw = {a} with { b }; } | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:115:20: Unsupported: cover bin 'default' +%Warning-COVERIGN: t/t_covergroup_unsup.v:115:20: Ignoring unsupported: cover bin 'default' 115 | { bins def = default; } | ^~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:116:29: Unsupported: cover bin 'default' 'sequence' +%Warning-COVERIGN: t/t_covergroup_unsup.v:116:29: Ignoring unsupported: cover bin 'default' 'sequence' 116 | { bins defs = default sequence; } | ^~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:118:18: Unsupported: cover bin trans list +%Warning-COVERIGN: t/t_covergroup_unsup.v:118:18: Ignoring unsupported: cover bin trans list 118 | { bins bts = ( 1, 2 ); } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:119:9: Unsupported: cover bin 'wildcard' trans list +%Warning-COVERIGN: t/t_covergroup_unsup.v:119:9: Ignoring unsupported: cover bin 'wildcard' trans list 119 | { wildcard bins wbts = ( 1, 2 ); } | ^~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:120:33: Unsupported: covergroup value range +%Warning-COVERIGN: t/t_covergroup_unsup.v:120:33: Ignoring unsupported: covergroup value range 120 | { bins bts2 = ( 2, 3 ), ( [5:6] ) ; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:120:19: Unsupported: cover bin trans list +%Warning-COVERIGN: t/t_covergroup_unsup.v:120:19: Ignoring unsupported: cover bin trans list 120 | { bins bts2 = ( 2, 3 ), ( [5:6] ) ; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:122:27: Unsupported: cover trans set '=>' +%Warning-COVERIGN: t/t_covergroup_unsup.v:122:27: Ignoring unsupported: cover trans set '=>' 122 | { bins bts2 = ( 1,5 => 6,7 ) ; } | ^~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:122:19: Unsupported: cover bin trans list +%Warning-COVERIGN: t/t_covergroup_unsup.v:122:19: Ignoring unsupported: cover bin trans list 122 | { bins bts2 = ( 1,5 => 6,7 ) ; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:123:25: Unsupported: cover '[*' +%Warning-COVERIGN: t/t_covergroup_unsup.v:123:25: Ignoring unsupported: cover '[*' 123 | { bins bts2 = ( 3 [*5] ) ; } | ^~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:123:19: Unsupported: cover bin trans list +%Warning-COVERIGN: t/t_covergroup_unsup.v:123:19: Ignoring unsupported: cover bin trans list 123 | { bins bts2 = ( 3 [*5] ) ; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:124:25: Unsupported: cover '[*' +%Warning-COVERIGN: t/t_covergroup_unsup.v:124:25: Ignoring unsupported: cover '[*' 124 | { bins bts2 = ( 3 [*5:6] ) ; } | ^~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:124:19: Unsupported: cover bin trans list +%Warning-COVERIGN: t/t_covergroup_unsup.v:124:19: Ignoring unsupported: cover bin trans list 124 | { bins bts2 = ( 3 [*5:6] ) ; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:125:25: Unsupported: cover '[->' +%Warning-COVERIGN: t/t_covergroup_unsup.v:125:25: Ignoring unsupported: cover '[->' 125 | { bins bts2 = ( 3 [->5] ) ; } | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:125:19: Unsupported: cover bin trans list +%Warning-COVERIGN: t/t_covergroup_unsup.v:125:19: Ignoring unsupported: cover bin trans list 125 | { bins bts2 = ( 3 [->5] ) ; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:126:25: Unsupported: cover '[->' +%Warning-COVERIGN: t/t_covergroup_unsup.v:126:25: Ignoring unsupported: cover '[->' 126 | { bins bts2 = ( 3 [->5:6] ) ; } | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:126:19: Unsupported: cover bin trans list +%Warning-COVERIGN: t/t_covergroup_unsup.v:126:19: Ignoring unsupported: cover bin trans list 126 | { bins bts2 = ( 3 [->5:6] ) ; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:127:25: Unsupported: cover '[=' +%Warning-COVERIGN: t/t_covergroup_unsup.v:127:25: Ignoring unsupported: cover '[=' 127 | { bins bts2 = ( 3 [=5] ) ; } | ^~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:127:19: Unsupported: cover bin trans list +%Warning-COVERIGN: t/t_covergroup_unsup.v:127:19: Ignoring unsupported: cover bin trans list 127 | { bins bts2 = ( 3 [=5] ) ; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:128:25: Unsupported: cover '[=' +%Warning-COVERIGN: t/t_covergroup_unsup.v:128:25: Ignoring unsupported: cover '[=' 128 | { bins bts2 = ( 3 [=5:6] ) ; } | ^~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:128:19: Unsupported: cover bin trans list +%Warning-COVERIGN: t/t_covergroup_unsup.v:128:19: Ignoring unsupported: cover bin trans list 128 | { bins bts2 = ( 3 [=5:6] ) ; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:132:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:132:4: Ignoring unsupported: covergroup 132 | covergroup cg_cross_bins; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:134:23: Unsupported: coverage select expression 'binsof' +%Warning-COVERIGN: t/t_covergroup_unsup.v:134:23: Ignoring unsupported: coverage select expression 'binsof' 134 | bins bin_a = binsof(a); | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:134:10: Unsupported: coverage cross bin +%Warning-COVERIGN: t/t_covergroup_unsup.v:134:10: Ignoring unsupported: coverage cross bin 134 | bins bin_a = binsof(a); | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:135:24: Unsupported: coverage select expression 'binsof' +%Warning-COVERIGN: t/t_covergroup_unsup.v:135:24: Ignoring unsupported: coverage select expression 'binsof' 135 | bins bin_ai = binsof(a) iff (!rst); | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:135:34: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:135:34: Ignoring unsupported: cover 'iff' 135 | bins bin_ai = binsof(a) iff (!rst); | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:135:10: Unsupported: coverage cross bin +%Warning-COVERIGN: t/t_covergroup_unsup.v:135:10: Ignoring unsupported: coverage cross bin 135 | bins bin_ai = binsof(a) iff (!rst); | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:136:23: Unsupported: coverage select expression 'binsof' +%Warning-COVERIGN: t/t_covergroup_unsup.v:136:23: Ignoring unsupported: coverage select expression 'binsof' 136 | bins bin_c = binsof(cp.x); | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:136:10: Unsupported: coverage cross bin +%Warning-COVERIGN: t/t_covergroup_unsup.v:136:10: Ignoring unsupported: coverage cross bin 136 | bins bin_c = binsof(cp.x); | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:137:24: Unsupported: coverage select expression 'binsof' +%Warning-COVERIGN: t/t_covergroup_unsup.v:137:24: Ignoring unsupported: coverage select expression 'binsof' 137 | bins bin_na = ! binsof(a); | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:137:10: Unsupported: coverage cross bin +%Warning-COVERIGN: t/t_covergroup_unsup.v:137:10: Ignoring unsupported: coverage cross bin 137 | bins bin_na = ! binsof(a); | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:139:33: Unsupported: coverage select expression 'intersect' +%Warning-COVERIGN: t/t_covergroup_unsup.v:139:33: Ignoring unsupported: coverage select expression 'intersect' 139 | bins bin_d = binsof(a) intersect { b }; | ^~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:139:10: Unsupported: coverage cross bin +%Warning-COVERIGN: t/t_covergroup_unsup.v:139:10: Ignoring unsupported: coverage cross bin 139 | bins bin_d = binsof(a) intersect { b }; | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:140:34: Unsupported: coverage select expression 'intersect' +%Warning-COVERIGN: t/t_covergroup_unsup.v:140:34: Ignoring unsupported: coverage select expression 'intersect' 140 | bins bin_nd = ! binsof(a) intersect { b }; | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:140:10: Unsupported: coverage cross bin +%Warning-COVERIGN: t/t_covergroup_unsup.v:140:10: Ignoring unsupported: coverage cross bin 140 | bins bin_nd = ! binsof(a) intersect { b }; | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:142:23: Unsupported: coverage select expression with +%Warning-COVERIGN: t/t_covergroup_unsup.v:142:23: Ignoring unsupported: coverage select expression with 142 | bins bin_e = with (a); | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:142:10: Unsupported: coverage cross bin +%Warning-COVERIGN: t/t_covergroup_unsup.v:142:10: Ignoring unsupported: coverage cross bin 142 | bins bin_e = with (a); | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:143:23: Unsupported: coverage select expression with +%Warning-COVERIGN: t/t_covergroup_unsup.v:143:23: Ignoring unsupported: coverage select expression with 143 | bins bin_e = ! with (a); | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:143:10: Unsupported: coverage cross bin +%Warning-COVERIGN: t/t_covergroup_unsup.v:143:10: Ignoring unsupported: coverage cross bin 143 | bins bin_e = ! with (a); | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:145:26: Unsupported: coverage select expression 'binsof' +%Warning-COVERIGN: t/t_covergroup_unsup.v:145:26: Ignoring unsupported: coverage select expression 'binsof' 145 | bins bin_par = (binsof(a)); | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:145:10: Unsupported: coverage cross bin +%Warning-COVERIGN: t/t_covergroup_unsup.v:145:10: Ignoring unsupported: coverage cross bin 145 | bins bin_par = (binsof(a)); | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:146:25: Unsupported: coverage select expression 'binsof' +%Warning-COVERIGN: t/t_covergroup_unsup.v:146:25: Ignoring unsupported: coverage select expression 'binsof' 146 | bins bin_and = binsof(a) && binsof(b); | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:146:38: Unsupported: coverage select expression 'binsof' +%Warning-COVERIGN: t/t_covergroup_unsup.v:146:38: Ignoring unsupported: coverage select expression 'binsof' 146 | bins bin_and = binsof(a) && binsof(b); | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:146:35: Unsupported: coverage select expression '&&' +%Warning-COVERIGN: t/t_covergroup_unsup.v:146:35: Ignoring unsupported: coverage select expression '&&' 146 | bins bin_and = binsof(a) && binsof(b); | ^~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:146:10: Unsupported: coverage cross bin +%Warning-COVERIGN: t/t_covergroup_unsup.v:146:10: Ignoring unsupported: coverage cross bin 146 | bins bin_and = binsof(a) && binsof(b); | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:147:24: Unsupported: coverage select expression 'binsof' +%Warning-COVERIGN: t/t_covergroup_unsup.v:147:24: Ignoring unsupported: coverage select expression 'binsof' 147 | bins bin_or = binsof(a) || binsof(b); | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:147:37: Unsupported: coverage select expression 'binsof' +%Warning-COVERIGN: t/t_covergroup_unsup.v:147:37: Ignoring unsupported: coverage select expression 'binsof' 147 | bins bin_or = binsof(a) || binsof(b); | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:147:34: Unsupported: coverage select expression '||' +%Warning-COVERIGN: t/t_covergroup_unsup.v:147:34: Ignoring unsupported: coverage select expression '||' 147 | bins bin_or = binsof(a) || binsof(b); | ^~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:147:10: Unsupported: coverage cross bin +%Warning-COVERIGN: t/t_covergroup_unsup.v:147:10: Ignoring unsupported: coverage cross bin 147 | bins bin_or = binsof(a) || binsof(b); | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:133:7: Unsupported: cross +%Warning-COVERIGN: t/t_covergroup_unsup.v:133:7: Ignoring unsupported: cover cross 133 | cross a, b { | ^~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:151:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:151:4: Ignoring unsupported: covergroup 151 | covergroup cg_more extends cg_empty; | ^~~~~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_covergroup_unsup.v b/test_regress/t/t_covergroup_unsup.v index 1d8dcade6..04d78b154 100644 --- a/test_regress/t/t_covergroup_unsup.v +++ b/test_regress/t/t_covergroup_unsup.v @@ -53,7 +53,7 @@ module t (/*AUTOARG*/ covergroup cg_bracket; {} endgroup - covergroup cg_bracket; + covergroup cg_bracket2; { option.name = "option"; } endgroup covergroup cg_cp; @@ -90,7 +90,7 @@ module t (/*AUTOARG*/ covergroup cg_cross3; cross a, b { option.comment = "cross"; option.weight = 12; } endgroup - covergroup cg_cross3; + covergroup cg_cross4; cross a, b { function void crossfunc; endfunction; } endgroup covergroup cg_cross_id; diff --git a/test_regress/t/t_covergroup_unsup_ign.py b/test_regress/t/t_covergroup_unsup_ign.py new file mode 100755 index 000000000..47c0cd627 --- /dev/null +++ b/test_regress/t/t_covergroup_unsup_ign.py @@ -0,0 +1,17 @@ +#!/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('vlt') +test.top_filename = "t/t_covergroup_unsup.v" + +test.lint(verilator_flags2=['--assert --coverage --Wno-COVERIGN']) + +test.passes() diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index 689bab73a..fc093919b 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -1,99 +1,119 @@ module Vt_debug_emitv_t; input logic clk; input logic in; - typedef - ???? // ENUMDTYPE 't.e_t' - - ???? // ENUMITEM 'ZERO' - 32'h0 - ???? // ENUMITEM 'ONE' - 32'h1int signed [31:0] struct packed - {int signed [31:0] a;}logic signed [2:0] struct - {logic signed [2:0] a;}logicunion - {logic a;}struct packed - {int signed [31:0] a;}bit [31:0] const struct packed - {int signed [31:0] a;}const struct packed - {int signed [31:0] a;}[0:2]struct - {logic signed [2:0] a;}union - {logic a;}int signed [31:0] int signed [31:0] [0:2]logic [15:0] logic [15:0] logic [15:0] int signed [31:0] int signed [31:0] int signed [31:0] - ???? // QUEUEDTYPE - int signed [31:0] string - ???? // ASSOCARRAYDTYPE - int signed [31:0] - ???? // DYNARRAYDTYPE - int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] real signedstringIData [31:0] logic signed [31:0] int signed [31:0] e_t; - typedef struct packed - {int signed [31:0] a;}logic signed [2:0] struct - {logic signed [2:0] a;}logicunion - {logic a;}struct packed - {int signed [31:0] a;}bit [31:0] const struct packed - {int signed [31:0] a;}const struct packed - {int signed [31:0] a;}[0:2]struct - {logic signed [2:0] a;}union - {logic a;}int signed [31:0] int signed [31:0] [0:2]logic [15:0] logic [15:0] logic [15:0] int signed [31:0] int signed [31:0] int signed [31:0] - ???? // QUEUEDTYPE - int signed [31:0] string - ???? // ASSOCARRAYDTYPE - int signed [31:0] - ???? // DYNARRAYDTYPE - int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] real signedstringIData [31:0] logic signed [31:0] int signed [31:0] ps_t; - typedef struct - {logic signed [2:0] a;}logicunion - {logic a;}struct packed - {int signed [31:0] a;}bit [31:0] const struct packed - {int signed [31:0] a;}const struct packed - {int signed [31:0] a;}[0:2]struct - {logic signed [2:0] a;}union - {logic a;}int signed [31:0] int signed [31:0] [0:2]logic [15:0] logic [15:0] logic [15:0] int signed [31:0] int signed [31:0] int signed [31:0] - ???? // QUEUEDTYPE - int signed [31:0] string - ???? // ASSOCARRAYDTYPE - int signed [31:0] - ???? // DYNARRAYDTYPE - int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] real signedstringIData [31:0] logic signed [31:0] int signed [31:0] us_t; - typedef union - {logic a;}struct packed - {int signed [31:0] a;}bit [31:0] const struct packed - {int signed [31:0] a;}const struct packed - {int signed [31:0] a;}[0:2]struct - {logic signed [2:0] a;}union - {logic a;}int signed [31:0] int signed [31:0] [0:2]logic [15:0] logic [15:0] logic [15:0] int signed [31:0] int signed [31:0] int signed [31:0] - ???? // QUEUEDTYPE - int signed [31:0] string - ???? // ASSOCARRAYDTYPE - int signed [31:0] - ???? // DYNARRAYDTYPE - int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] real signedstringIData [31:0] logic signed [31:0] int signed [31:0] union_t; - struct packed - {int signed [31:0] a;} ps[0:2]; - struct - {logic signed [2:0] a;} us; - union - {logic a;} unu; - int signed [31:0] array[0:2]; + typedef enum logic [2:0] { + ZERO = 3'h0, + ONE = 3'h1 + } e_t; + typedef struct packed { + logic [2:0] a; + } ps_t; + typedef struct { + logic signed [2:0] a; + } us_t; + typedef union { + logic a; + } union_t; + const struct packed { + logic [2:0] a; + } ps[0:2]; + struct { + logic signed [2:0] a; + } us; + union { + logic a; + } unu; + integer signed i1; + int signed array[0:2]; initial begin array = '{0:32'sh1, 1:32'sh2, 2:32'sh3}; end + logic [63:32] downto_32; + downto_32 = 32'h0; + function ident; + input int signed value; + begin : label0 + ident = value; + disable label0; + end + endfunction + Iface the_ifaces[3:0] (); + initial begin + begin + if ($test$plusargs(40'h48454c4c4f)) begin + $display("Hello argument found."); + end + if ((Pkg::FOO == 'sh0)) begin + $write(""); + end + if ((ZERO == 'sh0)) begin + $write(""); + end + if ($value$plusargs(56'h544553543d2564i1)) begin + $display("value was %~", i1); + end + else begin + $display("+TEST= not found"); + end + if (downto_32[33]) begin + $write(""); + end + if (downto_32[(ident('sh21) - 'h20)[5:0] + 32 + +:'h1]]) begin + $write(""); + end + if ((| downto_32[48:40])) begin + $write(""); + end + if ((| downto_32[57:55])) begin + $write(""); + end + if ((| downto_32[60:54])) begin + $write(""); + end + if (the_ifaces[2].ifsig) begin + $write(""); + end + end + end + bit [6:5] [4:3] [2:1] arraymanyd[10:11][12:13][14:15]; logic [15:0] pubflat; logic [15:0] pubflat_r; logic [15:0] pubflat_w; assign pubflat_w = pubflat; - int signed [31:0] fd; - int signed [31:0] i; - - ???? // QUEUEDTYPE - q; - - ???? // ASSOCARRAYDTYPE - assoc; - - ???? // DYNARRAYDTYPE - dyn; + int signed fd; + int signed i; + int signed q[$]; + int signed qb[$:'sh3]; + int signed assoc[string]; + int signed assocassoc[string][real]; + int signed dyn[]; + typedef struct packed { + logic nn1; + } nested_named_t; + typedef struct packed { + struct packed { + logic nn2; + } nested_anonymous; + struct packed { + logic nn1; + } nested_named; + logic [11:10] nn3; + } nibble_t; + struct packed { + struct packed { + logic nn2; + } nested_anonymous; + struct packed { + logic nn1; + } nested_named; + logic [11:10] nn3; + } [5:4] nibblearray[3:2]; task t; $display("stmt"); endtask function f; - input int signed [31:0] v; + input int signed v; begin : label0 $display("stmt"); f = ((v == 'sh0) ? 'sh63 : ((~ v) + 'sh1)); @@ -102,16 +122,16 @@ module Vt_debug_emitv_t; endfunction initial begin begin : unnamedblk1 - int signed [31:0] other; + int signed other; begin begin : unnamedblk2 - int signed [31:0] i; + int signed i; i = 'sh0; while ((i < 'sh3)) begin begin other = f(i); $display("stmt %~ %~", - iother, other); + i, other); t(); end i = (i + 'h1); @@ -128,7 +148,7 @@ module Vt_debug_emitv_t; $display("stmt"); end end - always @([changed] in) begin + always @( in) begin begin $display("stmt"); end @@ -144,11 +164,13 @@ module Vt_debug_emitv_t; $display("negedge clk, pfr = %x", pubflat_r); end end - int signed [31:0] cyc; - int signed [31:0] fo; - int signed [31:0] sum; - real signed r; + int signed cyc; + int signed fo; + int signed sum; + real r; string str; + int signed mod_val; + int signed mod_res; always @(posedge clk) begin begin cyc <= (cyc + 'sh1); @@ -156,7 +178,7 @@ module Vt_debug_emitv_t; fo = cyc; sub.inc(fosum); sum = sub.f(sum); - $display("[%0t] sum = %~", $timesum, sum); + $display("[%0t] sum = %~", $time, sum); $display("a?= %d", ($c('sh1) ? $c('sh14) : $c('sh1e))); $c(;); @@ -177,7 +199,7 @@ module Vt_debug_emitv_t; $readmemh(fd, array, 'sh0, 'sh0); sum = 'sh0; begin : unnamedblk3 - int signed [31:0] i; + int signed i; i = 'sh0; begin : label0 while ((i < cyc)) begin @@ -248,36 +270,28 @@ module Vt_debug_emitv_t; else begin $display("0"); end - $display("%~%~", $past(cyc)$past(cyc, 'sh1), - $past(cyc, 'sh1)); + $display("%~%~", $past(cyc), $past(cyc, + 'sh1)); str = $sformatf("cyc=%~", cyc); ; $display("str = %@", str); - $display("%% [%t] [%^] to=%o td=%d", $time - $realtime$time$time, $realtime - $time$time, $time$time, $time); + $display("%% [%t] [%^] to=%o td=%d", $time, + $realtime, $time, $time); $sscanf(40'h666f6f3d35, "foo=%d", i); ; $printtimescale; if ((i != 'sh5)) begin $stop; end - sum = - ???? // RAND - ; - sum = - ???? // RAND - 'sha; - sum = - ???? // RAND - ; - sum = - ???? // RAND - 'sha; + sum = $random(); + sum = $random('sha); + sum = $urandom(); + sum = $urandom('sha); if ((PKG_PARAM != 'sh1)) begin $stop; end sub.r = 62.0; + mod_res = (mod_val % 'sh5); $display("%g", $log10(r)); $display("%g", $ln(r)); $display("%g", $exp(r)); @@ -298,7 +312,7 @@ module Vt_debug_emitv_t; $display("%g", $atanh(r)); force sum = 'sha; begin : unnamedblk1_1 - integer signed [31:0] __Vrepeat0; + integer signed __Vrepeat0; __Vrepeat0 = 'sh2; while ((__Vrepeat0 > 32'h0)) begin if ((sum != 'sha)) begin @@ -314,22 +328,30 @@ module Vt_debug_emitv_t; endmodule package Vt_debug_emitv___024unit; class Vt_debug_emitv_Cls; - int signed [31:0] member; + int signed member; member = 'sh1; task method; endtask - task new; - endtask + function new; + endfunction endclass endpackage +interface Vt_debug_emitv_Iface; + input logic clk; + logic ifsig; + modport mp ( + input logic ifsig + ); +endinterface module Vt_debug_emitv_sub; + input logic clk; task inc; - input int signed [31:0] i; - output int signed [31:0] o; + input int signed i; + output int signed o; o = ({32'h1{{1'h0, i[31:1]}}} + 32'h1); endtask function f; - input int signed [31:0] v; + input int signed v; begin : label0 if ((v == 'sh0)) begin f = 'sh21; @@ -339,8 +361,16 @@ module Vt_debug_emitv_sub; disable label0; end endfunction - real signed r; + real r; endmodule +package Vt_debug_emitv_p; + logic pkgvar; +endpackage package Vt_debug_emitv_Pkg; logic signed [31:0] PKG_PARAM; + typedef enum int signed{ + FOO = 32'h0, + BAR = 32'h1, + BAZ = 32'h2 + } enum_t; endpackage diff --git a/test_regress/t/t_debug_emitv.py b/test_regress/t/t_debug_emitv.py index 6063ac2c8..227968047 100755 --- a/test_regress/t/t_debug_emitv.py +++ b/test_regress/t/t_debug_emitv.py @@ -16,7 +16,16 @@ test.lint( # Likewise XML v_flags=["--lint-only --dumpi-tree 9 --dumpi-V3EmitV 9 --debug-emitv"]) -test.files_identical(test.glob_one(test.obj_dir + "/" + test.vm_prefix + "_*_width.tree.v"), - test.golden_filename) +output_v = test.glob_one(test.obj_dir + "/" + test.vm_prefix + "_*_width.tree.v") + +test.files_identical(output_v, test.golden_filename) + +if test.verbose: + # Print if that the output Verilog is clean + # TODO not yet round-trip clean + test.run(cmd=[os.environ["VERILATOR_ROOT"] + "/bin/verilator", "--lint-only", output_v], + logfile=test.obj_dir + "/sim_roundtrip.log", + fails=True, + verilator_run=True) test.passes() diff --git a/test_regress/t/t_debug_emitv.v b/test_regress/t/t_debug_emitv.v index 989888d8c..c886cb300 100644 --- a/test_regress/t/t_debug_emitv.v +++ b/test_regress/t/t_debug_emitv.v @@ -7,6 +7,12 @@ package Pkg; localparam PKG_PARAM = 1; + + typedef enum int { + FOO = 0, + BAR, + BAZ + } enum_t; endpackage package PkgImp; import Pkg::*; @@ -19,7 +25,9 @@ class Cls; endfunction endclass -interface Iface; +interface Iface ( + input clk +); logic ifsig; modport mp(input ifsig); endinterface @@ -33,7 +41,7 @@ module t (/*AUTOARG*/ // verilator lint_off UNPACKED - typedef enum { + typedef enum [2:0] { ZERO, ONE = 1 } e_t; @@ -52,19 +60,59 @@ module t (/*AUTOARG*/ us_t us; union_t unu; - int array[3]; + integer i1; + int array[3]; initial array = '{1,2,3}; + logic [63:32] downto_32 = '0; - reg [15:0] pubflat /*verilator public_flat_rw @(posedge clk) */; + function automatic int ident(int value); + return value; + endfunction - reg [15:0] pubflat_r; - wire [15:0] pubflat_w = pubflat; - int fd; - int i; + Iface the_ifaces [3:0] (.*); - int q[$]; - int assoc[string]; - int dyn[]; + initial begin + if ($test$plusargs("HELLO")) $display("Hello argument found."); + if (Pkg::FOO == 0) $write(""); + if (ZERO == 0) $write(""); + if ($value$plusargs("TEST=%d", i1)) + $display("value was %d", i1); + else + $display("+TEST= not found"); + if (downto_32[33]) $write(""); + if (downto_32[ident(33)]) $write(""); + if (|downto_32[48:40]) $write(""); + if (|downto_32[55+:3]) $write(""); + if (|downto_32[60-:7]) $write(""); + if (the_ifaces[2].ifsig) $write(""); + end + + bit [6:5][4:3][2:1] arraymanyd[10:11][12:13][14:15]; + + reg [15:0] pubflat /*verilator public_flat_rw @(posedge clk) */; + + reg [15:0] pubflat_r; + wire [15:0] pubflat_w = pubflat; + int fd; + int i; + + int q[$]; + int qb[$ : 3]; + int assoc[string]; + int assocassoc[string][real]; + int dyn[]; + + typedef struct packed { + logic nn1; + } nested_named_t; + typedef struct packed { + struct packed { + logic nn2; + } nested_anonymous; + nested_named_t nested_named; + logic [11:10] nn3; + } nibble_t; + nibble_t [5:4] nibblearray[3:2]; task t; $display("stmt"); @@ -74,7 +122,7 @@ module t (/*AUTOARG*/ return v == 0 ? 99 : ~v + 1; endfunction - sub sub(); + sub sub(.*); initial begin int other; @@ -109,6 +157,8 @@ module t (/*AUTOARG*/ int sum; real r; string str; + int mod_val; + int mod_res; always_ff @ (posedge clk) begin cyc <= cyc + 1; r <= r + 0.01; @@ -179,6 +229,8 @@ module t (/*AUTOARG*/ if (Pkg::PKG_PARAM != 1) $stop; sub.r = 62.0; + mod_res = mod_val % 5; + $display("%g", $log10(r)); $display("%g", $ln(r)); $display("%g", $exp(r)); @@ -204,7 +256,7 @@ module t (/*AUTOARG*/ end endmodule -module sub(); +module sub(input logic clk); task inc(input int i, output int o); o = {1'b0, i[31:1]} + 32'd1; endtask diff --git a/test_regress/t/t_dpi_context.py b/test_regress/t/t_dpi_context.py index 8f4de8825..bcdb2941d 100755 --- a/test_regress/t/t_dpi_context.py +++ b/test_regress/t/t_dpi_context.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(v_flags2=["t/t_dpi_context_c.cpp"]) +test.compile(v_flags2=["t/t_dpi_context_c.cpp", "--debug", "-no-dump-tree"]) test.execute() diff --git a/test_regress/t/t_dpi_context.v b/test_regress/t/t_dpi_context.v index b0e11a23f..d246db028 100644 --- a/test_regress/t/t_dpi_context.v +++ b/test_regress/t/t_dpi_context.v @@ -32,6 +32,7 @@ module sub (input integer inst); import "DPI-C" context function int dpic_save(int value); import "DPI-C" context function int dpic_restore(); import "DPI-C" context function int unsigned dpic_getcontext(); + import "DPI-C" context function int unsigned dpic_get1(); int result; @@ -63,6 +64,11 @@ module sub (input integer inst); if (dpic_restore() != 23+inst) $stop; endtask + function automatic int call_dpic_get1; + int res = dpic_get1(); + return res; + endfunction + int unsigned cntxt1; int unsigned cntxt2; @@ -74,6 +80,7 @@ module sub (input integer inst); end // svContext should be the context of the function declaration, not the context of the function call if (cntxt1 != cntxt2) $stop; + if (call_dpic_get1() != 1) $stop; end endmodule diff --git a/test_regress/t/t_dpi_context_c.cpp b/test_regress/t/t_dpi_context_c.cpp index 9025e9407..9d27e8fd8 100644 --- a/test_regress/t/t_dpi_context_c.cpp +++ b/test_regress/t/t_dpi_context_c.cpp @@ -42,6 +42,7 @@ extern int dpic_line(); extern int dpic_save(int value); extern int dpic_restore(); extern unsigned dpic_getcontext(); +extern unsigned dpic_get1(); } #endif @@ -151,6 +152,8 @@ unsigned dpic_getcontext() { return (unsigned)(uintptr_t)scope; } +unsigned dpic_get1() { return 1; } + void dpic_final() { static int s_once = 0; if (s_once++) return; diff --git a/test_regress/t/t_exprstmt_on_lhs_of_nba.py b/test_regress/t/t_exprstmt_on_lhs_of_nba.py new file mode 100755 index 000000000..319c0ff4a --- /dev/null +++ b/test_regress/t/t_exprstmt_on_lhs_of_nba.py @@ -0,0 +1,16 @@ +#!/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('vlt') + +test.compile() + +test.passes() diff --git a/test_regress/t/t_exprstmt_on_lhs_of_nba.v b/test_regress/t/t_exprstmt_on_lhs_of_nba.v new file mode 100644 index 000000000..e9c7efb4e --- /dev/null +++ b/test_regress/t/t_exprstmt_on_lhs_of_nba.v @@ -0,0 +1,104 @@ +// 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(/*AUTOARG*/ + // Outputs + data_o, + // Inputs + clk, rst_i, write_valid_i, write_front_i, read_valid_i, data_i + ); + + localparam NR_ELEMENTS = 16; + localparam DATAW = 32; + + input clk; + input rst_i; + input write_valid_i; + input write_front_i; + input read_valid_i; + input [31:0] data_i; + output [31:0] data_o; + + reg [31:0] FIFOContent [NR_ELEMENTS-1:0]; + + typedef logic [$clog2(NR_ELEMENTS)-1:0] FIFOPointer_t; + + // verilator lint_off WIDTH + localparam FIFOPointer_t MAX_PTR_VAL = NR_ELEMENTS-1; + // verilator lint_on WIDTH + localparam FIFOPointer_t MIN_PTR_VAL = 0; + localparam FIFOPointer_t PTR_INC = 1; + FIFOPointer_t write_pointer; + FIFOPointer_t read_pointer; + + function FIFOPointer_t nextPointer(input FIFOPointer_t val); + if ($clog2(NR_ELEMENTS) == $clog2(NR_ELEMENTS+1) + && val == MAX_PTR_VAL) + nextPointer = MIN_PTR_VAL; // explicit wrap if NR_ELEMENTS is not a power of 2 + else + nextPointer = val + PTR_INC; + endfunction + + function FIFOPointer_t prevPointer(input FIFOPointer_t val); + if ($clog2(NR_ELEMENTS) == $clog2(NR_ELEMENTS+1) + && val == MIN_PTR_VAL) + prevPointer = MAX_PTR_VAL; // explicit wrap if NR_ELEMENTS is not a power of 2 + else + prevPointer = val - PTR_INC; + endfunction + + reg [$clog2(NR_ELEMENTS)-1:0] level; + reg is_empty; + + always @(posedge clk) begin + if (write_valid_i) + FIFOContent[write_front_i ? (read_valid_i ? read_pointer : prevPointer(read_pointer)) : write_pointer] <= data_i; + end + + assign data_o = FIFOContent[read_pointer]; + + always @(posedge clk) begin + if (rst_i) begin + is_empty <= 1; + end + else if (write_valid_i) begin + is_empty <= 0; + end + else if (read_valid_i && write_pointer == nextPointer(read_pointer)) begin + is_empty <= 1; + end + end + + always @(posedge clk) begin + if (rst_i) begin + level <= 0; + end + else begin + level <= level + (write_valid_i ? 1 : 0) - (read_valid_i ? 1 : 0); + end + end + + always @(posedge clk) begin + if (rst_i) begin + write_pointer <= 0; + end + else if (write_valid_i && !write_front_i) begin + write_pointer <= nextPointer(write_pointer); + end + end + + always @(posedge clk) begin + if (rst_i) begin + read_pointer <= 0; + end + else if (read_valid_i) begin + if (!(write_valid_i && write_front_i))read_pointer <= nextPointer(read_pointer); + end + else if (write_valid_i && write_front_i) begin + read_pointer <= prevPointer(read_pointer); + end + end +endmodule diff --git a/test_regress/t/t_flag_csplit_groups.py b/test_regress/t/t_flag_csplit_groups.py index 8fe9707ba..6ff731c8b 100755 --- a/test_regress/t/t_flag_csplit_groups.py +++ b/test_regress/t/t_flag_csplit_groups.py @@ -87,6 +87,7 @@ test.compile(v_flags2=["--trace", "--output-groups 2", "--output-split-cfuncs 1", "--exe", + "--stats", "../" + test.main_filename], verilator_make_gmake=False) # yapf:disable @@ -122,4 +123,9 @@ test.file_grep(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_classes_ test.file_grep_not(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_classes_Slow_2") test.file_grep_not(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_classes_2") +# Check combine count +test.file_grep(test.stats, r'Node count, CFILE + (\d+)', (191 if test.vltmt else 173)) +test.file_grep(test.stats, r'Makefile targets, VM_CLASSES_FAST + (\d+)', 2) +test.file_grep(test.stats, r'Makefile targets, VM_CLASSES_SLOW + (\d+)', 2) + test.passes() diff --git a/test_regress/t/t_flag_incdir.py b/test_regress/t/t_flag_incdir.py new file mode 100755 index 000000000..a4f25aabb --- /dev/null +++ b/test_regress/t/t_flag_incdir.py @@ -0,0 +1,16 @@ +#!/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('vlt') + +test.lint(verilator_flags2=['+incdir+ignore1+t/tsub+ignore2']) + +test.passes() diff --git a/test_regress/t/t_flag_incdir.v b/test_regress/t/t_flag_incdir.v new file mode 100644 index 000000000..7fbf7d202 --- /dev/null +++ b/test_regress/t/t_flag_incdir.v @@ -0,0 +1,14 @@ +// 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 + +`include "t_flag_f_tsub_inc.v" + +`ifndef GOT_DEF5 +`error "No GOT_DEF5" +`endif + +module t (/*AUTOARG*/); +endmodule diff --git a/test_regress/t/t_flag_values_bad.out b/test_regress/t/t_flag_values_bad.out index 8febf43ce..19b596488 100644 --- a/test_regress/t/t_flag_values_bad.out +++ b/test_regress/t/t_flag_values_bad.out @@ -1,4 +1,5 @@ %Error: --output-split-cfuncs must be >= 0: -1 %Error: --output-split-ctrace must be >= 0: -1 +%Error: --preproc-token-limit must be > 0: 0 %Error: --reloop-limit must be >= 2: -1 %Error: Exiting due to diff --git a/test_regress/t/t_flag_values_bad.py b/test_regress/t/t_flag_values_bad.py index ab8c8bb79..0bebd8379 100755 --- a/test_regress/t/t_flag_values_bad.py +++ b/test_regress/t/t_flag_values_bad.py @@ -11,9 +11,11 @@ import vltest_bootstrap test.scenarios('vlt') -test.lint( - verilator_flags2=["--output-split-cfuncs -1", "--output-split-ctrace -1", "--reloop-limit -1"], - fails=True, - expect_filename=test.golden_filename) +test.lint(verilator_flags2=[ + "--output-split-cfuncs -1", "--output-split-ctrace -1", "--preproc-token-limit 0", + "--reloop-limit -1" +], + fails=True, + expect_filename=test.golden_filename) test.passes() diff --git a/test_regress/t/t_force_assign.py b/test_regress/t/t_force_assign.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_force_assign.py @@ -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() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_force_assign.v b/test_regress/t/t_force_assign.v new file mode 100644 index 000000000..7b3724dd5 --- /dev/null +++ b/test_regress/t/t_force_assign.v @@ -0,0 +1,24 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +module t; + reg [2:0] a = 0; + + initial begin + a = 1; + if (a != 1) $stop; + + force a = 2; + if (a != 2) $stop; + + a = 3; + if (a != 2) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_func_task_bad.out b/test_regress/t/t_func_task_bad.out index 5a85eb04d..3cd03ead0 100644 --- a/test_regress/t/t_func_task_bad.out +++ b/test_regress/t/t_func_task_bad.out @@ -1,4 +1,9 @@ -%Error: t/t_func_task_bad.v:10:11: Illegal call of a task as a function: 'task_as_func' +%Error: t/t_func_task_bad.v:10:11: Cannot call a task/void-function as a function: 'task_as_func' + : ... note: In instance 't' 10 | if (task_as_func(1'b0)) $stop; | ^~~~~~~~~~~~ +%Error: t/t_func_task_bad.v:10:7: Logical operator IF expects a non-complex data type on the If. + : ... note: In instance 't' + 10 | if (task_as_func(1'b0)) $stop; + | ^~ %Error: Exiting due to diff --git a/test_regress/t/t_func_void_bad.out b/test_regress/t/t_func_void_bad.out index e14223a08..37b0f4cb6 100644 --- a/test_regress/t/t_func_void_bad.out +++ b/test_regress/t/t_func_void_bad.out @@ -1,9 +1,12 @@ -%Warning-IGNOREDRETURN: t/t_func_void_bad.v:21:7: Ignoring return value of non-void function (IEEE 1800-2023 13.4.1) - 21 | f1(); +%Warning-IGNOREDRETURN: t/t_func_void_bad.v:24:7: Ignoring return value of non-void function (IEEE 1800-2023 13.4.1) + 24 | f1(); | ^~ ... For warning description see https://verilator.org/warn/IGNOREDRETURN?v=latest ... Use "/* verilator lint_off IGNOREDRETURN */" and lint_on around source to disable this message. -%Warning-IGNOREDRETURN: t/t_func_void_bad.v:24:9: Ignoring return value of non-void function (IEEE 1800-2023 13.4.1) - 24 | c.fi(); +%Warning-IGNOREDRETURN: t/t_func_void_bad.v:27:9: Ignoring return value of non-void function (IEEE 1800-2023 13.4.1) + 27 | c.fi(); | ^~ +%Warning-IGNOREDRETURN: t/t_func_void_bad.v:28:9: Ignoring return value of non-void function (IEEE 1800-2023 13.4.1) + 28 | c.sfi(); + | ^~~ %Error: Exiting due to diff --git a/test_regress/t/t_func_void_bad.v b/test_regress/t/t_func_void_bad.v index 837595812..e9552e58c 100644 --- a/test_regress/t/t_func_void_bad.v +++ b/test_regress/t/t_func_void_bad.v @@ -8,6 +8,9 @@ class Cls; function int fi(); return 10; endfunction + static function int sfi(); + return 10; + endfunction endclass module t; @@ -22,6 +25,7 @@ module t; // c = new; c.fi(); // Bad - ignored result + c.sfi(); // Bad - ignored result // $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_gen_class.py b/test_regress/t/t_gen_class.py new file mode 100755 index 000000000..bd059b0f2 --- /dev/null +++ b/test_regress/t/t_gen_class.py @@ -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() diff --git a/test_regress/t/t_gen_class.v b/test_regress/t/t_gen_class.v new file mode 100644 index 000000000..58628ba58 --- /dev/null +++ b/test_regress/t/t_gen_class.v @@ -0,0 +1,67 @@ +// 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 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 Child; + int ch_value; +endmodule + +module Parent; + for (genvar i = 0; i < 10; i++) begin : gen_child + Child child(); + end +endmodule + +module t; + Parent parent(); + + virtual class ChildAgentBase; + pure virtual task preload(int value); + pure virtual function string name(); + endclass + + ChildAgentBase child_agents[10]; + + for (genvar i = 0; i < 10; i++) begin : gfor + class ChildAgent extends ChildAgentBase; + task automatic preload(int value); + parent.gen_child[i].child.ch_value = value; + endtask + function string name(); + return $sformatf("%m"); + endfunction + endclass + + ChildAgent agent = new(); + + initial child_agents[i] = agent; + end + + task automatic preload_children; + for (int i = 0; i < 10; i++) begin + child_agents[i].preload(i); + end + endtask + + string s; + + initial begin + #1; // Ensure all class instances are initialized + preload_children(); + `checkh(parent.gen_child[3].child.ch_value, 3); + `checkh(parent.gen_child[8].child.ch_value, 8); +`ifdef VERILATOR + // Some legal examples "t.gfor[4].\ChildAgent::name", "t.gfor[4].ChildAgent.name" + `checks(child_agents[4].name(), "t.gfor[4].ChildAgent.name"); +`endif + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_hier_block_int.py b/test_regress/t/t_hier_block_int.py new file mode 100755 index 000000000..0384eccd5 --- /dev/null +++ b/test_regress/t/t_hier_block_int.py @@ -0,0 +1,21 @@ +#!/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=['--stats', '--hierarchical']) + +test.execute() + +test.file_grep(test.obj_dir + "/Vsub/sub.sv", r'^module\s+(\S+)\s+', "sub") +test.file_grep(test.stats, r'HierBlock,\s+Hierarchical blocks\s+(\d+)', 1) + +test.passes() diff --git a/test_regress/t/t_hier_block_int.v b/test_regress/t/t_hier_block_int.v new file mode 100644 index 000000000..8ff804d88 --- /dev/null +++ b/test_regress/t/t_hier_block_int.v @@ -0,0 +1,50 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2025 by Antmicro. 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 + +module t(/*AUTOARG*/ + // inputs + clk +); + input clk; + + byte out1; + shortint out2; + int out3; + longint out4; + integer out5; + time out6; + + sub sub(out1, out2, out3, out4, out5, out6); + + always_ff @(posedge clk) begin + if (out1 == 1 && out2 == 2 && out3 == 3 && out4 == 4 && out5 == 5 && out6 == 6) begin + $write("*-* All Finished *-*\n"); + $finish; + end + else begin + $write("Mismatch\n"); + $stop; + end + end +endmodule + +module sub( + output byte out1, + output shortint out2, + output int out3, + output longint out4, + output integer out5, + output time out6 +); /*verilator hier_block*/ + assign out1 = 1; + assign out2 = 2; + assign out3 = 3; + assign out4 = 4; + assign out5 = 5; + assign out6 = 6; +endmodule diff --git a/test_regress/t/t_hier_block_perf.py b/test_regress/t/t_hier_block_perf.py new file mode 100755 index 000000000..f73b5db4c --- /dev/null +++ b/test_regress/t/t_hier_block_perf.py @@ -0,0 +1,40 @@ +#!/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('vlt_all') +test.init_benchmarksim() +test.cycles = (int(test.benchmark) if test.benchmark else 1000000) +test.sim_time = test.cycles * 10 + 1000 +THREADS = int(os.environ["SIM_THREADS"]) if "SIM_THREADS" in os.environ else 2 + +test.compile(benchmarksim=1, + v_flags2=[ + "+define+SIM_CYCLES=" + str(test.cycles), "--prof-exec", "--hierarchical", + "--stats" + ], + threads=(THREADS if test.vltmt else 1)) + +test.file_grep(test.obj_dir + "/V" + test.name + "__hier.dir/V" + test.name + "__stats.txt", + r'Optimizations, Hierarchical DPI wrappers with costs\s+(\d+)', 3) + +test.execute(all_run_flags=[ + "+verilator+prof+exec+start+2", + " +verilator+prof+exec+window+2", + " +verilator+prof+exec+file+" + test.obj_dir + "/profile_exec.dat", + " +verilator+prof+vlt+file+" + test.obj_dir + "/profile.vlt"]) # yapf:disable + +test.run(cmd=[ + os.environ["VERILATOR_ROOT"] + "/bin/verilator_gantt", test.obj_dir + + "/profile_exec.dat", "--vcd " + test.obj_dir + "/profile_exec.vcd", "| tee " + test.obj_dir + + "/gantt.log" +]) + +test.passes() diff --git a/test_regress/t/t_hier_block_perf.v b/test_regress/t/t_hier_block_perf.v new file mode 100644 index 000000000..59470bb03 --- /dev/null +++ b/test_regress/t/t_hier_block_perf.v @@ -0,0 +1,314 @@ +// 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 + +// based on t_gate_ormux + +`ifndef HIER_CORES + `define HIER_CORES 3 +`endif + +`ifndef MAIN_CORES + `define MAIN_CORES 1 +`endif + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + generate + for (genvar i = 0; i < `MAIN_CORES; ++i) NonHierCore mainCore(clk); + endgenerate + + generate + for (genvar i = 0; i < `HIER_CORES; ++i) Core hierCore(clk); + endgenerate +endmodule + +module Core(input clk); /* verilator hier_block */ + reg [63:0] crc; + logic [31:0] rdata; + logic [31:0] rdata2; + wire [31:0] wdata = crc[31:0]; + wire [15:0] sel = {11'h0, crc[36:32]}; + wire we = crc[48]; + + Test test ( + // Outputs + .rdata (rdata[31:0]), + .rdata2 (rdata2[31:0]), + // Inputs + .clk (clk), + .we (we), + .sel (sel[15:0]), + .wdata (wdata[31:0])); + wire [63:0] result = {rdata2, rdata}; + + Check check(.clk(clk), .crc(crc), .result(result), .rdata(rdata), .rdata2(rdata2)); +endmodule + +module NonHierCore(input clk); + reg [63:0] crc; + logic [31:0] rdata; + logic [31:0] rdata2; + wire [31:0] wdata = crc[31:0]; + wire [15:0] sel = {11'h0, crc[36:32]}; + wire we = crc[48]; + + Test test ( + // Outputs + .rdata (rdata[31:0]), + .rdata2 (rdata2[31:0]), + // Inputs + .clk (clk), + .we (we), + .sel (sel[15:0]), + .wdata (wdata[31:0])); + wire [63:0] result = {rdata2, rdata}; + + Check check(.clk(clk), .crc(crc), .result(result), .rdata(rdata), .rdata2(rdata2)); +endmodule + +module Check( + input clk, + output reg [63:0] crc, + input wire [63:0] result, + input logic [31:0] rdata, + input logic [31:0] rdata2 + ); + integer cyc = 0; + reg [63:0] sum; + + always @ (posedge clk) begin +`ifdef TEST_VERBOSE + $write("[%0t] cyc==%0d crc=%x result=%x\n", $time, cyc, crc, result); +`endif + cyc <= cyc + 1; + crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]}; + sum <= result ^ {sum[62:0], sum[63] ^ sum[2] ^ sum[0]}; + if (rdata2 != rdata) $stop; + if (cyc==0) begin + // Setup + crc <= 64'h5aef0c8d_d70a4497; + sum <= '0; + end + else if (cyc<10) begin + sum <= '0; + end + else if (cyc == 99) begin + $write("[%0t] cyc==%0d crc=%x sum=%x\n", $time, cyc, crc, sum); + if (crc !== 64'hc77bb9b3784ea091) $stop; +`define EXPECTED_SUM 64'h8977713eb467bc86 + if (sum !== `EXPECTED_SUM) $stop; + end + else if (cyc == `SIM_CYCLES) begin + $write("[%0t] cyc==%0d crc=%x sum=%x\n", $time, cyc, crc, sum); + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule + +module Test(/*AUTOARG*/ + // Outputs + rdata, rdata2, + // Inputs + clk, we, sel, wdata + ); + input clk; + input we; + input [15:0] sel; + input [31:0] wdata; + output logic [31:0] rdata; + output logic [31:0] rdata2; + + logic we_d1r; + logic [15:0] sel_d1r; + logic [31:0] wdata_d1r; + always_ff @ (posedge clk) begin + we_d1r <= we; + sel_d1r <= sel; + wdata_d1r <= wdata; + end + + reg [31:0] csr0000; + reg [31:0] csr0001; + reg [31:0] csr0002; + reg [31:0] csr0003; + reg [31:0] csr0004; + reg [31:0] csr0005; + reg [31:0] csr0006; + reg [31:0] csr0007; + reg [31:0] csr0008; + reg [31:0] csr0009; + reg [31:0] csr000a; + reg [31:0] csr000b; + reg [31:0] csr000c; + reg [31:0] csr000d; + reg [31:0] csr000e; + reg [31:0] csr000f; + wire [31:0] csr0010 = 32'h33675230; + wire [31:0] csr0011 = 32'h00fa2144; + wire [31:0] csr0012 = 32'h6a5e8e10; + wire [31:0] csr0013 = 32'h000a5b5e; + wire [31:0] csr0014 = 32'h002fe51b; + wire [31:0] csr0015 = 32'h00027e00; + wire [31:0] csr0016 = 32'h0000e3c0; + wire [31:0] csr0017 = 32'h00efcf16; + wire [31:0] csr0018 = 32'h007a2600; + wire [31:0] csr0019 = 32'h0a4a9f10; + wire [31:0] csr001a = 32'h7d789de3; + wire [31:0] csr001b = 32'h40f655f9; + wire [31:0] csr001c = 32'hadad01f4; + wire [31:0] csr001d = 32'h02e7b33c; + wire [31:0] csr001e = 32'h12101533; + wire [31:0] csr001f = 32'h2cc1cce5; + initial begin + csr0000 = 32'he172d365; + csr0001 = 32'h35cc25e2; + csr0002 = 32'haf48436e; + csr0003 = 32'h135e55e4; + csr0004 = 32'h5fd6e48a; + csr0005 = 32'hb07d34ad; + csr0006 = 32'h2aa05deb; + csr0007 = 32'hfe97b680; + csr0008 = 32'h960f20bb; + csr0009 = 32'h251129f0; + csr000a = 32'hef3d2f93; + csr000b = 32'hef4bc127; + csr000c = 32'h3dfecb10; + csr000d = 32'h1b4690f5; + csr000e = 32'ha07822ab; + csr000f = 32'hf817cbf6; + end + + always_ff @ (posedge clk) begin + if (we_d1r && sel_d1r == 16'h0000) csr0000 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0001) csr0001 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0002) csr0002 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0003) csr0003 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0004) csr0004 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0005) csr0005 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0006) csr0006 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0007) csr0007 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0008) csr0008 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0009) csr0009 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h000a) csr000a <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h000b) csr000b <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h000c) csr000c <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h000d) csr000d <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h000e) csr000e <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h000f) csr000f <= wdata_d1r; + end + + wire dec0000 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec0001 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec0002 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec0003 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + wire dec0004 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec0005 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec0006 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec0007 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + wire dec0008 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec0009 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec000a = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec000b = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + wire dec000c = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec000d = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec000e = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec000f = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + wire dec0010 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec0011 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec0012 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec0013 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + wire dec0014 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec0015 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec0016 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec0017 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + wire dec0018 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec0019 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec001a = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec001b = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + wire dec001c = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec001d = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec001e = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec001f = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + + assign rdata = (32'h0 + | {32{dec0000}} & csr0000 + | {32{dec0001}} & csr0001 + | {32{dec0002}} & csr0002 + | {32{dec0003}} & csr0003 + | {32{dec0004}} & csr0004 + | {32{dec0005}} & csr0005 + | {32{dec0006}} & csr0006 + | {32{dec0007}} & csr0007 + | {32{dec0008}} & csr0008 + | {32{dec0009}} & csr0009 + | {32{dec000a}} & csr000a + | {32{dec000b}} & csr000b + | {32{dec000c}} & csr000c + | {32{dec000d}} & csr000d + | {32{dec000e}} & csr000e + | {32{dec000f}} & csr000f + | {32{dec0010}} & csr0010 + | {32{dec0011}} & csr0011 + | {32{dec0012}} & csr0012 + | {32{dec0013}} & csr0013 + | {32{dec0014}} & csr0014 + | {32{dec0015}} & csr0015 + | {32{dec0016}} & csr0016 + | {32{dec0017}} & csr0017 + | {32{dec0018}} & csr0018 + | {32{dec0019}} & csr0019 + | {32{dec001a}} & csr001a + | {32{dec001b}} & csr001b + | {32{dec001c}} & csr001c + | {32{dec001d}} & csr001d + | {32{dec001e}} & csr001e + | {32{dec001f}} & csr001f + ); + + always_comb begin + case (sel_d1r) + 16'h0000: rdata2 = csr0000; + 16'h0001: rdata2 = csr0001; + 16'h0002: rdata2 = csr0002; + 16'h0003: rdata2 = csr0003; + 16'h0004: rdata2 = csr0004; + 16'h0005: rdata2 = csr0005; + 16'h0006: rdata2 = csr0006; + 16'h0007: rdata2 = csr0007; + 16'h0008: rdata2 = csr0008; + 16'h0009: rdata2 = csr0009; + 16'h000a: rdata2 = csr000a; + 16'h000b: rdata2 = csr000b; + 16'h000c: rdata2 = csr000c; + 16'h000d: rdata2 = csr000d; + 16'h000e: rdata2 = csr000e; + 16'h000f: rdata2 = csr000f; + 16'h0010: rdata2 = csr0010; + 16'h0011: rdata2 = csr0011; + 16'h0012: rdata2 = csr0012; + 16'h0013: rdata2 = csr0013; + 16'h0014: rdata2 = csr0014; + 16'h0015: rdata2 = csr0015; + 16'h0016: rdata2 = csr0016; + 16'h0017: rdata2 = csr0017; + 16'h0018: rdata2 = csr0018; + 16'h0019: rdata2 = csr0019; + 16'h001a: rdata2 = csr001a; + 16'h001b: rdata2 = csr001b; + 16'h001c: rdata2 = csr001c; + 16'h001d: rdata2 = csr001d; + 16'h001e: rdata2 = csr001e; + 16'h001f: rdata2 = csr001f; + default: rdata2 = 0; + endcase + end + +endmodule diff --git a/test_regress/t/t_hier_block_struct_nohier.py b/test_regress/t/t_hier_block_struct_nohier.py new file mode 100755 index 000000000..8545961d7 --- /dev/null +++ b/test_regress/t/t_hier_block_struct_nohier.py @@ -0,0 +1,19 @@ +#!/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') +test.top_filename = "t/t_hier_block_struct.v" + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_implements_nested.py b/test_regress/t/t_implements_nested.py new file mode 100755 index 000000000..c2d985114 --- /dev/null +++ b/test_regress/t/t_implements_nested.py @@ -0,0 +1,16 @@ +#!/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('linter') + +test.lint() + +test.passes() diff --git a/test_regress/t/t_implements_nested.v b/test_regress/t/t_implements_nested.v new file mode 100644 index 000000000..87fc8d603 --- /dev/null +++ b/test_regress/t/t_implements_nested.v @@ -0,0 +1,16 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class Cls; + // IEEE 2023 only disallows nested interface inside another interface, not + // class + interface class good_can_nest; + endclass +endclass + +module t (/*AUTOARG*/); + Cls c; +endmodule diff --git a/test_regress/t/t_implements_nested_bad.out b/test_regress/t/t_implements_nested_bad.out index 1121c001f..fab3573e0 100644 --- a/test_regress/t/t_implements_nested_bad.out +++ b/test_regress/t/t_implements_nested_bad.out @@ -1,5 +1,4 @@ -%Error-UNSUPPORTED: t/t_implements_nested_bad.v:8:14: Unsupported: class within class - 8 | interface class bad_cannot_nest; - | ^~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: t/t_implements_nested_bad.v:9:17: Interface class shall not be nested within another interface class. (IEEE 1800-2023 8.26) + 9 | interface class bad_cannot_nest; + | ^~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_implements_nested_bad.v b/test_regress/t/t_implements_nested_bad.v index 119033fa3..be483fb27 100644 --- a/test_regress/t/t_implements_nested_bad.v +++ b/test_regress/t/t_implements_nested_bad.v @@ -5,7 +5,9 @@ // SPDX-License-Identifier: CC0-1.0 class Cls; - interface class bad_cannot_nest; + interface class inte; + interface class bad_cannot_nest; + endclass endclass endclass diff --git a/test_regress/t/t_interface_virtual_bad.out b/test_regress/t/t_interface_virtual_bad.out index 7149f62ba..84ac73793 100644 --- a/test_regress/t/t_interface_virtual_bad.out +++ b/test_regress/t/t_interface_virtual_bad.out @@ -1,4 +1,4 @@ -%Error: t/t_interface_virtual_bad.v:31:12: Operator ASSIGN expected 'PBus' interface on Assign RHS but 'q8__Viftop' is a different interface ('QBus'). +%Error: t/t_interface_virtual_bad.v:31:12: Operator ASSIGN expected 'PBus' interface on Assign RHS but 'q8' is a different interface ('QBus'). : ... note: In instance 't' 31 | v8 = q8; | ^~ @@ -6,7 +6,7 @@ : ... note: In instance 't' 35 | v8 = v8_phy; | ^~~~~~ -%Error: t/t_interface_virtual_bad.v:37:17: Operator ASSIGN expected non-interface on Assign RHS but 'p8__Viftop' is an interface. +%Error: t/t_interface_virtual_bad.v:37:17: Operator ASSIGN expected non-interface on Assign RHS but 'p8' is an interface. : ... note: In instance 't' 37 | data = p8.phy; | ^~~ @@ -18,7 +18,7 @@ : ... note: In instance 't' 39 | data = v8; | ^~ -%Error: t/t_interface_virtual_bad.v:40:14: Operator ASSIGN expected non-interface on Assign RHS but 'p8__Viftop' is an interface. +%Error: t/t_interface_virtual_bad.v:40:14: Operator ASSIGN expected non-interface on Assign RHS but 'p8' is an interface. : ... note: In instance 't' 40 | data = p8; | ^~ diff --git a/test_regress/t/t_interface_wire_bad.out b/test_regress/t/t_interface_wire_bad.out index c792bf9b6..e98a4c000 100644 --- a/test_regress/t/t_interface_wire_bad.out +++ b/test_regress/t/t_interface_wire_bad.out @@ -1,4 +1,4 @@ -%Error: t/t_interface_wire_bad.v:17:20: Operator ASSIGNW expected non-interface on Assign RHS but 'a__Viftop' is an interface. +%Error: t/t_interface_wire_bad.v:17:20: Operator ASSIGNW expected non-interface on Assign RHS but 'a' is an interface. : ... note: In instance 't' 17 | wire wbad = sub.a; | ^ diff --git a/test_regress/t/t_json_only_tag.out b/test_regress/t/t_json_only_tag.out index cb03e77cd..20db5ea20 100644 --- a/test_regress/t/t_json_only_tag.out +++ b/test_regress/t/t_json_only_tag.out @@ -7,7 +7,7 @@ {"type":"VAR","name":"foo_op","addr":"(I)","loc":"d,16:11,16:17","dtypep":"(G)","origName":"foo_op","isSc":false,"isPrimaryIO":false,"direction":"OUTPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"TYPEDEF","name":"my_struct","addr":"(J)","loc":"d,25:6,25:15","dtypep":"(K)","attrPublic":false,"childDTypep": [],"attrsp": []}, {"type":"CELL","name":"itop","addr":"(L)","loc":"d,29:8,29:12","origName":"itop","recursive":false,"modp":"(M)","pinsp": [],"paramsp": [],"rangep": [],"intfRefsp": []}, - {"type":"VAR","name":"itop__Viftop","addr":"(N)","loc":"d,29:8,29:12","dtypep":"(O)","origName":"itop__Viftop","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"IFACEREF","dtypeName":"","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, + {"type":"VAR","name":"itop","addr":"(N)","loc":"d,29:8,29:12","dtypep":"(O)","origName":"itop__Viftop","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"IFACEREF","dtypeName":"","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"VAR","name":"this_struct","addr":"(P)","loc":"d,31:14,31:25","dtypep":"(Q)","origName":"this_struct","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"VAR","dtypeName":"","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"VAR","name":"dotted","addr":"(R)","loc":"d,33:16,33:22","dtypep":"(S)","origName":"dotted","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"WIRE","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"ASSIGNW","name":"","addr":"(T)","loc":"d,33:23,33:24","dtypep":"(S)", @@ -69,10 +69,10 @@ {"type":"BASICDTYPE","name":"logic","addr":"(RB)","loc":"d,24:7,24:12","dtypep":"(RB)","keyword":"logic","generic":false,"rangep": []}, {"type":"STRUCTDTYPE","name":"m.my_struct","addr":"(K)","loc":"d,20:12,20:18","dtypep":"(K)","packed":true,"isFourstate":true,"generic":false,"classOrPackagep":"UNLINKED", "membersp": [ - {"type":"MEMBERDTYPE","name":"clk","addr":"(SB)","loc":"d,21:19,21:22","dtypep":"(SB)","generic":false,"childDTypep": [],"valuep": []}, - {"type":"MEMBERDTYPE","name":"k","addr":"(TB)","loc":"d,22:19,22:20","dtypep":"(TB)","generic":false,"childDTypep": [],"valuep": []}, - {"type":"MEMBERDTYPE","name":"enable","addr":"(UB)","loc":"d,23:19,23:25","dtypep":"(UB)","generic":false,"childDTypep": [],"valuep": []}, - {"type":"MEMBERDTYPE","name":"data","addr":"(VB)","loc":"d,24:19,24:23","dtypep":"(VB)","generic":false,"childDTypep": [],"valuep": []} + {"type":"MEMBERDTYPE","name":"clk","addr":"(SB)","loc":"d,21:19,21:22","dtypep":"(OB)","isConstrainedRand":false,"name":"clk","tag":"this is clk","generic":false,"refDTypep":"(OB)","childDTypep": [],"valuep": []}, + {"type":"MEMBERDTYPE","name":"k","addr":"(TB)","loc":"d,22:19,22:20","dtypep":"(PB)","isConstrainedRand":false,"name":"k","tag":"","generic":false,"refDTypep":"(PB)","childDTypep": [],"valuep": []}, + {"type":"MEMBERDTYPE","name":"enable","addr":"(UB)","loc":"d,23:19,23:25","dtypep":"(QB)","isConstrainedRand":false,"name":"enable","tag":"enable","generic":false,"refDTypep":"(QB)","childDTypep": [],"valuep": []}, + {"type":"MEMBERDTYPE","name":"data","addr":"(VB)","loc":"d,24:19,24:23","dtypep":"(RB)","isConstrainedRand":false,"name":"data","tag":"data","generic":false,"refDTypep":"(RB)","childDTypep": [],"valuep": []} ]}, {"type":"IFACEREFDTYPE","name":"","addr":"(O)","loc":"d,29:8,29:12","dtypep":"(O)","isPortDecl":false,"isVirtual":false,"cellName":"itop","ifaceName":"ifc","modportName":"","generic":false,"ifacep":"UNLINKED","cellp":"(L)","modportp":"UNLINKED","paramsp": []}, {"type":"BASICDTYPE","name":"logic","addr":"(S)","loc":"d,31:27,31:28","dtypep":"(S)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, diff --git a/test_regress/t/t_langext_1d.py b/test_regress/t/t_langext_1d.py new file mode 100755 index 000000000..6b8d4bc93 --- /dev/null +++ b/test_regress/t/t_langext_1d.py @@ -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') +test.top_filename = "t/t_langext_1.v" + +# This is a compile only test. +test.compile(v_flags2=["+verilog2001ext+.v"]) + +test.passes() diff --git a/test_regress/t/t_langext_1d_bad.py b/test_regress/t/t_langext_1d_bad.py new file mode 100755 index 000000000..706e9ab40 --- /dev/null +++ b/test_regress/t/t_langext_1d_bad.py @@ -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('linter') +test.top_filename = "t/t_langext_1.v" + +# This is a lint only test. +test.lint(v_flags2=["+verilog1995ext+.v"], fails=True) + +test.passes() diff --git a/test_regress/t/t_lint_implicit_func_bad.out b/test_regress/t/t_lint_implicit_func_bad.out index 6d790a04c..78301f0c4 100644 --- a/test_regress/t/t_lint_implicit_func_bad.out +++ b/test_regress/t/t_lint_implicit_func_bad.out @@ -1,4 +1,5 @@ -%Error: t/t_lint_implicit_func_bad.v:12:11: Illegal call of a task as a function: 'imp_func_conflict' +%Error: t/t_lint_implicit_func_bad.v:12:11: Cannot call a task/void-function as a function: 'imp_func_conflict' + : ... note: In instance 't' 12 | assign imp_func_conflict = 1'b1; | ^~~~~~~~~~~~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_lint_importstar_bad.v b/test_regress/t/t_lint_importstar_bad.v index d61e12f78..752d64a64 100644 --- a/test_regress/t/t_lint_importstar_bad.v +++ b/test_regress/t/t_lint_importstar_bad.v @@ -5,7 +5,7 @@ // SPDX-License-Identifier: CC0-1.0 package defs; - int sig; + localparam PAR = 1; endpackage import defs::*; diff --git a/test_regress/t/t_module_input_default_value.v b/test_regress/t/t_module_input_default_value.v index 509dd0ad4..e14c222f4 100644 --- a/test_regress/t/t_module_input_default_value.v +++ b/test_regress/t/t_module_input_default_value.v @@ -43,6 +43,15 @@ module dut_default_input_logic32 endmodule +module dut_default_input_wire32 + ( + input wire [31:0] i = 32'h12345678, + output logic [31:0] o + ); + assign o = i; +endmodule + + module t (/*AUTOARG*/ // Inputs @@ -104,6 +113,11 @@ module t (.i(), // open .o(dut_logic32_o_open)); + logic [31:0] dut_wire32_o_open; + dut_default_input_wire32 u_dut_wire32_open + (.i(), // open + .o(dut_wire32_o_open)); + // 3. DUT instances with overriden values // instance names are u_dut*_overriden @@ -153,6 +167,7 @@ module t if (dut0_o_open != 0) $error; if (dut1_o_open != 1) $error; if (dut_logic32_o_open != 32'h1234_5678) $error; + if (dut_wire32_o_open != 32'h1234_5678) $error; // despite the port map override. At least the parameter goes through? $display("%t %m: outputs - overrides got {%0d %0d %0x} want {1 0 %0x}", diff --git a/test_regress/t/t_opt_const_cov.py b/test_regress/t/t_opt_const_cov.py index 1af2400b7..147adc0df 100755 --- a/test_regress/t/t_opt_const_cov.py +++ b/test_regress/t/t_opt_const_cov.py @@ -16,6 +16,6 @@ test.compile(verilator_flags2=["-Wno-UNOPTTHREADS", "--stats", "--coverage", "-- test.execute() if test.vlt: - test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 144) + test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 620) test.passes() diff --git a/test_regress/t/t_opt_dead_enumpkg.py b/test_regress/t/t_opt_dead_enumpkg.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_opt_dead_enumpkg.py @@ -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() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_opt_dead_enumpkg.v b/test_regress/t/t_opt_dead_enumpkg.v new file mode 100644 index 000000000..491e1ad5a --- /dev/null +++ b/test_regress/t/t_opt_dead_enumpkg.v @@ -0,0 +1,22 @@ +// 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 + +package pkg; + typedef enum logic [2:0] { + TWO = 2, + THREE = 3 + } enum_t; +endpackage + +module t; + localparam L_TWO = pkg::TWO; + initial begin + if (L_TWO != 2) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_optm_if_array.py b/test_regress/t/t_opt_if_array.py similarity index 100% rename from test_regress/t/t_optm_if_array.py rename to test_regress/t/t_opt_if_array.py diff --git a/test_regress/t/t_optm_if_array.v b/test_regress/t/t_opt_if_array.v similarity index 100% rename from test_regress/t/t_optm_if_array.v rename to test_regress/t/t_opt_if_array.v diff --git a/test_regress/t/t_opt_ifjumpgo.py b/test_regress/t/t_opt_ifjumpgo.py new file mode 100755 index 000000000..2c0e4f4f6 --- /dev/null +++ b/test_regress/t/t_opt_ifjumpgo.py @@ -0,0 +1,20 @@ +#!/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=['--stats']) + +test.file_grep(test.stats, r'Node count, JUMPGO + 3 *$') + +test.execute() + +test.passes() diff --git a/test_regress/t/t_opt_ifjumpgo.v b/test_regress/t/t_opt_ifjumpgo.v new file mode 100644 index 000000000..e0fabacaa --- /dev/null +++ b/test_regress/t/t_opt_ifjumpgo.v @@ -0,0 +1,67 @@ +// 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 + +class uvm_object; +endclass + +class uvm_callback; +endclass + +class uvm_callbacks #(type T=uvm_object, type CB=uvm_callback); + bit m_registered = 1; + virtual function bit m_is_registered(uvm_object obj, uvm_callback cb); + if (m_is_for_me(cb) && m_am_i_a(obj)) begin + return m_registered; + end + endfunction + + virtual function bit m_is_for_me(uvm_callback cb); + CB this_cb; + // verilator lint_off WIDTHTRUNC + return ($cast(this_cb, cb)); + // verilator lint_on WIDTHTRUNC + endfunction + + virtual function bit m_am_i_a(uvm_object obj); + T this_t; + // verilator lint_off WIDTHTRUNC + return ($cast(this_t, obj)); + // verilator lint_on WIDTHTRUNC + endfunction +endclass + +class my_object extends uvm_object; +endclass + +class my_callback extends uvm_callback; +endclass + +class other_object extends uvm_object; +endclass + +module t; + + initial begin + my_object obj; + other_object oobj; + my_callback cb; + uvm_callbacks#(my_object, my_callback) ucs; + bit i; + + obj = new; + oobj = new; + cb = new; + ucs = new; + + i = ucs.m_is_registered(obj, cb); + if (i !== 1) $stop; + i = ucs.m_is_registered(oobj, cb); + if (i !== 0) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_optm_redor.py b/test_regress/t/t_opt_redor.py similarity index 100% rename from test_regress/t/t_optm_redor.py rename to test_regress/t/t_opt_redor.py diff --git a/test_regress/t/t_optm_redor.v b/test_regress/t/t_opt_redor.v similarity index 100% rename from test_regress/t/t_optm_redor.v rename to test_regress/t/t_opt_redor.v diff --git a/test_regress/t/t_param_array6_noslice.py b/test_regress/t/t_param_array6_noslice.py new file mode 100755 index 000000000..79f955063 --- /dev/null +++ b/test_regress/t/t_param_array6_noslice.py @@ -0,0 +1,19 @@ +#!/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') +test.top_filename = "t/t_param_array6.v" + +test.compile(verilator_flags2=['-fno-slice']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_pp_circ_subst_bad2.out b/test_regress/t/t_pp_circ_subst_bad2.out new file mode 100644 index 000000000..657d44e8a --- /dev/null +++ b/test_regress/t/t_pp_circ_subst_bad2.out @@ -0,0 +1,3 @@ +%Error: t/t_pp_circ_subst_bad.v:8:40002: Too many preprocessor tokens on a line (>20000); perhaps recursive `define +%Error: t/t_pp_circ_subst_bad.v:8:1: syntax error, unexpected IDENTIFIER +%Error: Exiting due to diff --git a/test_regress/t/t_pp_circ_subst_bad2.py b/test_regress/t/t_pp_circ_subst_bad2.py new file mode 100755 index 000000000..a4770180d --- /dev/null +++ b/test_regress/t/t_pp_circ_subst_bad2.py @@ -0,0 +1,19 @@ +#!/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('linter') +test.top_filename = "t/t_pp_circ_subst_bad.v" + +test.lint(verilator_flags2=["--preproc-token-limit 20000"], + fails=True, + expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_preproc_resolve.out b/test_regress/t/t_preproc_resolve.out new file mode 100644 index 000000000..7f76f5919 --- /dev/null +++ b/test_regress/t/t_preproc_resolve.out @@ -0,0 +1,26 @@ +`timescale 1ns/1ps +module top( + input logic clk, + input logic rst, + output logic top_out +); + submod u_submod ( + .clk (clk), + .rst (rst), + .out_signal(top_out) + ); +endmodule +`timescale 1ns/1ps +module submod( + input logic clk, + input logic rst, + output logic out_signal +); + always_ff @(posedge clk or posedge rst) begin + if (rst) begin + out_signal <= 1'b0; + end else begin + out_signal <= ~out_signal; + end + end +endmodule diff --git a/test_regress/t/t_preproc_resolve.py b/test_regress/t/t_preproc_resolve.py new file mode 100755 index 000000000..d6f95780f --- /dev/null +++ b/test_regress/t/t_preproc_resolve.py @@ -0,0 +1,29 @@ +#!/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('vlt') + +stdout_filename = os.path.join(test.obj_dir, test.name + ".out") + +test.compile( + # Override default flags + v_flags=[''], + verilator_flags=["-E -P --preproc-resolve -y t/t_preproc_resolve"], + verilator_flags2=[''], + verilator_flags3=[''], + verilator_make_gmake=False, + make_top_shell=False, + make_main=False, + stdout_filename=stdout_filename) + +test.files_identical(stdout_filename, test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_preproc_resolve.v b/test_regress/t/t_preproc_resolve.v new file mode 100644 index 000000000..7d49cd8d0 --- /dev/null +++ b/test_regress/t/t_preproc_resolve.v @@ -0,0 +1,18 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +`timescale 1ns/1ps +module top(/*AUTOARG*/ + input logic clk, + input logic rst, + output logic top_out +); + submod u_submod (/*AUTOINST*/ + .clk (clk), + .rst (rst), + .out_signal(top_out) + ); +endmodule diff --git a/test_regress/t/t_preproc_resolve/submod.sv b/test_regress/t/t_preproc_resolve/submod.sv new file mode 100644 index 000000000..701cd7269 --- /dev/null +++ b/test_regress/t/t_preproc_resolve/submod.sv @@ -0,0 +1,20 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +`timescale 1ns/1ps +module submod(/*AUTOARG*/ + input logic clk, + input logic rst, + output logic out_signal +); + always_ff @(posedge clk or posedge rst) begin + if (rst) begin + out_signal <= 1'b0; + end else begin + out_signal <= ~out_signal; + end + end +endmodule diff --git a/test_regress/t/t_preproc_strify_join.out b/test_regress/t/t_preproc_strify_join.out new file mode 100644 index 000000000..b80acd35b --- /dev/null +++ b/test_regress/t/t_preproc_strify_join.out @@ -0,0 +1,3 @@ +"foo-bar-qux" +""foo-bar-qux"" +"" diff --git a/test_regress/t/t_preproc_strify_join.py b/test_regress/t/t_preproc_strify_join.py new file mode 100755 index 000000000..981d60faf --- /dev/null +++ b/test_regress/t/t_preproc_strify_join.py @@ -0,0 +1,24 @@ +#!/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('vlt') + +stdout_filename = os.path.join(test.obj_dir, test.name + "__test.vpp") + +test.compile(verilator_flags2=['-E -P'], + verilator_make_gmake=False, + make_top_shell=False, + make_main=False, + stdout_filename=stdout_filename) + +test.files_identical(stdout_filename, test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_preproc_strify_join.v b/test_regress/t/t_preproc_strify_join.v new file mode 100644 index 000000000..88241fae5 --- /dev/null +++ b/test_regress/t/t_preproc_strify_join.v @@ -0,0 +1,19 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +`define FOO foo +`define BAR bar +`define QUX qux +`define STRIFY `"`FOO``-```BAR``-```QUX```" + +`define NESTED_STRIFY `"`STRIFY```" + +`define EMPTY +`define EMPTY_STRIFY `"`EMPTY```" + +`STRIFY +`NESTED_STRIFY +`EMPTY_STRIFY diff --git a/test_regress/t/t_queue_unknown_sel.v b/test_regress/t/t_queue_unknown_sel.v index 489c7bcee..fa75b6c34 100644 --- a/test_regress/t/t_queue_unknown_sel.v +++ b/test_regress/t/t_queue_unknown_sel.v @@ -262,23 +262,22 @@ interface z_if; ); endinterface class z_txn_class; - import z_pkg::*; - rand txn_type_t req_txn_type; - rand cid_t cid; - rand sid_t sid; - rand ctag_t ctag; - rand stag_t stag; - rand size_t size; - rand address_t address; - rand state_t state1; - rand state_t state2; - rand state_t state3; + rand z_pkg::txn_type_t req_txn_type; + rand z_pkg::cid_t cid; + rand z_pkg::sid_t sid; + rand z_pkg::ctag_t ctag; + rand z_pkg::stag_t stag; + rand z_pkg::size_t size; + rand z_pkg::address_t address; + rand z_pkg::state_t state1; + rand z_pkg::state_t state2; + rand z_pkg::state_t state3; rand logic f1; rand logic f2; rand logic f3; rand logic f4; - data_t data[]; - mask_t mask[]; + z_pkg::data_t data[]; + z_pkg::mask_t mask[]; bit corrupt[]; logic [2:0] req_opcode; endclass diff --git a/test_regress/t/t_sc_names.cpp b/test_regress/t/t_sc_names.cpp index 354d27ce2..3d7b6cd7f 100644 --- a/test_regress/t/t_sc_names.cpp +++ b/test_regress/t/t_sc_names.cpp @@ -14,7 +14,7 @@ int sc_main(int argc, char* argv[]) { std::vector ch = tb->get_child_objects(); bool found = false; - /* We expect to find clk in here. */ + // We expect to find clk in here for (int i = 0; i < ch.size(); ++i) { if (!std::strcmp(ch[i]->basename(), "clk")) found = true; } diff --git a/test_regress/t/t_scope_map.cpp b/test_regress/t/t_scope_map.cpp index 93de549dc..df6e22e1a 100644 --- a/test_regress/t/t_scope_map.cpp +++ b/test_regress/t/t_scope_map.cpp @@ -50,8 +50,8 @@ int main(int argc, char** argv) { for (const auto& varname : *varNameMap) { const VerilatedVar* varp = &(varname.second); - int varLeft = varp->packed().left(); - int varRight = varp->packed().right(); + int varLeft = varp->range(0)->left(); + int varRight = varp->range(0)->right(); #ifdef TEST_VERBOSE VL_PRINTF("\tVar = %s\n", varname.first); @@ -125,7 +125,7 @@ int main(int argc, char** argv) { for (const auto& varname : *varNameMap) { const VerilatedVar* varp = &(varname.second); - int varLeft = varp->packed().left(); + int varLeft = varp->range(0)->left(); int varBits = varLeft + 1; uint8_t* varData = reinterpret_cast(varp->datap()); diff --git a/test_regress/t/t_semaphore_class_nested.py b/test_regress/t/t_semaphore_class_nested.py new file mode 100755 index 000000000..bbbcfc078 --- /dev/null +++ b/test_regress/t/t_semaphore_class_nested.py @@ -0,0 +1,16 @@ +#!/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') + +test.compile(verilator_flags2=["--binary --timing"]) + +test.passes() diff --git a/test_regress/t/t_semaphore_class_nested.v b/test_regress/t/t_semaphore_class_nested.v new file mode 100644 index 000000000..c18f65b45 --- /dev/null +++ b/test_regress/t/t_semaphore_class_nested.v @@ -0,0 +1,39 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class semaphore_cls; + class InnerKeyClass; + int innerKeys; + function new(int keyCount = 0); + innerKeys = keyCount; + endfunction + endclass + // Test an implementation similar to what Verilator will do internally + InnerKeyClass m_keys; + function new(int keyCount = 0); + m_keys = new(keyCount); + endfunction + function void put(int keyCount = 1); + m_keys.innerKeys += keyCount; + endfunction + task get(int keyCount = 1); + wait (m_keys.innerKeys >= keyCount); + m_keys.innerKeys -= keyCount; + endtask + function int try_get(int keyCount = 1); + if (m_keys.innerKeys >= keyCount) begin + m_keys.innerKeys -= keyCount; + return 1; + end + else begin + return 0; + end + endfunction +endclass + +`define SEMAPHORE_T semaphore_cls + +`include "t_semaphore.v" diff --git a/test_regress/t/t_sequence_sexpr_unsup.out b/test_regress/t/t_sequence_sexpr_unsup.out index f72f26834..cdd372742 100644 --- a/test_regress/t/t_sequence_sexpr_unsup.out +++ b/test_regress/t/t_sequence_sexpr_unsup.out @@ -230,13 +230,14 @@ %Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:126:7: Unsupported: first_match (in sequence expression) 126 | first_match (a, res0 = 1, res1 = 2); | ^~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:129:10: Unsupported: cover sequence +%Warning-COVERIGN: t/t_sequence_sexpr_unsup.v:129:10: Ignoring unsupported: cover sequence 129 | cover sequence (s_a) $display(""); | ^~~~~~~~ -%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:130:10: Unsupported: cover sequence + ... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message. +%Warning-COVERIGN: t/t_sequence_sexpr_unsup.v:130:10: Ignoring unsupported: cover sequence 130 | cover sequence (@(posedge a) disable iff (b) s_a) $display(""); | ^~~~~~~~ -%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:131:10: Unsupported: cover sequence +%Warning-COVERIGN: t/t_sequence_sexpr_unsup.v:131:10: Ignoring unsupported: cover sequence 131 | cover sequence (disable iff (b) s_a) $display(""); | ^~~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_simulate_array.py b/test_regress/t/t_simulate_array.py new file mode 100755 index 000000000..15d1de923 --- /dev/null +++ b/test_regress/t/t_simulate_array.py @@ -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() + +test.execute(check_finished=True) + +test.passes() diff --git a/test_regress/t/t_simulate_array.v b/test_regress/t/t_simulate_array.v new file mode 100644 index 000000000..77c19e2ce --- /dev/null +++ b/test_regress/t/t_simulate_array.v @@ -0,0 +1,24 @@ +// 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 + +function integer fun; +integer array[0:0]; +begin + array[0] = 10; + fun = array[0]; +end +endfunction + +module test (); +begin + localparam something = fun(); + initial begin + if (something !== 10) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +end +endmodule diff --git a/test_regress/t/t_split_var_types.py b/test_regress/t/t_split_var_types.py new file mode 100755 index 000000000..6c6dd3e23 --- /dev/null +++ b/test_regress/t/t_split_var_types.py @@ -0,0 +1,17 @@ +#!/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') + +test.compile(verilator_flags2=['--trace']) + +test.execute() +test.passes() diff --git a/test_regress/t/t_split_var_types.v b/test_regress/t/t_split_var_types.v new file mode 100644 index 000000000..b61923ec0 --- /dev/null +++ b/test_regress/t/t_split_var_types.v @@ -0,0 +1,31 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 +// SPDX-License-Identifier: CC0-1.0 + +module t(/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + // Test loop + always @ (posedge clk) begin + $write("*-* All Finished *-*\n"); + $finish; + end + + bug5782 u_bug5782(.data_out()); + +endmodule + +// #5782 internal error with --trace. Bit range is not properly handled. +module bug5782 ( + output logic [31:0][15:0] data_out +); + logic [31:0][15:0] data [8] /*verilator split_var*/; + always begin + data_out = data[7]; + end +endmodule diff --git a/test_regress/t/t_stmt_incr_unsup.out b/test_regress/t/t_stmt_incr_unsup.out index c7c313981..a7ac92291 100644 --- a/test_regress/t/t_stmt_incr_unsup.out +++ b/test_regress/t/t_stmt_incr_unsup.out @@ -1,11 +1,17 @@ -%Warning-SIDEEFFECT: t/t_stmt_incr_unsup.v:17:12: Expression side effect may be mishandled +%Warning-SIDEEFFECT: t/t_stmt_incr_unsup.v:17:31: Expression side effect may be mishandled : ... Suggest use a temporary variable in place of this expression - 17 | arr[postincrement_i()]++; - | ^ + 17 | arr[postincrement_i()][postincrement_i()]++; + | ^ ... For warning description see https://verilator.org/warn/SIDEEFFECT?v=latest ... Use "/* verilator lint_off SIDEEFFECT */" and lint_on around source to disable this message. %Warning-SIDEEFFECT: t/t_stmt_incr_unsup.v:17:13: Expression side effect may be mishandled + : ... note: In instance 't' : ... Suggest use a temporary variable in place of this expression - 17 | arr[postincrement_i()]++; + 17 | arr[postincrement_i()][postincrement_i()]++; | ^~~~~~~~~~~~~~~ +%Warning-SIDEEFFECT: t/t_stmt_incr_unsup.v:17:32: Expression side effect may be mishandled + : ... note: In instance 't' + : ... Suggest use a temporary variable in place of this expression + 17 | arr[postincrement_i()][postincrement_i()]++; + | ^~~~~~~~~~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_stmt_incr_unsup.v b/test_regress/t/t_stmt_incr_unsup.v index e1682331a..09e258028 100644 --- a/test_regress/t/t_stmt_incr_unsup.v +++ b/test_regress/t/t_stmt_incr_unsup.v @@ -12,9 +12,9 @@ endfunction module t; initial begin - int arr [1:0] = {0, 0}; + int arr [3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; i = 0; - arr[postincrement_i()]++; + arr[postincrement_i()][postincrement_i()]++; $display("Value: %d", i); end endmodule diff --git a/test_regress/t/t_string_sel.py b/test_regress/t/t_string_sel.py new file mode 100755 index 000000000..b7a43ed66 --- /dev/null +++ b/test_regress/t/t_string_sel.py @@ -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('vlt') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_string_sel.v b/test_regress/t/t_string_sel.v new file mode 100644 index 000000000..acf7478e9 --- /dev/null +++ b/test_regress/t/t_string_sel.v @@ -0,0 +1,60 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +typedef struct { + string str; +} str_s; + +class c; + string str; + function new(); + str = "foo"; + endfunction + function string get_str(); + return str; + endfunction +endclass + +module t (/*AUTOARG*/); + string str = "bar"; + + function string get_str(); + return str; + endfunction + + initial begin + c o = new; + str_s st = '{"qux"}; + string sc = {"foo", "bar"}; + + // read + if (str[0] != "b") $stop; + if (get_str()[1] != "a") $stop; + if (str[3] != "\0") $stop; + if (st.str[2] != "x") $stop; + if (st.str[99] != "\0") $stop; + if (o.str[0] != "f") $stop; + if (o.get_str()[1] != "o") $stop; + if (o.str[-1] != "\0") $stop; + if (sc[2] != "o") $stop; + if ($sformatf("foo%s", "bar")[3] != "b") $stop; + if (sc[-1] != "\0") $stop; + if (sc[6] != "\0") $stop; + if (sc[99] != "\0") $stop; + + // write + sc[5] = "z"; + if (sc != "foobaz") $stop; + o.str[0] = "b"; + if (o.str != "boo") $stop; + st.str[2] = "z"; + if (st.str != "quz") $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_struct_pat.v b/test_regress/t/t_struct_pat.v index 931113fc0..7b9660935 100644 --- a/test_regress/t/t_struct_pat.v +++ b/test_regress/t/t_struct_pat.v @@ -31,8 +31,14 @@ module t(/*AUTOARG*/); bit [3:0][31:0] b4; } sab4p_t; + typedef struct { + int i; + real r; + } sir_t; + sab4u_t ab4u[2][3]; sab4p_t ab4p[2][3]; + sir_t sir; initial begin abcp = '{1, 2, 3}; @@ -96,6 +102,10 @@ module t(/*AUTOARG*/); if (ab4u[1][2].b4[2] !== 20) $stop; if (ab4u[1][2].b4[3] !== 30) $stop; + sir = '{1, 2.2}; + if (sir.i !== 1) $stop; + if (sir.r !== 2.2) $stop; + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_sys_monitor_dotted.py b/test_regress/t/t_sys_monitor_dotted.py new file mode 100755 index 000000000..bd059b0f2 --- /dev/null +++ b/test_regress/t/t_sys_monitor_dotted.py @@ -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() diff --git a/test_regress/t/t_sys_monitor_dotted.v b/test_regress/t/t_sys_monitor_dotted.v new file mode 100644 index 000000000..92ac9b4b0 --- /dev/null +++ b/test_regress/t/t_sys_monitor_dotted.v @@ -0,0 +1,94 @@ +// 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 + +interface addsub_ifc; + logic [7:0] a, b; + logic doAdd0; + logic clk; + logic rst_n; + logic [7:0] result; + logic overflow; +endinterface + +module adder_sub_8bit + ( + input logic clk, + input logic rst_n, + input logic [7:0] a, + input logic [7:0] b, + input logic doAdd0, + output logic [7:0] result, + output logic overflow + ); + + logic [7:0] b_modified; + logic [8:0] temp_result; + + assign b_modified = doAdd0 ? b : ~b + 8'b1; + + always_comb begin + temp_result = {1'b0, a} + {1'b0, b_modified}; + end + + always_ff @(posedge clk or negedge rst_n) begin + if (!rst_n) begin + result <= 8'h0; + overflow <= 1'b0; + end else begin + result <= temp_result[7:0]; + overflow <= (a[7] == b_modified[7] && result[7] != a[7]); + end + end + +endmodule + +module t; + addsub_ifc dut_ifc(); + + adder_sub_8bit dut + ( + .clk(dut_ifc.clk), + .rst_n(dut_ifc.rst_n), + .a(dut_ifc.a), + .b(dut_ifc.b), + .doAdd0(dut_ifc.doAdd0), + .result(dut_ifc.result), + .overflow(dut_ifc.overflow) + ); + + initial begin + dut_ifc.clk = 0; + forever #5 dut_ifc.clk = ~dut_ifc.clk; + end + + initial begin + dut_ifc.rst_n = 0; + dut_ifc.a = 8'h0; + dut_ifc.b = 8'h0; + dut_ifc.doAdd0 = 1'b1; + + #10 dut_ifc.rst_n = 1; + + #10; + dut_ifc.a = 8'h35; + dut_ifc.b = 8'h42; + dut_ifc.doAdd0 = 1'b1; + + #20; + $write("*-* All Finished *-*\n"); + $finish; + end + + initial begin + $display("[%0t] Initial rst_n=%b a=%h b=%h doAdd0=%b result=%h overflow=%b", + $time, dut_ifc.rst_n, dut_ifc.a, dut_ifc.b, + dut_ifc.doAdd0, dut_ifc.result, dut_ifc.overflow); + $monitor("[%0t] Monitor rst_n=%b a=%h b=%h doAdd0=%b result=%h overflow=%b", + $time, dut_ifc.rst_n, dut_ifc.a, dut_ifc.b, + dut_ifc.doAdd0, dut_ifc.result, dut_ifc.overflow); + end + +endmodule diff --git a/test_regress/t/t_sys_rand_seed.v b/test_regress/t/t_sys_rand_seed.v index 549f5e8c8..3a9acec20 100644 --- a/test_regress/t/t_sys_rand_seed.v +++ b/test_regress/t/t_sys_rand_seed.v @@ -12,6 +12,9 @@ module t; int valuea; int valueb; int valuec; + int igna; + int ignb; + int ignc; initial begin // $random unlike $urandom updates the value if given @@ -32,7 +35,29 @@ module t; valuea = $urandom(10); valueb = $urandom(10); - if (valuea !== valueb) $stop; + valuec = $urandom(10); + if (valuea !== valueb && valueb != valuec) $stop; + + valuea = $urandom(10); + valueb = $urandom(11); + valuec = $urandom(12); + if (valuea == valueb && valueb == valuec) $stop; // May false fail 1 in 1^64 + + $urandom(10); + valuea = $urandom; + $urandom(10); + valueb = $urandom; + $urandom(10); + valuec = $urandom; + if (valuea != valueb && valueb != valuec) $stop; // May false fail 1 in 1^64 + + igna = $urandom(10); + valuea = $urandom; + ignb = $urandom(10); + valueb = $urandom; + ignc = $urandom(10); + valuec = $urandom; + if (valuea != valueb && valueb != valuec) $stop; // May false fail 1 in 1^64 valuea = $urandom(10); valueb = $urandom(); diff --git a/test_regress/t/t_time_param.py b/test_regress/t/t_time_param.py new file mode 100755 index 000000000..bd059b0f2 --- /dev/null +++ b/test_regress/t/t_time_param.py @@ -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() diff --git a/test_regress/t/t_time_param.v b/test_regress/t/t_time_param.v new file mode 100644 index 000000000..52abb1de3 --- /dev/null +++ b/test_regress/t/t_time_param.v @@ -0,0 +1,39 @@ +// 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 + +`timescale 1ns/1ps + +module vip_snitch_cluster + #(parameter realtime ClkPeriod = 10ns) + (output logic clk_o); + + initial begin + forever begin + clk_o = 1; + #(ClkPeriod/2); + clk_o = 0; + #(ClkPeriod/2); + end + end + + initial begin + #(ClkPeriod*100); + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule + +module t; + logic clk; + + vip_snitch_cluster #( + .ClkPeriod(1ns) + ) vip ( + .clk_o(clk) + ); + +endmodule diff --git a/test_regress/t/t_timing_clkgen2.py b/test_regress/t/t_timing_clkgen2.py index a4e75e7ad..500334384 100755 --- a/test_regress/t/t_timing_clkgen2.py +++ b/test_regress/t/t_timing_clkgen2.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--exe --main --timing -Wwarn-BLKSEQ"]) test.execute() diff --git a/test_regress/t/t_timing_clkgen2.v b/test_regress/t/t_timing_clkgen2.v index c1ec90fb1..4df0b2261 100644 --- a/test_regress/t/t_timing_clkgen2.v +++ b/test_regress/t/t_timing_clkgen2.v @@ -17,24 +17,18 @@ module t; int cnt2 = 0; always #4 clk = ~clk; - always @(negedge clk) begin - cnt1++; - `WRITE_VERBOSE(("[%0t] NEG clk (%b)\n", $time, clk)); - end always @(posedge clk) begin - cnt1++; - `WRITE_VERBOSE(("[%0t] POS clk (%b)\n", $time, clk)); + cnt1 <= cnt1 + 1; + `WRITE_VERBOSE(("[%0t] clk (%b)\n", $time, clk)); end assign #2 clk_inv = ~clk; initial forever begin @(posedge clk_inv) cnt2++; - `WRITE_VERBOSE(("[%0t] POS clk_inv (%b)\n", $time, clk_inv)); - @(negedge clk_inv) cnt2++; - `WRITE_VERBOSE(("[%0t] NEG clk_inv (%b)\n", $time, clk_inv)); + `WRITE_VERBOSE(("[%0t] clk_inv (%b)\n", $time, clk_inv)); end - initial #41 begin + initial #81 begin if (cnt1 != 10 && cnt2 != 10) $stop; $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_timing_debug2.out b/test_regress/t/t_timing_debug2.out index ac4dd1943..a00833c4e 100644 --- a/test_regress/t/t_timing_debug2.out +++ b/test_regress/t/t_timing_debug2.out @@ -185,7 +185,6 @@ -V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:274 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:173 --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay10::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay20::__VnoInFunc_do_delay -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:247 -V{t#,#} Process forked at t/t_timing_class.v:246 finished @@ -499,7 +498,6 @@ -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:257 -V{t#,#}+ Vt_timing_debug2_t__03a__03aForkDelayClass::__VnoInFunc_do_delay -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:174 --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay20::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay40::__VnoInFunc_do_delay -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:131 -V{t#,#}+ Vt_timing_debug2_t__03a__03aClkClass::__VnoInFunc_flip @@ -1038,9 +1036,7 @@ -V{t#,#} Process forked at t/t_timing_class.v:250 finished -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:245 -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:175 --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay40::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t__03a__03aNoDelay::__VnoInFunc_do_delay --V{t#,#}+ Vt_timing_debug2_t__03a__03aNoDelay::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t___eval_initial__TOP__t__Vtiming__6____Vfork_1__0 -V{t#,#}+ Vt_timing_debug2_t__03a__03aAssignDelayClass::__VnoInFunc_do_assign -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:76 @@ -1082,8 +1078,6 @@ -V{t#,#}End-of-eval cleanup -V{t#,#}+++++TOP Evaluate Vt_timing_debug2::eval_step -V{t#,#}+ Vt_timing_debug2___024root___eval_debug_assertions --V{t#,#}+ Vt_timing_debug2_t__03a__03aForkDelayClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aForkClass::~ -V{t#,#}+ Eval -V{t#,#}+ Vt_timing_debug2___024root___eval -V{t#,#}+ Vt_timing_debug2___024root___eval_phase__act @@ -1236,7 +1230,6 @@ -V{t#,#}End-of-eval cleanup -V{t#,#}+++++TOP Evaluate Vt_timing_debug2::eval_step -V{t#,#}+ Vt_timing_debug2___024root___eval_debug_assertions --V{t#,#}+ Vt_timing_debug2_t__03a__03aAssignDelayClass::~ -V{t#,#}+ Eval -V{t#,#}+ Vt_timing_debug2___024root___eval -V{t#,#}+ Vt_timing_debug2___024root___eval_phase__act @@ -1308,18 +1301,3 @@ -V{t#,#}+ Vt_timing_debug2___024root___eval_phase__nba -V{t#,#}End-of-eval cleanup -V{t#,#}+ Vt_timing_debug2___024root___eval_final --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay40::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay20::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay10::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aNoDelay::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aClkClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aLocalWaitClass::~ --V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aWaitClass::~ --V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aEventClass::~ --V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::~ diff --git a/test_regress/t/t_vpi_const_type.cpp b/test_regress/t/t_vpi_const_type.cpp index ac8ebd2db..5efd97175 100644 --- a/test_regress/t/t_vpi_const_type.cpp +++ b/test_regress/t/t_vpi_const_type.cpp @@ -62,13 +62,32 @@ int mon_check() { const char* strConstTypeStr = vpi_get_str(vpiConstType, strHandle); CHECK_RESULT_CSTR(strConstTypeStr, "vpiStringConst") - TestVpiHandle sigHandle = vpi_handle_by_name((PLI_BYTE8*)"t.signal", NULL); + // t.signal_rd is not constant, and should error on a write + TestVpiHandle sigHandle = vpi_handle_by_name((PLI_BYTE8*)"t.signal_rd", NULL); CHECK_RESULT_NZ(sigHandle) PLI_INT32 sigConstType = vpi_get(vpiConstType, sigHandle); - // t.signal is not constant CHECK_RESULT(sigConstType, vpiUndefined) const char* sigConstTypeStr = vpi_get_str(vpiConstType, sigHandle); CHECK_RESULT_CSTR(sigConstTypeStr, "*undefined*") + // and should error on a write + s_vpi_value vpi_value; + vpi_value.format = vpiIntVal; + vpi_value.value.integer = 1; + vpi_put_value(sigHandle, &vpi_value, NULL, vpiNoDelay); + CHECK_RESULT(vpi_chk_error(nullptr), vpiError); + // and an intertial write + vpi_put_value(sigHandle, &vpi_value, NULL, vpiInertialDelay); + CHECK_RESULT(vpi_chk_error(nullptr), vpiError); + + // t.signal_rw is not constant + sigHandle = vpi_handle_by_name((PLI_BYTE8*)"t.signal_rw", NULL); + CHECK_RESULT_NZ(sigHandle) + sigConstType = vpi_get(vpiConstType, sigHandle); + CHECK_RESULT(sigConstType, vpiUndefined) + sigConstTypeStr = vpi_get_str(vpiConstType, sigHandle); + CHECK_RESULT_CSTR(sigConstTypeStr, "*undefined*") + + // left range of t.signal_rw TestVpiHandle leftHandle = vpi_handle(vpiLeftRange, sigHandle); CHECK_RESULT_NZ(leftHandle) PLI_INT32 leftConstType = vpi_get(vpiConstType, leftHandle); diff --git a/test_regress/t/t_vpi_const_type.v b/test_regress/t/t_vpi_const_type.v index ef76c8f35..2ae4e2f59 100644 --- a/test_regress/t/t_vpi_const_type.v +++ b/test_regress/t/t_vpi_const_type.v @@ -16,7 +16,8 @@ module t (/*AUTOARG*/ parameter time timeParam /*verilator public_flat_rd*/ = 0; parameter string strParam /*verilator public_flat_rd*/ = "abc"; - logic [31:0] signal /*verilator public_flat_rw*/; + logic [31:0] signal_rw /*verilator public_flat_rw*/; + logic [31:0] signal_rd /*verilator public_flat_rd*/; int status; diff --git a/test_regress/t/t_vpi_dump.cpp b/test_regress/t/t_vpi_dump.cpp index 828c757b5..3a78e6c97 100644 --- a/test_regress/t/t_vpi_dump.cpp +++ b/test_regress/t/t_vpi_dump.cpp @@ -127,11 +127,12 @@ void modDump(TestVpiHandle& it, int n) { const int type = vpi_get(vpiType, hndl); const char* name = vpi_get_str(vpiName, hndl); const char* fullname = vpi_get_str(vpiFullName, hndl); - printf("%s (%s) %s\n", name, strFromVpiObjType(type), fullname); + printf("%s (%s) %s ", name, strFromVpiObjType(type), fullname); if (type == vpiParameter || type == vpiConstType) { - for (int i = 0; i < n + 1; i++) printf(" "); - printf("vpiConstType=%s\n", strFromVpiConstType(vpi_get(vpiConstType, hndl))); + printf(" vpiConstType=%s", strFromVpiConstType(vpi_get(vpiConstType, hndl))); } + if (type == vpiModule) { printf(" vpiDefName=%s", vpi_get_str(vpiDefName, hndl)); } + printf("\n"); if (iterate_over.find(type) == iterate_over.end()) { continue; } for (int type : iterate_over.at(type)) { diff --git a/test_regress/t/t_vpi_dump.out b/test_regress/t/t_vpi_dump.out index a100566d1..9bacaf77b 100644 --- a/test_regress/t/t_vpi_dump.out +++ b/test_regress/t/t_vpi_dump.out @@ -1,307 +1,255 @@ -t (vpiModule) t +t (vpiModule) t vpiDefName=t vpiReg: - LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND (vpiReg) t.LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND - a (vpiReg) TOP.a - bus1 (vpiReg) t.bus1 - clk (vpiReg) TOP.clk - count (vpiReg) t.count - do_generate (vpiParameter) t.do_generate - vpiConstType=vpiDecConst - fourthreetwoone (vpiMemory) t.fourthreetwoone - vpiMemoryWord: - fourthreetwoone (vpiMemoryWord) t.fourthreetwoone[3] - fourthreetwoone (vpiMemoryWord) t.fourthreetwoone[4] - half_count (vpiReg) t.half_count - long_int (vpiParameter) t.long_int - vpiConstType=vpiDecConst - onebit (vpiReg) t.onebit - quads (vpiMemory) t.quads - vpiMemoryWord: - quads (vpiMemoryWord) t.quads[3] - quads (vpiMemoryWord) t.quads[2] - real1 (vpiRealVar) t.real1 - status (vpiReg) t.status - str1 (vpiStringVar) t.str1 - text (vpiReg) t.text - text_byte (vpiReg) t.text_byte - text_half (vpiReg) t.text_half - text_long (vpiReg) t.text_long - text_word (vpiReg) t.text_word - twoone (vpiReg) t.twoone - x (vpiReg) TOP.x + LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND (vpiReg) t.LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND + a (vpiReg) TOP.a + bus1 (vpiReg) t.bus1 + clk (vpiReg) TOP.clk + count (vpiReg) t.count + do_generate (vpiParameter) t.do_generate vpiConstType=vpiDecConst + fourthreetwoone (vpiRegArray) t.fourthreetwoone + vpiReg: + fourthreetwoone (vpiReg) t.fourthreetwoone[3] + fourthreetwoone (vpiReg) t.fourthreetwoone[4] + half_count (vpiReg) t.half_count + long_int (vpiParameter) t.long_int vpiConstType=vpiDecConst + onebit (vpiReg) t.onebit + quads (vpiRegArray) t.quads + vpiReg: + quads (vpiReg) t.quads[3] + quads (vpiReg) t.quads[2] + real1 (vpiRealVar) t.real1 + status (vpiReg) t.status + str1 (vpiStringVar) t.str1 + text (vpiReg) t.text + text_byte (vpiReg) t.text_byte + text_half (vpiReg) t.text_half + text_long (vpiReg) t.text_long + text_word (vpiReg) t.text_word + twoone (vpiReg) t.twoone + x (vpiReg) TOP.x vpiParameter: - do_generate (vpiParameter) t.do_generate - vpiConstType=vpiDecConst - long_int (vpiParameter) t.long_int - vpiConstType=vpiDecConst + do_generate (vpiParameter) t.do_generate vpiConstType=vpiDecConst + long_int (vpiParameter) t.long_int vpiConstType=vpiDecConst vpiInternalScope: - arr[1] (vpiGenScope) t.arr[1] + arr[1] (vpiGenScope) t.arr[1] vpiReg: vpiParameter: vpiInternalScope: - arr (vpiModule) t.arr[1].arr + arr (vpiModule) t.arr[1].arr vpiDefName=ModArr vpiReg: - LENGTH (vpiParameter) t.arr[1].arr.LENGTH - vpiConstType=vpiDecConst - check (vpiReg) t.arr[1].arr.check - rfr (vpiReg) t.arr[1].arr.rfr - sig (vpiReg) t.arr[1].arr.sig - verbose (vpiReg) t.arr[1].arr.verbose + LENGTH (vpiParameter) t.arr[1].arr.LENGTH vpiConstType=vpiDecConst + check (vpiReg) t.arr[1].arr.check + rfr (vpiReg) t.arr[1].arr.rfr + sig (vpiReg) t.arr[1].arr.sig + verbose (vpiReg) t.arr[1].arr.verbose vpiParameter: - LENGTH (vpiParameter) t.arr[1].arr.LENGTH - vpiConstType=vpiDecConst - arr[2] (vpiGenScope) t.arr[2] + LENGTH (vpiParameter) t.arr[1].arr.LENGTH vpiConstType=vpiDecConst + arr[2] (vpiGenScope) t.arr[2] vpiReg: vpiParameter: vpiInternalScope: - arr (vpiModule) t.arr[2].arr + arr (vpiModule) t.arr[2].arr vpiDefName=ModArr vpiReg: - LENGTH (vpiParameter) t.arr[2].arr.LENGTH - vpiConstType=vpiDecConst - check (vpiReg) t.arr[2].arr.check - rfr (vpiReg) t.arr[2].arr.rfr - sig (vpiReg) t.arr[2].arr.sig - verbose (vpiReg) t.arr[2].arr.verbose + LENGTH (vpiParameter) t.arr[2].arr.LENGTH vpiConstType=vpiDecConst + check (vpiReg) t.arr[2].arr.check + rfr (vpiReg) t.arr[2].arr.rfr + sig (vpiReg) t.arr[2].arr.sig + verbose (vpiReg) t.arr[2].arr.verbose vpiParameter: - LENGTH (vpiParameter) t.arr[2].arr.LENGTH - vpiConstType=vpiDecConst - cond_scope (vpiGenScope) t.cond_scope + LENGTH (vpiParameter) t.arr[2].arr.LENGTH vpiConstType=vpiDecConst + cond_scope (vpiGenScope) t.cond_scope vpiReg: - scoped_wire (vpiParameter) t.cond_scope.scoped_wire - vpiConstType=vpiDecConst + scoped_wire (vpiParameter) t.cond_scope.scoped_wire vpiConstType=vpiDecConst vpiParameter: - scoped_wire (vpiParameter) t.cond_scope.scoped_wire - vpiConstType=vpiDecConst + scoped_wire (vpiParameter) t.cond_scope.scoped_wire vpiConstType=vpiDecConst vpiInternalScope: - scoped_sub (vpiModule) t.cond_scope.scoped_sub + scoped_sub (vpiModule) t.cond_scope.scoped_sub vpiDefName=ModSub vpiReg: - subsig1 (vpiReg) t.cond_scope.scoped_sub.subsig1 - subsig2 (vpiReg) t.cond_scope.scoped_sub.subsig2 + subsig1 (vpiReg) t.cond_scope.scoped_sub.subsig1 + subsig2 (vpiReg) t.cond_scope.scoped_sub.subsig2 vpiParameter: - sub_wrap_gen (vpiModule) t.cond_scope.sub_wrap_gen + sub_wrap_gen (vpiModule) t.cond_scope.sub_wrap_gen vpiDefName=ModSubWrapper vpiReg: vpiParameter: vpiInternalScope: - my_sub (vpiModule) t.cond_scope.sub_wrap_gen.my_sub + my_sub (vpiModule) t.cond_scope.sub_wrap_gen.my_sub vpiDefName=ModSub vpiReg: - subsig1 (vpiReg) t.cond_scope.sub_wrap_gen.my_sub.subsig1 - subsig2 (vpiReg) t.cond_scope.sub_wrap_gen.my_sub.subsig2 + subsig1 (vpiReg) t.cond_scope.sub_wrap_gen.my_sub.subsig1 + subsig2 (vpiReg) t.cond_scope.sub_wrap_gen.my_sub.subsig2 vpiParameter: - intf_arr[0] (vpiModule) t.intf_arr[0] + intf_arr[0] (vpiModule) t.intf_arr[0] vpiDefName=TestInterface vpiReg: - addr (vpiReg) t.intf_arr[0].addr + addr (vpiReg) t.intf_arr[0].addr vpiParameter: - intf_arr[1] (vpiModule) t.intf_arr[1] + intf_arr[1] (vpiModule) t.intf_arr[1] vpiDefName=TestInterface vpiReg: - addr (vpiReg) t.intf_arr[1].addr + addr (vpiReg) t.intf_arr[1].addr vpiParameter: - outer_scope[1] (vpiGenScope) t.outer_scope[1] + outer_scope[1] (vpiGenScope) t.outer_scope[1] vpiReg: - scoped_param (vpiParameter) t.outer_scope[1].scoped_param - vpiConstType=vpiDecConst + scoped_param (vpiParameter) t.outer_scope[1].scoped_param vpiConstType=vpiDecConst vpiParameter: - scoped_param (vpiParameter) t.outer_scope[1].scoped_param - vpiConstType=vpiDecConst + scoped_param (vpiParameter) t.outer_scope[1].scoped_param vpiConstType=vpiDecConst vpiInternalScope: - inner_scope[1] (vpiGenScope) t.outer_scope[1].inner_scope[1] + inner_scope[1] (vpiGenScope) t.outer_scope[1].inner_scope[1] vpiReg: - scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[1].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[1].scoped_param_inner vpiConstType=vpiDecConst vpiParameter: - scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[1].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[1].scoped_param_inner vpiConstType=vpiDecConst vpiInternalScope: - arr (vpiModule) t.outer_scope[1].inner_scope[1].arr + arr (vpiModule) t.outer_scope[1].inner_scope[1].arr vpiDefName=ModArr vpiReg: - LENGTH (vpiParameter) t.outer_scope[1].inner_scope[1].arr.LENGTH - vpiConstType=vpiDecConst - check (vpiReg) t.outer_scope[1].inner_scope[1].arr.check - rfr (vpiReg) t.outer_scope[1].inner_scope[1].arr.rfr - sig (vpiReg) t.outer_scope[1].inner_scope[1].arr.sig - verbose (vpiReg) t.outer_scope[1].inner_scope[1].arr.verbose + LENGTH (vpiParameter) t.outer_scope[1].inner_scope[1].arr.LENGTH vpiConstType=vpiDecConst + check (vpiReg) t.outer_scope[1].inner_scope[1].arr.check + rfr (vpiReg) t.outer_scope[1].inner_scope[1].arr.rfr + sig (vpiReg) t.outer_scope[1].inner_scope[1].arr.sig + verbose (vpiReg) t.outer_scope[1].inner_scope[1].arr.verbose vpiParameter: - LENGTH (vpiParameter) t.outer_scope[1].inner_scope[1].arr.LENGTH - vpiConstType=vpiDecConst - inner_scope[2] (vpiGenScope) t.outer_scope[1].inner_scope[2] + LENGTH (vpiParameter) t.outer_scope[1].inner_scope[1].arr.LENGTH vpiConstType=vpiDecConst + inner_scope[2] (vpiGenScope) t.outer_scope[1].inner_scope[2] vpiReg: - scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[2].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[2].scoped_param_inner vpiConstType=vpiDecConst vpiParameter: - scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[2].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[2].scoped_param_inner vpiConstType=vpiDecConst vpiInternalScope: - arr (vpiModule) t.outer_scope[1].inner_scope[2].arr + arr (vpiModule) t.outer_scope[1].inner_scope[2].arr vpiDefName=ModArr vpiReg: - LENGTH (vpiParameter) t.outer_scope[1].inner_scope[2].arr.LENGTH - vpiConstType=vpiDecConst - check (vpiReg) t.outer_scope[1].inner_scope[2].arr.check - rfr (vpiReg) t.outer_scope[1].inner_scope[2].arr.rfr - sig (vpiReg) t.outer_scope[1].inner_scope[2].arr.sig - verbose (vpiReg) t.outer_scope[1].inner_scope[2].arr.verbose + LENGTH (vpiParameter) t.outer_scope[1].inner_scope[2].arr.LENGTH vpiConstType=vpiDecConst + check (vpiReg) t.outer_scope[1].inner_scope[2].arr.check + rfr (vpiReg) t.outer_scope[1].inner_scope[2].arr.rfr + sig (vpiReg) t.outer_scope[1].inner_scope[2].arr.sig + verbose (vpiReg) t.outer_scope[1].inner_scope[2].arr.verbose vpiParameter: - LENGTH (vpiParameter) t.outer_scope[1].inner_scope[2].arr.LENGTH - vpiConstType=vpiDecConst - inner_scope[3] (vpiGenScope) t.outer_scope[1].inner_scope[3] + LENGTH (vpiParameter) t.outer_scope[1].inner_scope[2].arr.LENGTH vpiConstType=vpiDecConst + inner_scope[3] (vpiGenScope) t.outer_scope[1].inner_scope[3] vpiReg: - scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[3].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[3].scoped_param_inner vpiConstType=vpiDecConst vpiParameter: - scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[3].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[3].scoped_param_inner vpiConstType=vpiDecConst vpiInternalScope: - arr (vpiModule) t.outer_scope[1].inner_scope[3].arr + arr (vpiModule) t.outer_scope[1].inner_scope[3].arr vpiDefName=ModArr vpiReg: - LENGTH (vpiParameter) t.outer_scope[1].inner_scope[3].arr.LENGTH - vpiConstType=vpiDecConst - check (vpiReg) t.outer_scope[1].inner_scope[3].arr.check - rfr (vpiReg) t.outer_scope[1].inner_scope[3].arr.rfr - sig (vpiReg) t.outer_scope[1].inner_scope[3].arr.sig - verbose (vpiReg) t.outer_scope[1].inner_scope[3].arr.verbose + LENGTH (vpiParameter) t.outer_scope[1].inner_scope[3].arr.LENGTH vpiConstType=vpiDecConst + check (vpiReg) t.outer_scope[1].inner_scope[3].arr.check + rfr (vpiReg) t.outer_scope[1].inner_scope[3].arr.rfr + sig (vpiReg) t.outer_scope[1].inner_scope[3].arr.sig + verbose (vpiReg) t.outer_scope[1].inner_scope[3].arr.verbose vpiParameter: - LENGTH (vpiParameter) t.outer_scope[1].inner_scope[3].arr.LENGTH - vpiConstType=vpiDecConst - outer_scope[2] (vpiGenScope) t.outer_scope[2] + LENGTH (vpiParameter) t.outer_scope[1].inner_scope[3].arr.LENGTH vpiConstType=vpiDecConst + outer_scope[2] (vpiGenScope) t.outer_scope[2] vpiReg: - scoped_param (vpiParameter) t.outer_scope[2].scoped_param - vpiConstType=vpiDecConst + scoped_param (vpiParameter) t.outer_scope[2].scoped_param vpiConstType=vpiDecConst vpiParameter: - scoped_param (vpiParameter) t.outer_scope[2].scoped_param - vpiConstType=vpiDecConst + scoped_param (vpiParameter) t.outer_scope[2].scoped_param vpiConstType=vpiDecConst vpiInternalScope: - inner_scope[1] (vpiGenScope) t.outer_scope[2].inner_scope[1] + inner_scope[1] (vpiGenScope) t.outer_scope[2].inner_scope[1] vpiReg: - scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[1].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[1].scoped_param_inner vpiConstType=vpiDecConst vpiParameter: - scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[1].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[1].scoped_param_inner vpiConstType=vpiDecConst vpiInternalScope: - arr (vpiModule) t.outer_scope[2].inner_scope[1].arr + arr (vpiModule) t.outer_scope[2].inner_scope[1].arr vpiDefName=ModArr vpiReg: - LENGTH (vpiParameter) t.outer_scope[2].inner_scope[1].arr.LENGTH - vpiConstType=vpiDecConst - check (vpiReg) t.outer_scope[2].inner_scope[1].arr.check - rfr (vpiReg) t.outer_scope[2].inner_scope[1].arr.rfr - sig (vpiReg) t.outer_scope[2].inner_scope[1].arr.sig - verbose (vpiReg) t.outer_scope[2].inner_scope[1].arr.verbose + LENGTH (vpiParameter) t.outer_scope[2].inner_scope[1].arr.LENGTH vpiConstType=vpiDecConst + check (vpiReg) t.outer_scope[2].inner_scope[1].arr.check + rfr (vpiReg) t.outer_scope[2].inner_scope[1].arr.rfr + sig (vpiReg) t.outer_scope[2].inner_scope[1].arr.sig + verbose (vpiReg) t.outer_scope[2].inner_scope[1].arr.verbose vpiParameter: - LENGTH (vpiParameter) t.outer_scope[2].inner_scope[1].arr.LENGTH - vpiConstType=vpiDecConst - inner_scope[2] (vpiGenScope) t.outer_scope[2].inner_scope[2] + LENGTH (vpiParameter) t.outer_scope[2].inner_scope[1].arr.LENGTH vpiConstType=vpiDecConst + inner_scope[2] (vpiGenScope) t.outer_scope[2].inner_scope[2] vpiReg: - scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[2].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[2].scoped_param_inner vpiConstType=vpiDecConst vpiParameter: - scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[2].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[2].scoped_param_inner vpiConstType=vpiDecConst vpiInternalScope: - arr (vpiModule) t.outer_scope[2].inner_scope[2].arr + arr (vpiModule) t.outer_scope[2].inner_scope[2].arr vpiDefName=ModArr vpiReg: - LENGTH (vpiParameter) t.outer_scope[2].inner_scope[2].arr.LENGTH - vpiConstType=vpiDecConst - check (vpiReg) t.outer_scope[2].inner_scope[2].arr.check - rfr (vpiReg) t.outer_scope[2].inner_scope[2].arr.rfr - sig (vpiReg) t.outer_scope[2].inner_scope[2].arr.sig - verbose (vpiReg) t.outer_scope[2].inner_scope[2].arr.verbose + LENGTH (vpiParameter) t.outer_scope[2].inner_scope[2].arr.LENGTH vpiConstType=vpiDecConst + check (vpiReg) t.outer_scope[2].inner_scope[2].arr.check + rfr (vpiReg) t.outer_scope[2].inner_scope[2].arr.rfr + sig (vpiReg) t.outer_scope[2].inner_scope[2].arr.sig + verbose (vpiReg) t.outer_scope[2].inner_scope[2].arr.verbose vpiParameter: - LENGTH (vpiParameter) t.outer_scope[2].inner_scope[2].arr.LENGTH - vpiConstType=vpiDecConst - inner_scope[3] (vpiGenScope) t.outer_scope[2].inner_scope[3] + LENGTH (vpiParameter) t.outer_scope[2].inner_scope[2].arr.LENGTH vpiConstType=vpiDecConst + inner_scope[3] (vpiGenScope) t.outer_scope[2].inner_scope[3] vpiReg: - scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[3].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[3].scoped_param_inner vpiConstType=vpiDecConst vpiParameter: - scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[3].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[3].scoped_param_inner vpiConstType=vpiDecConst vpiInternalScope: - arr (vpiModule) t.outer_scope[2].inner_scope[3].arr + arr (vpiModule) t.outer_scope[2].inner_scope[3].arr vpiDefName=ModArr vpiReg: - LENGTH (vpiParameter) t.outer_scope[2].inner_scope[3].arr.LENGTH - vpiConstType=vpiDecConst - check (vpiReg) t.outer_scope[2].inner_scope[3].arr.check - rfr (vpiReg) t.outer_scope[2].inner_scope[3].arr.rfr - sig (vpiReg) t.outer_scope[2].inner_scope[3].arr.sig - verbose (vpiReg) t.outer_scope[2].inner_scope[3].arr.verbose + LENGTH (vpiParameter) t.outer_scope[2].inner_scope[3].arr.LENGTH vpiConstType=vpiDecConst + check (vpiReg) t.outer_scope[2].inner_scope[3].arr.check + rfr (vpiReg) t.outer_scope[2].inner_scope[3].arr.rfr + sig (vpiReg) t.outer_scope[2].inner_scope[3].arr.sig + verbose (vpiReg) t.outer_scope[2].inner_scope[3].arr.verbose vpiParameter: - LENGTH (vpiParameter) t.outer_scope[2].inner_scope[3].arr.LENGTH - vpiConstType=vpiDecConst - outer_scope[3] (vpiGenScope) t.outer_scope[3] + LENGTH (vpiParameter) t.outer_scope[2].inner_scope[3].arr.LENGTH vpiConstType=vpiDecConst + outer_scope[3] (vpiGenScope) t.outer_scope[3] vpiReg: - scoped_param (vpiParameter) t.outer_scope[3].scoped_param - vpiConstType=vpiDecConst + scoped_param (vpiParameter) t.outer_scope[3].scoped_param vpiConstType=vpiDecConst vpiParameter: - scoped_param (vpiParameter) t.outer_scope[3].scoped_param - vpiConstType=vpiDecConst + scoped_param (vpiParameter) t.outer_scope[3].scoped_param vpiConstType=vpiDecConst vpiInternalScope: - inner_scope[1] (vpiGenScope) t.outer_scope[3].inner_scope[1] + inner_scope[1] (vpiGenScope) t.outer_scope[3].inner_scope[1] vpiReg: - scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[1].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[1].scoped_param_inner vpiConstType=vpiDecConst vpiParameter: - scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[1].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[1].scoped_param_inner vpiConstType=vpiDecConst vpiInternalScope: - arr (vpiModule) t.outer_scope[3].inner_scope[1].arr + arr (vpiModule) t.outer_scope[3].inner_scope[1].arr vpiDefName=ModArr vpiReg: - LENGTH (vpiParameter) t.outer_scope[3].inner_scope[1].arr.LENGTH - vpiConstType=vpiDecConst - check (vpiReg) t.outer_scope[3].inner_scope[1].arr.check - rfr (vpiReg) t.outer_scope[3].inner_scope[1].arr.rfr - sig (vpiReg) t.outer_scope[3].inner_scope[1].arr.sig - verbose (vpiReg) t.outer_scope[3].inner_scope[1].arr.verbose + LENGTH (vpiParameter) t.outer_scope[3].inner_scope[1].arr.LENGTH vpiConstType=vpiDecConst + check (vpiReg) t.outer_scope[3].inner_scope[1].arr.check + rfr (vpiReg) t.outer_scope[3].inner_scope[1].arr.rfr + sig (vpiReg) t.outer_scope[3].inner_scope[1].arr.sig + verbose (vpiReg) t.outer_scope[3].inner_scope[1].arr.verbose vpiParameter: - LENGTH (vpiParameter) t.outer_scope[3].inner_scope[1].arr.LENGTH - vpiConstType=vpiDecConst - inner_scope[2] (vpiGenScope) t.outer_scope[3].inner_scope[2] + LENGTH (vpiParameter) t.outer_scope[3].inner_scope[1].arr.LENGTH vpiConstType=vpiDecConst + inner_scope[2] (vpiGenScope) t.outer_scope[3].inner_scope[2] vpiReg: - scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[2].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[2].scoped_param_inner vpiConstType=vpiDecConst vpiParameter: - scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[2].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[2].scoped_param_inner vpiConstType=vpiDecConst vpiInternalScope: - arr (vpiModule) t.outer_scope[3].inner_scope[2].arr + arr (vpiModule) t.outer_scope[3].inner_scope[2].arr vpiDefName=ModArr vpiReg: - LENGTH (vpiParameter) t.outer_scope[3].inner_scope[2].arr.LENGTH - vpiConstType=vpiDecConst - check (vpiReg) t.outer_scope[3].inner_scope[2].arr.check - rfr (vpiReg) t.outer_scope[3].inner_scope[2].arr.rfr - sig (vpiReg) t.outer_scope[3].inner_scope[2].arr.sig - verbose (vpiReg) t.outer_scope[3].inner_scope[2].arr.verbose + LENGTH (vpiParameter) t.outer_scope[3].inner_scope[2].arr.LENGTH vpiConstType=vpiDecConst + check (vpiReg) t.outer_scope[3].inner_scope[2].arr.check + rfr (vpiReg) t.outer_scope[3].inner_scope[2].arr.rfr + sig (vpiReg) t.outer_scope[3].inner_scope[2].arr.sig + verbose (vpiReg) t.outer_scope[3].inner_scope[2].arr.verbose vpiParameter: - LENGTH (vpiParameter) t.outer_scope[3].inner_scope[2].arr.LENGTH - vpiConstType=vpiDecConst - inner_scope[3] (vpiGenScope) t.outer_scope[3].inner_scope[3] + LENGTH (vpiParameter) t.outer_scope[3].inner_scope[2].arr.LENGTH vpiConstType=vpiDecConst + inner_scope[3] (vpiGenScope) t.outer_scope[3].inner_scope[3] vpiReg: - scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[3].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[3].scoped_param_inner vpiConstType=vpiDecConst vpiParameter: - scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[3].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[3].scoped_param_inner vpiConstType=vpiDecConst vpiInternalScope: - arr (vpiModule) t.outer_scope[3].inner_scope[3].arr + arr (vpiModule) t.outer_scope[3].inner_scope[3].arr vpiDefName=ModArr vpiReg: - LENGTH (vpiParameter) t.outer_scope[3].inner_scope[3].arr.LENGTH - vpiConstType=vpiDecConst - check (vpiReg) t.outer_scope[3].inner_scope[3].arr.check - rfr (vpiReg) t.outer_scope[3].inner_scope[3].arr.rfr - sig (vpiReg) t.outer_scope[3].inner_scope[3].arr.sig - verbose (vpiReg) t.outer_scope[3].inner_scope[3].arr.verbose + LENGTH (vpiParameter) t.outer_scope[3].inner_scope[3].arr.LENGTH vpiConstType=vpiDecConst + check (vpiReg) t.outer_scope[3].inner_scope[3].arr.check + rfr (vpiReg) t.outer_scope[3].inner_scope[3].arr.rfr + sig (vpiReg) t.outer_scope[3].inner_scope[3].arr.sig + verbose (vpiReg) t.outer_scope[3].inner_scope[3].arr.verbose vpiParameter: - LENGTH (vpiParameter) t.outer_scope[3].inner_scope[3].arr.LENGTH - vpiConstType=vpiDecConst - sub (vpiModule) t.sub + LENGTH (vpiParameter) t.outer_scope[3].inner_scope[3].arr.LENGTH vpiConstType=vpiDecConst + sub (vpiModule) t.sub vpiDefName=ModSub vpiReg: - subsig1 (vpiReg) t.sub.subsig1 - subsig2 (vpiReg) t.sub.subsig2 + subsig1 (vpiReg) t.sub.subsig1 + subsig2 (vpiReg) t.sub.subsig2 vpiParameter: - sub_wrap (vpiModule) t.sub_wrap + sub_wrap (vpiModule) t.sub_wrap vpiDefName=ModSubWrapper vpiReg: vpiParameter: vpiInternalScope: - my_sub (vpiModule) t.sub_wrap.my_sub + my_sub (vpiModule) t.sub_wrap.my_sub vpiDefName=ModSub vpiReg: - subsig1 (vpiReg) t.sub_wrap.my_sub.subsig1 - subsig2 (vpiReg) t.sub_wrap.my_sub.subsig2 + subsig1 (vpiReg) t.sub_wrap.my_sub.subsig1 + subsig2 (vpiReg) t.sub_wrap.my_sub.subsig2 vpiParameter: *-* All Finished *-* diff --git a/test_regress/t/t_vpi_dump.v b/test_regress/t/t_vpi_dump.v index 9a4600458..c9c4b6e0f 100644 --- a/test_regress/t/t_vpi_dump.v +++ b/test_regress/t/t_vpi_dump.v @@ -65,7 +65,7 @@ module t ( /*AUTOARG*/ t_bus bus1; - sub sub (); + ModSub sub (); TestInterface intf_arr[2](); @@ -80,7 +80,7 @@ module t ( /*AUTOARG*/ genvar i; generate for (i = 1; i <= 2; i = i + 1) begin : arr - arr #(.LENGTH(i)) arr (); + ModArr #(.LENGTH(i)) arr (); end for (i = 1; i <= 3; i = i + 1) begin : outer_scope @@ -90,38 +90,38 @@ module t ( /*AUTOARG*/ genvar j; for (j = 1; j <= 3; j = j + 1) begin : inner_scope parameter int scoped_param_inner = scoped_param + 1; - arr #(.LENGTH(scoped_param_inner)) arr (); + ModArr #(.LENGTH(scoped_param_inner)) arr (); end end endgenerate - sub_wrapper sub_wrap (); + ModSubWrapper sub_wrap (); generate if (do_generate == 1) begin : cond_scope - sub scoped_sub (); + ModSub scoped_sub (); parameter int scoped_wire = 1; - sub_wrapper sub_wrap_gen (); + ModSubWrapper sub_wrap_gen (); end else begin : cond_scope_else - sub scoped_sub_else (); + ModSub scoped_sub_else (); end endgenerate endmodule : t -module sub; +module ModSub; reg subsig1; reg subsig2; `ifdef IVERILOG // stop icarus optimizing signals away wire redundant = subsig1 | subsig2; `endif -endmodule : sub +endmodule : ModSub -module arr; +module ModArr; parameter LENGTH = 1; @@ -142,9 +142,9 @@ module arr; check <= 0; end -endmodule : arr +endmodule : ModArr -module sub_wrapper; - sub my_sub (); +module ModSubWrapper; + ModSub my_sub (); endmodule diff --git a/test_regress/t/t_vpi_dump_missing_scopes.out b/test_regress/t/t_vpi_dump_missing_scopes.out index 378dc68d3..23c8997eb 100644 --- a/test_regress/t/t_vpi_dump_missing_scopes.out +++ b/test_regress/t/t_vpi_dump_missing_scopes.out @@ -1,29 +1,29 @@ -t (vpiModule) t +t (vpiModule) t vpiDefName=t vpiReg: vpiParameter: vpiInternalScope: - top_wrap_1 (vpiModule) t.top_wrap_1 + top_wrap_1 (vpiModule) t.top_wrap_1 vpiDefName=gen_wrapper vpiReg: vpiParameter: vpiInternalScope: - gen_loop[0] (vpiGenScope) t.top_wrap_1.gen_loop[0] + gen_loop[0] (vpiGenScope) t.top_wrap_1.gen_loop[0] vpiReg: vpiParameter: vpiInternalScope: - after_gen_loop (vpiModule) t.top_wrap_1.gen_loop[0].after_gen_loop + after_gen_loop (vpiModule) t.top_wrap_1.gen_loop[0].after_gen_loop vpiDefName=sub vpiReg: - subsig1 (vpiReg) t.top_wrap_1.gen_loop[0].after_gen_loop.subsig1 + subsig1 (vpiReg) t.top_wrap_1.gen_loop[0].after_gen_loop.subsig1 vpiParameter: - top_wrap_2 (vpiModule) t.top_wrap_2 + top_wrap_2 (vpiModule) t.top_wrap_2 vpiDefName=gen_wrapper vpiReg: vpiParameter: vpiInternalScope: - gen_loop[0] (vpiGenScope) t.top_wrap_2.gen_loop[0] + gen_loop[0] (vpiGenScope) t.top_wrap_2.gen_loop[0] vpiReg: vpiParameter: vpiInternalScope: - after_gen_loop (vpiModule) t.top_wrap_2.gen_loop[0].after_gen_loop + after_gen_loop (vpiModule) t.top_wrap_2.gen_loop[0].after_gen_loop vpiDefName=sub vpiReg: - subsig1 (vpiReg) t.top_wrap_2.gen_loop[0].after_gen_loop.subsig1 + subsig1 (vpiReg) t.top_wrap_2.gen_loop[0].after_gen_loop.subsig1 vpiParameter: *-* All Finished *-* diff --git a/test_regress/t/t_vpi_dump_no_inline.py b/test_regress/t/t_vpi_dump_no_inline.py new file mode 100755 index 000000000..83dfce490 --- /dev/null +++ b/test_regress/t/t_vpi_dump_no_inline.py @@ -0,0 +1,32 @@ +#!/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') +test.pli_filename = "t/t_vpi_dump.cpp" +test.golden_filename = "t/t_vpi_dump.out" +test.top_filename = "t/t_vpi_dump.v" + +test.compile(make_top_shell=False, + make_main=False, + make_pli=True, + iv_flags2=["-g2005-sv"], + verilator_flags2=[ + "--exe --vpi --public-flat-rw --no-l2name --fno-inline", test.pli_filename, + test.t_dir + "/TestVpiMain.cpp" + ], + make_flags=['CPPFLAGS_ADD=-DVL_NO_LEGACY']) + +test.execute(use_libvpi=True, + expect_filename=test.golden_filename, + xrun_run_expect_filename=re.sub(r'\.out$', '.xrun.out', test.golden_filename), + iv_run_expect_filename=re.sub(r'\.out$', '.iv.out', test.golden_filename)) + +test.passes() diff --git a/test_regress/t/t_vpi_get.cpp b/test_regress/t/t_vpi_get.cpp index 021d761b6..7a9e5711f 100644 --- a/test_regress/t/t_vpi_get.cpp +++ b/test_regress/t/t_vpi_get.cpp @@ -49,7 +49,7 @@ static int _mon_check_props(TestVpiHandle& handle, int size, int direction, int CHECK_RESULT(vpisize, size); // icarus verilog does not support vpiScalar, vpiVector or vpi*Range - if (TestSimulator::has_get_scalar()) { + if (TestSimulator::has_get_scalar() && type != vpiParameter) { int vpiscalar = vpi_get(vpiScalar, handle); CHECK_RESULT((bool)vpiscalar, (bool)scalar); int vpivector = vpi_get(vpiVector, handle); @@ -57,23 +57,29 @@ static int _mon_check_props(TestVpiHandle& handle, int size, int direction, int } // Icarus only supports ranges on memories - if (!scalar && !(TestSimulator::is_icarus() && type != vpiMemory)) { - TestVpiHandle left_h, right_h; - + if (!scalar && type != vpiIntVar && type != vpiParameter + && !(TestSimulator::is_icarus() && type != vpiMemory)) { // check coherency for vectors - // get left hand side of range - left_h = vpi_handle(vpiLeftRange, handle); - CHECK_RESULT_NZ(left_h); - vpi_get_value(left_h, &value); - int coherency = value.value.integer; - // get right hand side of range - right_h = vpi_handle(vpiRightRange, handle); - CHECK_RESULT_NZ(right_h); - vpi_get_value(right_h, &value); - TEST_MSG("%d:%d\n", coherency, value.value.integer); - coherency -= value.value.integer; - // calculate size & check - coherency = abs(coherency) + 1; + + int coherency = 1; + TestVpiHandle iter_h = vpi_iterate(vpiRange, handle); + while (TestVpiHandle range_h = vpi_scan(iter_h)) { + int rangeSize; + TestVpiHandle left_h, right_h; + // get left hand side of range + left_h = vpi_handle(vpiLeftRange, range_h); + CHECK_RESULT_NZ(left_h); + vpi_get_value(left_h, &value); + rangeSize = value.value.integer; + // get right hand side of range + right_h = vpi_handle(vpiRightRange, range_h); + CHECK_RESULT_NZ(right_h); + vpi_get_value(right_h, &value); + rangeSize = abs(rangeSize - value.value.integer) + 1; + coherency *= rangeSize; + } + iter_h.freed(); + CHECK_RESULT(coherency, size); } @@ -83,12 +89,13 @@ static int _mon_check_props(TestVpiHandle& handle, int size, int direction, int int vpidir = vpi_get(vpiDirection, handle); // Don't check port directions in verilator // See issue #681 - if (!TestSimulator::is_verilator()) CHECK_RESULT(vpidir, direction); + if (!TestSimulator::is_verilator() && !TestSimulator::is_questa()) + CHECK_RESULT(vpidir, direction); } // check type of object int vpitype = vpi_get(vpiType, handle); - if (!(TestSimulator::is_verilator() && type == vpiPort)) { + if (!TestSimulator::is_verilator() && !TestSimulator::is_questa() && type == vpiPort) { // Don't check for ports in verilator // See issue #681 CHECK_RESULT(vpitype, type); @@ -114,27 +121,25 @@ int mon_check_props() { // the code that sets up the VerilatedAssertOneThread() check in // verilated_vpi.cc, it was causing the check to falsely fail // (due to m_threadid within the check not being initted yet.) - static struct params values[] - = {{"onebit", {1, vpiNoDirection, 1, vpiReg}, {0, 0, 0, 0}}, - {"twoone", {2, vpiNoDirection, 0, vpiReg}, {0, 0, 0, 0}}, - {"onetwo", - {2, vpiNoDirection, 0, TestSimulator::is_verilator() ? vpiReg : vpiMemory}, - {0, 0, 0, 0}}, - {"fourthreetwoone", - {2, vpiNoDirection, 0, vpiMemory}, - {2, vpiNoDirection, 0, vpiMemoryWord}}, - {"theint", {32, vpiNoDirection, 0, vpiReg}, {0, 0, 0, 0}}, - {"clk", {1, vpiInput, 1, vpiPort}, {0, 0, 0, 0}}, - {"testin", {16, vpiInput, 0, vpiPort}, {0, 0, 0, 0}}, - {"testout", {24, vpiOutput, 0, vpiPort}, {0, 0, 0, 0}}, - {"sub.subin", {1, vpiInput, 1, vpiPort}, {0, 0, 0, 0}}, - {"sub.subout", {1, vpiOutput, 1, vpiPort}, {0, 0, 0, 0}}, - {"sub.subparam", {32, vpiNoDirection, 0, vpiParameter}, {0, 0, 0, 0}}, - {"sub.the_intf.bytesig", {8, vpiNoDirection, 0, vpiReg}, {0, 0, 0, 0}}, - {"sub.the_intf.param", {32, vpiNoDirection, 0, vpiParameter}, {0, 0, 0, 0}}, - {"sub.the_intf.lparam", {32, vpiNoDirection, 0, vpiParameter}, {0, 0, 0, 0}}, - {"twobytwo", {4, vpiNoDirection, 0, vpiReg}, {0, 0, 0, 0}}, - {NULL, {0, 0, 0, 0}, {0, 0, 0, 0}}}; + static struct params values[] = { + {"onebit", {1, vpiNoDirection, 1, vpiReg}, {0, 0, 0, 0}}, + {"twoone", {2, vpiNoDirection, 0, vpiReg}, {0, 0, 0, 0}}, + {"onetwo", {2, vpiNoDirection, 1, vpiRegArray}, {0, 0, 0, 0}}, + {"fourthreetwoone", {2, vpiNoDirection, 0, vpiRegArray}, {2, vpiNoDirection, 0, vpiReg}}, + {"theint", + {32, vpiNoDirection, 0, TestSimulator::is_verilator() ? vpiReg : vpiIntVar}, + {0, 0, 0, 0}}, + {"clk", {1, vpiInput, 1, vpiPort}, {0, 0, 0, 0}}, + {"testin", {16, vpiInput, 0, vpiPort}, {0, 0, 0, 0}}, + {"testout", {24, vpiOutput, 0, vpiPort}, {0, 0, 0, 0}}, + {"sub.subin", {1, vpiInput, 1, vpiPort}, {0, 0, 0, 0}}, + {"sub.subout", {1, vpiOutput, 1, vpiPort}, {0, 0, 0, 0}}, + {"sub.subparam", {32, vpiNoDirection, 0, vpiParameter}, {0, 0, 0, 0}}, + {"sub.the_intf.bytesig", {8, vpiNoDirection, 0, vpiReg}, {0, 0, 0, 0}}, + {"sub.the_intf.param", {32, vpiNoDirection, 0, vpiParameter}, {0, 0, 0, 0}}, + {"sub.the_intf.lparam", {32, vpiNoDirection, 0, vpiParameter}, {0, 0, 0, 0}}, + {"twobytwo", {4, vpiNoDirection, 0, vpiReg}, {0, 0, 0, 0}}, + {NULL, {0, 0, 0, 0}, {0, 0, 0, 0}}}; struct params* value = values; while (value->signal) { TestVpiHandle h = VPI_HANDLE(value->signal); @@ -145,7 +150,7 @@ int mon_check_props() { return status; if (value->children.size) { int size = 0; - TestVpiHandle iter_h = vpi_iterate(vpiMemoryWord, h); + TestVpiHandle iter_h = vpi_iterate(vpiReg, h); while (TestVpiHandle word_h = vpi_scan(iter_h)) { // check size and range if (int status diff --git a/test_regress/t/t_vpi_get_value_array.cpp b/test_regress/t/t_vpi_get_value_array.cpp new file mode 100644 index 000000000..86796eb04 --- /dev/null +++ b/test_regress/t/t_vpi_get_value_array.cpp @@ -0,0 +1,695 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2024 by Diego Roux. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* + +#ifndef IS_VPI + +#include "verilated.h" +#include "verilated_vpi.h" + +#include "Vt_vpi_get_value_array.h" + +#endif + +// These require the above. Comment prevents clang-format moving them +#include "TestSimulator.h" +#include "TestVpi.h" + +#include + +//====================================================================== + +int test_vpiRawFourStateVal(char* name, PLI_BYTE8* test_data, int index, const unsigned low, + const unsigned num, const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u low=%u num=%u size=%u elem_size=%u\n\n", __func__, name, + index, low, num, size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiRawFourStateVal; + arrayvalue.flags = 0; + arrayvalue.value.vectors = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // compare to test data + index -= low; + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; + for (unsigned j = 0; j < elem_size; j++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", (i * 2 * elem_size) + j, (offset * elem_size) + j); +#endif + CHECK_RESULT_HEX(arrayvalue.value.rawvals[(i * 2 * elem_size) + j], + test_data[(offset * elem_size) + j]); + } + for (unsigned j = 0; j < elem_size; j++) { + CHECK_RESULT_HEX(arrayvalue.value.rawvals[(((i * 2) + 1) * elem_size) + j], 0); + } + } + + return 0; +} + +int test_vpiRawTwoStateVal(char* name, PLI_BYTE8* test_data, int index, const unsigned low, + const unsigned num, const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u low=%u num=%u size=%u elem_size=%u\n\n", __func__, name, + index, low, num, size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw two state + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiRawTwoStateVal; + arrayvalue.flags = 0; + arrayvalue.value.vectors = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // compare to test data + index -= low; + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; + for (unsigned j = 0; j < elem_size; j++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", (i * elem_size) + j, (offset * elem_size) + j); +#endif + CHECK_RESULT_HEX(arrayvalue.value.rawvals[(i * elem_size) + j], + test_data[(offset * elem_size) + j]); + } + } + + return 0; +} + +int test_vpiVectorVal(char* name, PLI_BYTE8* test_data, int index, const unsigned low, + const unsigned num, const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u low=%u num=%u size=%u elem_size=%u\n\n", __func__, name, + index, low, num, size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + const unsigned elem_size_words = (elem_size + 3) / sizeof(PLI_UINT32); + const unsigned vec_size = elem_size_words * size; + std::vector test_data_vectors; + test_data_vectors.reserve(vec_size); + unsigned test_data_index = 0; + for (unsigned i = 0; i < size; i++) { + unsigned count = 0; + for (unsigned j = 0; j < elem_size_words; j++) { + PLI_UINT32& aval = test_data_vectors[(i * elem_size_words) + j].aval; + test_data_vectors[(i * elem_size_words) + j].bval = UINT32_MAX; + aval = 0; + for (unsigned k = 0; k < sizeof(PLI_UINT32); k++) { + if (count++ == elem_size) break; + aval |= static_cast(test_data[test_data_index++] & 0xFF) << (k * 8); + } + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test vector + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiVectorVal; + arrayvalue.flags = 0; + arrayvalue.value.vectors = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < vec_size; i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.vectors[i].aval, i, + test_data_vectors[i].aval); + } +#endif + + // compare to test data + index -= low; + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; + for (unsigned j = 0; j < elem_size_words; j++) { +#ifdef TEST_VERBOSE + printf("array[%u] == test[%u]\n", (i * elem_size_words) + j, + (offset * elem_size_words) + j); +#endif + CHECK_RESULT_HEX(arrayvalue.value.vectors[(i * elem_size_words) + j].aval, + test_data_vectors[(offset * elem_size_words) + j].aval); + CHECK_RESULT_HEX(arrayvalue.value.vectors[(i * elem_size_words) + j].bval, 0); + } + } + + return 0; +} + +int test_vpiIntVal(char* name, PLI_BYTE8* test_data, int index, const unsigned low, + const unsigned num, const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u low=%u num=%u size=%u elem_size=%u\n\n", __func__, name, + index, low, num, size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + std::vector test_data_integers; + test_data_integers.reserve(size); + for (unsigned i = 0; i < size; i++) { + PLI_INT32& integer = test_data_integers[i]; + integer = 0; + for (unsigned j = 0; j < elem_size; j++) { + integer |= (static_cast(test_data[(i * elem_size) + j]) & 0xFF) << (j * 8); + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < size; i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.integers[i], i, + test_data_integers[i]); + } +#endif + + // compare to test data + index -= low; + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; +#ifdef TEST_VERBOSE + printf("array[%u] == test[%u]\n", i, offset); +#endif + CHECK_RESULT_HEX(arrayvalue.value.integers[i], test_data_integers[offset]); + } + + return 0; +} + +int test_vpiShortIntVal(char* name, PLI_BYTE8* test_data, int index, const unsigned low, + const unsigned num, const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u low=%u num=%u size=%u elem_size=%u\n\n", __func__, name, + index, low, num, size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + std::vector test_data_shortints; + test_data_shortints.reserve(size); + for (unsigned i = 0; i < size; i++) { + if (elem_size == 2) { + test_data_shortints[i] = test_data[i * 2] & 0xFF; + test_data_shortints[i] |= test_data[(i * 2) + 1] << 8; + } else { + test_data_shortints[i] = test_data[i] & 0xFF; + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiShortIntVal; + arrayvalue.flags = 0; + arrayvalue.value.shortints = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < size; i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.shortints[i], i, + test_data_shortints[i]); + } +#endif + + // compare to test data + index -= low; + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; +#ifdef TEST_VERBOSE + printf("array[%u] == test[%u]\n", i, offset); +#endif + CHECK_RESULT_HEX(arrayvalue.value.shortints[i], test_data_shortints[offset]); + } + + return 0; +} + +int test_vpiLongIntVal(char* name, PLI_BYTE8* test_data, int index, const unsigned low, + const unsigned num, const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u low=%u num=%u size=%u elem_size=%u\n\n", __func__, name, + index, low, num, size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + std::vector test_data_longints; + test_data_longints.reserve(size); + for (unsigned i = 0; i < size; i++) { + PLI_INT64& longint = test_data_longints[i]; + longint = 0; + for (unsigned j = 0; j < elem_size; j++) { + longint |= (static_cast(test_data[(i * elem_size) + j]) & 0xFF) << (j * 8); + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiLongIntVal; + arrayvalue.flags = 0; + arrayvalue.value.longints = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // compare to test data + index -= low; + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; +#ifdef TEST_VERBOSE + printf("array[%u] == test[%u]\n", i, offset); +#endif + CHECK_RESULT_HEX(arrayvalue.value.longints[i], test_data_longints[offset]); + } + + return 0; +} + +int mon_check_props() { + // skip test if not verilator (value_array accessors unimplemented in other sims) + if (!TestSimulator::is_verilator()) { +#ifdef VERILATOR + printf("TestSimulator indicating not verilator, but VERILATOR macro is defined\n"); + return 1; +#endif + return 0; + } + + const unsigned NUM_ELEMENTS = 4; + + PLI_BYTE8 read_bytes[NUM_ELEMENTS] + = {static_cast(0xad), static_cast(0xde), + static_cast(0xef), static_cast(0xbe)}; + + PLI_BYTE8 read_shorts[NUM_ELEMENTS * 2] = { + static_cast(0xad), static_cast(0xde), static_cast(0xef), + static_cast(0xbe), static_cast(0xfe), static_cast(0xca), + static_cast(0x0d), static_cast(0xf0)}; + + PLI_BYTE8 read_words[NUM_ELEMENTS * 4] = { + static_cast(0xef), static_cast(0xbe), static_cast(0xad), + static_cast(0xde), static_cast(0x0d), static_cast(0xf0), + static_cast(0xfe), static_cast(0xca), static_cast(0x03), + static_cast(0x02), static_cast(0x01), static_cast(0x00), + static_cast(0x07), static_cast(0x06), static_cast(0x05), + static_cast(0x04)}; + + PLI_BYTE8 read_longs[NUM_ELEMENTS * 8] = { + static_cast(0x0d), static_cast(0xf0), static_cast(0xfe), + static_cast(0xca), static_cast(0xef), static_cast(0xbe), + static_cast(0xad), static_cast(0xde), static_cast(0x07), + static_cast(0x06), static_cast(0x05), static_cast(0x04), + static_cast(0x03), static_cast(0x02), static_cast(0x01), + static_cast(0x00), static_cast(0x0F), static_cast(0x0E), + static_cast(0x0D), static_cast(0x0C), static_cast(0x0B), + static_cast(0x0A), static_cast(0x09), static_cast(0x08), + static_cast(0x17), static_cast(0x16), static_cast(0x15), + static_cast(0x14), static_cast(0x13), static_cast(0x12), + static_cast(0x11), static_cast(0x10)}; + + PLI_BYTE8 read_customs[NUM_ELEMENTS * 9] = { + static_cast(0x0d), static_cast(0xf0), static_cast(0xfe), + static_cast(0xca), static_cast(0xef), static_cast(0xbe), + static_cast(0xad), static_cast(0xde), static_cast(0x1A), + static_cast(0x07), static_cast(0x06), static_cast(0x05), + static_cast(0x04), static_cast(0x03), static_cast(0x02), + static_cast(0x01), static_cast(0x00), static_cast(0x15), + static_cast(0x0F), static_cast(0x0E), static_cast(0x0D), + static_cast(0x0C), static_cast(0x0B), static_cast(0x0A), + static_cast(0x09), static_cast(0x08), static_cast(0x0A), + static_cast(0x17), static_cast(0x16), static_cast(0x15), + static_cast(0x14), static_cast(0x13), static_cast(0x12), + static_cast(0x11), static_cast(0x10), static_cast(0x05)}; + + char read_bytes_name[] = "test.read_bytes"; + char read_bytes_nonzero_index_name[] = "test.read_bytes_nonzero_index"; + char read_bytes_rl_name[] = "test.read_bytes_rl"; + char read_shorts_name[] = "test.read_shorts"; + char read_words_name[] = "test.read_words"; + char read_integers_name[] = "test.read_integers"; + char read_longs_name[] = "test.read_longs"; + char read_customs_name[] = "test.read_customs"; + char read_customs_nonzero_index_rl_name[] = "test.read_customs_nonzero_index_rl"; + + for (unsigned i = 0; i < NUM_ELEMENTS; i++) { + for (unsigned j = 0; j < (NUM_ELEMENTS + 1); j++) { + if (test_vpiRawFourStateVal(read_bytes_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawFourStateVal(read_bytes_nonzero_index_name, read_bytes, i + 1, 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawFourStateVal(read_bytes_rl_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawFourStateVal(read_shorts_name, read_shorts, i, 0, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiRawFourStateVal(read_words_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawFourStateVal(read_integers_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawFourStateVal(read_longs_name, read_longs, i, 0, j, NUM_ELEMENTS, 8)) + return 1; + if (test_vpiRawFourStateVal(read_customs_name, read_customs, i, 0, j, NUM_ELEMENTS, 9)) + return 1; + if (test_vpiRawFourStateVal(read_customs_nonzero_index_rl_name, read_customs, i + 1, 1, + j, NUM_ELEMENTS, 9)) + return 1; + + if (test_vpiRawTwoStateVal(read_bytes_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawTwoStateVal(read_bytes_rl_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawTwoStateVal(read_bytes_nonzero_index_name, read_bytes, i + 1, 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawTwoStateVal(read_shorts_name, read_shorts, i, 0, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiRawTwoStateVal(read_words_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawTwoStateVal(read_integers_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawTwoStateVal(read_longs_name, read_longs, i, 0, j, NUM_ELEMENTS, 8)) + return 1; + if (test_vpiRawTwoStateVal(read_customs_name, read_customs, i, 0, j, NUM_ELEMENTS, 9)) + return 1; + if (test_vpiRawTwoStateVal(read_customs_nonzero_index_rl_name, read_customs, i + 1, 1, + j, NUM_ELEMENTS, 9)) + return 1; + + if (test_vpiVectorVal(read_bytes_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) return 1; + if (test_vpiVectorVal(read_bytes_nonzero_index_name, read_bytes, i + 1, 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiVectorVal(read_bytes_rl_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiVectorVal(read_shorts_name, read_shorts, i, 0, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiVectorVal(read_words_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) return 1; + if (test_vpiVectorVal(read_integers_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiVectorVal(read_longs_name, read_longs, i, 0, j, NUM_ELEMENTS, 8)) return 1; + if (test_vpiVectorVal(read_customs_name, read_customs, i, 0, j, NUM_ELEMENTS, 9)) + return 1; + if (test_vpiVectorVal(read_customs_nonzero_index_rl_name, read_customs, i + 1, 1, j, + NUM_ELEMENTS, 9)) + return 1; + + if (test_vpiShortIntVal(read_bytes_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiShortIntVal(read_bytes_nonzero_index_name, read_bytes, i + 1, 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiShortIntVal(read_bytes_rl_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiShortIntVal(read_shorts_name, read_shorts, i, 0, j, NUM_ELEMENTS, 2)) + return 1; + + if (test_vpiIntVal(read_bytes_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) return 1; + if (test_vpiIntVal(read_bytes_nonzero_index_name, read_bytes, i + 1, 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiIntVal(read_bytes_rl_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) return 1; + if (test_vpiIntVal(read_words_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) return 1; + if (test_vpiIntVal(read_integers_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) return 1; + + if (test_vpiLongIntVal(read_bytes_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiLongIntVal(read_bytes_nonzero_index_name, read_bytes, i + 1, 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiLongIntVal(read_bytes_rl_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiLongIntVal(read_shorts_name, read_shorts, i, 0, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiLongIntVal(read_words_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiLongIntVal(read_integers_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiLongIntVal(read_longs_name, read_longs, i, 0, j, NUM_ELEMENTS, 8)) + return 1; + } + } + + { + // test unsupported format + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_longs", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiRealVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported foramt + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_words", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiShortRealVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported format + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_longs", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiTimeVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported TestVpiHandle + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported type + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_scalar", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test indexp out of bounds + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_bounds", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {4}; + + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + + indexp[0] = 0; + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported flags + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_words", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = vpiUserAllocFlag; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported format & vltype combination + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_words", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiShortIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test num out of bounds + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_words", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, &arrayvalue, indexp, 5); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test null arrayvalue + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_words", NULL); + CHECK_RESULT_NZ(object); + + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, 0, indexp, 0); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test null indexp + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_words", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + + vpi_get_value_array(object, &arrayvalue, 0, 0); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + return 0; +} + +extern "C" int mon_check(void) { return mon_check_props(); } + +#ifdef IS_VPI +static int mon_check_vpi() { + TestVpiHandle href = vpi_handle(vpiSysTfCall, 0); + s_vpi_value vpi_value; + + vpi_value.format = vpiIntVal; + vpi_value.value.integer = mon_check(); + vpi_put_value(href, &vpi_value, NULL, vpiNoDelay); + + return 0; +} + +static s_vpi_systf_data vpi_systf_data[] = {{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$mon_check", + (PLI_INT32(*)(PLI_BYTE8*))mon_check_vpi, 0, 0, 0}, + 0}; + +// cver entry +void vpi_compat_bootstrap(void) { + p_vpi_systf_data systf_data_p; + systf_data_p = &(vpi_systf_data[0]); + while (systf_data_p->type != 0) vpi_register_systf(systf_data_p++); +} + +// icarus entry +void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0}; + +#else + +int main(int argc, char** argv) { + Verilated::commandArgs(argc, argv); + const std::unique_ptr contextp{new VerilatedContext}; + const std::unique_ptr top{new VM_PREFIX{contextp.get(), ""}}; + contextp->fatalOnVpiError(0); + +#ifdef VERILATOR +#ifdef TEST_VERBOSE + contextp->scopesDump(); +#endif +#endif + + while (!contextp->gotFinish()) { top->eval(); } + + return 0; +} +#endif diff --git a/test_regress/t/t_vpi_get_value_array.py b/test_regress/t/t_vpi_get_value_array.py new file mode 100755 index 000000000..9f7ed1832 --- /dev/null +++ b/test_regress/t/t_vpi_get_value_array.py @@ -0,0 +1,23 @@ +#!/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') + +test.compile(make_top_shell=False, + make_main=False, + make_pli=True, + verilator_flags2=["--exe --vpi --no-l2name", test.pli_filename], + iv_flags2=["-g2005-sv -D USE_VPI_NOT_DPI"], + v_flags2=["+define+USE_VPI_NOT_DPI +define+VERILATOR_COMMENTS"]) + +test.execute(use_libvpi=True) + +test.passes() diff --git a/test_regress/t/t_vpi_get_value_array.v b/test_regress/t/t_vpi_get_value_array.v new file mode 100644 index 000000000..86a82df92 --- /dev/null +++ b/test_regress/t/t_vpi_get_value_array.v @@ -0,0 +1,105 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2024 by Diego Roux. 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 + +`ifdef VERILATOR_COMMENTS + `define PUBLIC_FLAT_RD /*verilator public_flat_rd*/ + `define PUBLIC_FLAT_RW /*verilator public_flat_rw*/ +`else + `define PUBLIC_FLAT_RD + `define PUBLIC_FLAT_RW +`endif + +module test (); + +`ifdef VERILATOR +`systemc_header +extern "C" int mon_check(); +`verilog +`endif + + reg [7:0] read_bytes [0:3] `PUBLIC_FLAT_RD; + reg [7:0] read_bytes_nonzero_index [1:4] `PUBLIC_FLAT_RD; + reg [7:0] read_bytes_rl [3:0] `PUBLIC_FLAT_RD; + + reg [15:0] read_shorts [0:3] `PUBLIC_FLAT_RD; + reg [31:0] read_words [0:3] `PUBLIC_FLAT_RD; + reg [31:0] read_words_rl [3:0] `PUBLIC_FLAT_RD; + reg [63:0] read_longs [0:3] `PUBLIC_FLAT_RD; + integer read_integers [0:3] `PUBLIC_FLAT_RD; + reg [68:0] read_customs [0:3] `PUBLIC_FLAT_RD; + reg [68:0] read_customs_nonzero_index_rl [4:1] `PUBLIC_FLAT_RD; + + reg [7:0] read_scalar `PUBLIC_FLAT_RD; + reg [7:0] read_bounds [1:3] `PUBLIC_FLAT_RD; + + integer status; + + initial begin + read_bytes[0] = 8'had; + read_bytes[1] = 8'hde; + read_bytes[2] = 8'hef; + read_bytes[3] = 8'hbe; + + read_bytes_rl[3] = 8'had; + read_bytes_rl[2] = 8'hde; + read_bytes_rl[1] = 8'hef; + read_bytes_rl[0] = 8'hbe; + + read_bytes_nonzero_index[1] = 8'had; + read_bytes_nonzero_index[2] = 8'hde; + read_bytes_nonzero_index[3] = 8'hef; + read_bytes_nonzero_index[4] = 8'hbe; + + read_shorts[0] = 16'hdead; + read_shorts[1] = 16'hbeef; + read_shorts[2] = 16'hcafe; + read_shorts[3] = 16'hf00d; + + read_words[0] = 32'hdeadbeef; + read_words[1] = 32'hcafef00d; + read_words[2] = 32'h00010203; + read_words[3] = 32'h04050607; + + read_integers[0] = 32'hdeadbeef; + read_integers[1] = 32'hcafef00d; + read_integers[2] = 32'h00010203; + read_integers[3] = 32'h04050607; + + read_longs[0] = 64'hdeadbeefcafef00d; + read_longs[1] = 64'h0001020304050607; + read_longs[2] = 64'h08090a0b0c0d0e0f; + read_longs[3] = 64'h1011121314151617; + + read_customs[0] = 69'hFAdeadbeefcafef00d; //0x001F'FFFF'FFFF'FFFF'FFFF + read_customs[1] = 69'hF50001020304050607; + read_customs[2] = 69'h0A08090a0b0c0d0e0f; + read_customs[3] = 69'h051011121314151617; + + read_customs_nonzero_index_rl[4] = 69'hFAdeadbeefcafef00d; //0x001F'FFFF'FFFF'FFFF'FFFF + read_customs_nonzero_index_rl[3] = 69'hF50001020304050607; + read_customs_nonzero_index_rl[2] = 69'h0A08090a0b0c0d0e0f; + read_customs_nonzero_index_rl[1] = 69'h051011121314151617; + + +`ifdef IVERILOG + status = $mon_check; +`endif + +`ifdef VERILATOR + status = $c32("mon_check()"); +`endif + + if (status != 0) begin + $write("%%Error: t_vpi_get_value_array.cpp:%0d: C Test failed\n", status); + $stop; + end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_vpi_memory.cpp b/test_regress/t/t_vpi_memory.cpp index e61cf737a..857ee2d90 100644 --- a/test_regress/t/t_vpi_memory.cpp +++ b/test_regress/t/t_vpi_memory.cpp @@ -52,25 +52,27 @@ void _mon_check_range(const TestVpiHandle& handle, int size, int left, int right int vpisize = vpi_get(vpiSize, handle); TEST_CHECK_EQ(vpisize, size); } - int coherency; + // check coherency + int coherency = 1; { - // check left hand side of range - TestVpiHandle left_h = vpi_handle(vpiLeftRange, handle); - TEST_CHECK_NZ(left_h); - vpi_get_value(left_h, &value); - TEST_CHECK_EQ(value.value.integer, left); - coherency = value.value.integer; + TestVpiHandle iter_h = vpi_iterate(vpiRange, handle); + while (TestVpiHandle range_h = vpi_scan(iter_h)) { + int rangeSize; + TestVpiHandle left_h, right_h; + // get left hand side of range + left_h = vpi_handle(vpiLeftRange, range_h); + TEST_CHECK_NZ(left_h); + vpi_get_value(left_h, &value); + rangeSize = value.value.integer; + // get right hand side of range + right_h = vpi_handle(vpiRightRange, range_h); + TEST_CHECK_NZ(right_h); + vpi_get_value(right_h, &value); + rangeSize = abs(rangeSize - value.value.integer) + 1; + coherency *= rangeSize; + } + iter_h.freed(); } - { - // check right hand side of range - TestVpiHandle right_h = vpi_handle(vpiRightRange, handle); - TEST_CHECK_NZ(right_h); - vpi_get_value(right_h, &value); - TEST_CHECK_EQ(value.value.integer, right); - coherency -= value.value.integer; - } - // calculate size & check - coherency = abs(coherency) + 1; TEST_CHECK_EQ(coherency, size); } @@ -83,8 +85,8 @@ void _mem_check(const char* name, int size, int left, int right, int words) { TEST_CHECK_NZ(mem_h); // check type int vpitype = vpi_get(vpiType, mem_h); - if (vpitype != vpiMemory && vpitype != vpiReg) { - printf("%%Error: %s:%d vpiType neither vpiMemory or vpiReg: %d\n", FILENM, __LINE__, + if (vpitype != vpiRegArray && vpitype != vpiReg) { + printf("%%Error: %s:%d vpiType neither vpiRegArray or vpiReg: %d\n", FILENM, __LINE__, vpitype); errors++; } @@ -96,9 +98,9 @@ void _mem_check(const char* name, int size, int left, int right, int words) { } } // iterate and store - if (vpitype == vpiMemory) { + if (vpitype == vpiRegArray) { _mon_check_range(mem_h, words, words, 1); - TestVpiHandle iter_h = vpi_iterate(vpiMemoryWord, mem_h); + TestVpiHandle iter_h = vpi_iterate(vpiReg, mem_h); int cnt = 0; while (TestVpiHandle lcl_h = vpi_scan(iter_h)) { value.format = vpiIntVal; @@ -112,15 +114,15 @@ void _mem_check(const char* name, int size, int left, int right, int words) { TEST_CHECK_EQ(cnt, words); // should be words addresses } else { int expSize = size * words; - _mon_check_range(mem_h, expSize, expSize - 1, 0); + _mon_check_range(mem_h, expSize, words, 1); value.format = vpiBinStrVal; value.value.str = const_cast(binStr.c_str()); vpi_put_value(mem_h, &value, NULL, vpiNoDelay); TEST_CHECK_Z(vpi_chk_error(&e)); } - if (vpitype == vpiMemory) { + if (vpitype == vpiRegArray) { // iterate and accumulate - TestVpiHandle iter_h = vpi_iterate(vpiMemoryWord, mem_h); + TestVpiHandle iter_h = vpi_iterate(vpiReg, mem_h); int cnt = 0; while (TestVpiHandle lcl_h = vpi_scan(iter_h)) { ++cnt; @@ -148,7 +150,7 @@ void _mem_check(const char* name, int size, int left, int right, int words) { { // make sure trying to get properties that don't exist // doesn't crash - TestVpiHandle iter_h = vpi_iterate(vpiMemoryWord, mem_h); + TestVpiHandle iter_h = vpi_iterate(vpiReg, mem_h); int should_be_undefined = vpi_get(vpiSize, iter_h); TEST_CHECK_EQ(should_be_undefined, vpiUndefined); should_be_undefined = vpi_get(vpiIndex, iter_h); @@ -160,7 +162,7 @@ void _mem_check(const char* name, int size, int left, int right, int words) { should_be_NULL = vpi_handle(vpiScope, iter_h); TEST_CHECK_EQ(should_be_NULL, 0); } - if (vpitype == vpiMemory) { + if (vpitype == vpiRegArray) { // check vpiRange TestVpiHandle iter_h = vpi_iterate(vpiRange, mem_h); TEST_CHECK_NZ(iter_h); diff --git a/test_regress/t/t_vpi_multidim.cpp b/test_regress/t/t_vpi_multidim.cpp new file mode 100644 index 000000000..5041703cf --- /dev/null +++ b/test_regress/t/t_vpi_multidim.cpp @@ -0,0 +1,459 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// 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 +// +//************************************************************************* + +#ifdef IS_VPI + +#include "vpi_user.h" + +#include + +#else + +#include "verilated.h" +#include "verilated_vcd_c.h" +#include "verilated_vpi.h" + +#include "Vt_vpi_multidim.h" +#include "Vt_vpi_multidim__Dpi.h" +#include "svdpi.h" + +#endif + +#include +#include +#include +#include +#include + +// These require the above. Comment prevents clang-format moving them +#include "TestCheck.h" +#include "TestSimulator.h" +#include "TestVpi.h" + +int errors = 0; + +// TEST START + +void _arr_type_check(TestVpiHandle& arr_h, int expType, int expSize, int expRangeHigh, + int expRangeLow) { + const int vpitype = vpi_get(vpiType, arr_h); + TEST_CHECK_EQ(vpitype, expType); + const int vpisize = vpi_get(vpiSize, arr_h); + TEST_CHECK_EQ(vpisize, expSize); + + s_vpi_value value; + value.format = vpiIntVal; + + TestVpiHandle left_h = vpi_handle(vpiLeftRange, arr_h); + TEST_CHECK_NZ(left_h); + vpi_get_value(left_h, &value); + TEST_CHECK_EQ(value.value.integer, expRangeHigh); + + TestVpiHandle right_h = vpi_handle(vpiRightRange, arr_h); + TEST_CHECK_NZ(right_h); + vpi_get_value(right_h, &value); + TEST_CHECK_EQ(value.value.integer, expRangeLow); +} + +void _arr_iter_check(const char* name, int wordSize, const int* lows) { + TestVpiHandle arr_h + = vpi_handle_by_name(const_cast(TestSimulator::rooted(name)), NULL); + TEST_CHECK_NZ(arr_h); + + _arr_type_check(arr_h, vpiRegArray, 4, lows[0] + 1, lows[0]); + + { + // can't iterate through RegArrays on a nested RegArray + TestVpiHandle arr_iter_h = vpi_iterate(vpiRegArray, arr_h); + TEST_CHECK_Z(vpi_scan(arr_iter_h)); + arr_iter_h.freed(); + } + + if (!TestSimulator::is_questa()) { + // but we can access them by index (Questa can't) + for (int idx = lows[0]; idx < lows[0] + 2; idx++) { + TestVpiHandle arr_elem_h = vpi_handle_by_index(arr_h, idx); + TEST_CHECK_NZ(arr_elem_h); + + // first indexing yields size-2 RegArrays + _arr_type_check(arr_elem_h, vpiRegArray, 2, lows[1] + 1, lows[1]); + + for (int idx2 = lows[1]; idx2 < lows[1] + 2; idx2++) { + TestVpiHandle arr_elem2_h = vpi_handle_by_index(arr_elem_h, idx2); + TEST_CHECK_NZ(arr_elem2_h); + + // second indexing yields wordSize Regs + _arr_type_check(arr_elem2_h, vpiReg, wordSize, lows[2] + 1, lows[2]); + } + } + } + + { + // it's also possible to directly iterate through all four Regs + TestVpiHandle arr_iter_h = vpi_iterate(vpiReg, arr_h); + for (int idx = 0; idx < 4; idx++) { + TestVpiHandle arr_elem_h = vpi_scan(arr_iter_h); + TEST_CHECK_NZ(arr_elem_h); + + // which gives us wordSize Regs + _arr_type_check(arr_elem_h, vpiReg, wordSize, lows[2] + 1, lows[2]); + + { + // can't iterate through Regs on a nested Reg + TestVpiHandle arr_iter2_h = vpi_iterate(vpiReg, arr_elem_h); + TEST_CHECK_Z(vpi_scan(arr_iter2_h)); + arr_iter2_h.freed(); + } + + // but we can access them by index + for (int idx2 = lows[2]; idx2 < lows[2] + 2; idx2++) { + TestVpiHandle arr_elem2_h = vpi_handle_by_index(arr_elem_h, idx2); + TEST_CHECK_NZ(arr_elem2_h); + + // first indexing yields wordSize / 2 Regs + _arr_type_check(arr_elem2_h, vpiReg, wordSize / 2, lows[3] + wordSize / 2 - 1, + lows[3]); + + for (int idx3 = lows[3]; idx3 < lows[3] + wordSize / 2; idx3++) { + TestVpiHandle arr_elem3_h = vpi_handle_by_index(arr_elem2_h, idx3); + TEST_CHECK_NZ(arr_elem3_h); + { + // second indexing yields size-1 RegBits (no support for RegBit VPI type + // yet) + const int vpitype = vpi_get(vpiType, arr_elem3_h); + if (TestSimulator::is_verilator()) { + TEST_CHECK_EQ(vpitype, vpiReg); + } else { + TEST_CHECK_EQ(vpitype, vpiRegBit); + } + const int vpisize = vpi_get(vpiSize, arr_elem3_h); + TEST_CHECK_EQ(vpisize, 1); + } + } + } + + // iterating through packed ranges + TestVpiHandle range_iter_h = vpi_iterate(vpiRange, arr_elem_h); + for (int idx2 = 0; idx2 < 2; idx2++) { + TestVpiHandle range_h = vpi_scan(range_iter_h); + TEST_CHECK_NZ(range_h); + { + s_vpi_value value; + value.format = vpiIntVal; + TestVpiHandle side_h = vpi_handle(vpiLeftRange, range_h); + TEST_CHECK_NZ(side_h); + vpi_get_value(side_h, &value); + if (idx2 == 0) { + TEST_CHECK_EQ(value.value.integer, lows[2] + 1); + } else { + TEST_CHECK_EQ(value.value.integer, lows[3] + wordSize / 2 - 1); + } + side_h = vpi_handle(vpiRightRange, range_h); + TEST_CHECK_NZ(side_h); + vpi_get_value(side_h, &value); + if (idx2 == 0) { + TEST_CHECK_EQ(value.value.integer, lows[2]); + } else { + TEST_CHECK_EQ(value.value.integer, lows[3]); + } + } + } + TEST_CHECK_Z(vpi_scan(range_iter_h)); + range_iter_h.freed(); + } + TEST_CHECK_Z(vpi_scan(arr_iter_h)); + arr_iter_h.freed(); + } + + { + // iterating through unpacked ranges + TestVpiHandle range_iter_h = vpi_iterate(vpiRange, arr_h); + for (int idx = 0; idx < 2; idx++) { + TestVpiHandle range_h = vpi_scan(range_iter_h); + TEST_CHECK_NZ(range_h); + { + s_vpi_value value; + value.format = vpiIntVal; + TestVpiHandle side_h = vpi_handle(vpiLeftRange, range_h); + TEST_CHECK_NZ(side_h); + vpi_get_value(side_h, &value); + if (idx == 0) { + TEST_CHECK_EQ(value.value.integer, lows[0] + 1); + } else { + TEST_CHECK_EQ(value.value.integer, lows[1] + 1); + } + side_h = vpi_handle(vpiRightRange, range_h); + TEST_CHECK_NZ(side_h); + vpi_get_value(side_h, &value); + if (idx == 0) { + TEST_CHECK_EQ(value.value.integer, lows[0]); + } else { + TEST_CHECK_EQ(value.value.integer, lows[1]); + } + } + } + TEST_CHECK_Z(vpi_scan(range_iter_h)); + range_iter_h.freed(); + } +} + +void _arr_access_format_check(TestVpiHandle& reg_h, int wordSize, const int* lows, + const char* octVal_s, PLI_INT32 format) { + const int spanSize = wordSize / 2; + s_vpi_value value_in; + s_vpi_value value_out; + s_vpi_error_info e; + char zero_s[2] = "0"; + + // zero out the vector + value_in.format = vpiOctStrVal; + value_in.value.str = zero_s; + vpi_put_value(reg_h, &value_in, NULL, vpiNoDelay); + TEST_CHECK_Z(vpi_chk_error(&e)); + + value_in.format = format; + value_out.format = format; + + for (int i = 0; i < 2; i++) { + TestVpiHandle subreg_h = vpi_handle_by_index(reg_h, lows[2] + i); + TEST_CHECK_NZ(subreg_h); + + char octSpan_s[spanSize / 3 + 1]; + strncpy(octSpan_s, &octVal_s[spanSize / 3 * (1 - i)], spanSize / 3); + octSpan_s[spanSize / 3] = '\0'; + + uint64_t intVal; + t_vpi_vecval vecVal[2]; + sscanf(octSpan_s, "%" SCNo64, &intVal); + char strVal_s[spanSize + 1]; // max length of the string happens for binary + + if (format == vpiIntVal) { + value_in.value.integer = intVal; + } else if (format == vpiVectorVal) { + if (spanSize > 32) { + vecVal[1].aval = intVal >> 32; + vecVal[1].bval = 0; + } + vecVal[0].aval = intVal; + vecVal[0].bval = 0; + value_in.value.vector = vecVal; + } else if (format == vpiBinStrVal) { + for (int j = 0; j < spanSize; j++) + strVal_s[j] = (intVal >> (spanSize - j - 1)) % 2 + '0'; + strVal_s[spanSize] = '\0'; + value_in.value.str = strVal_s; + } else if (format == vpiDecStrVal) { + sprintf(strVal_s, "%" PRIu64, intVal); + value_in.value.str = strVal_s; + } else if (format == vpiHexStrVal) { + sprintf(strVal_s, "%0*" PRIx64, (spanSize + 3) / 4, intVal); + value_in.value.str = strVal_s; + } else if (format == vpiOctStrVal) { + sprintf(strVal_s, "%" PRIo64, intVal); + value_in.value.str = strVal_s; + } else if (format == vpiStringVal) { + const int byteCount = (spanSize + 7) / 8; + for (int j = 0; j < byteCount; j++) + strVal_s[j] = (intVal >> (8 * (byteCount - j - 1))) & 0xff; + strVal_s[byteCount] = '\0'; + value_in.value.str = strVal_s; + } + + vpi_put_value(subreg_h, &value_in, NULL, vpiNoDelay); + TEST_CHECK_Z(vpi_chk_error(&e)); + + vpi_get_value(subreg_h, &value_out); + switch (format) { + case vpiIntVal: TEST_CHECK_EQ(value_out.value.integer, value_in.value.integer); break; + case vpiVectorVal: + if (spanSize > 32) + TEST_CHECK_EQ(value_out.value.vector[1].aval, value_in.value.vector[1].aval); + TEST_CHECK_EQ(value_out.value.vector[0].aval, value_in.value.vector[0].aval); + break; + case vpiStringVal: + TEST_CHECK_EQ(value_out.value.str[0], + value_in.value.str[0] ? value_in.value.str[0] : ' '); + break; + case vpiBinStrVal: + case vpiDecStrVal: + case vpiHexStrVal: + case vpiOctStrVal: TEST_CHECK_CSTR(value_out.value.str, value_in.value.str); break; + } + } + + // validate the resulting flattened vector + value_out.format = vpiOctStrVal; + vpi_get_value(reg_h, &value_out); + TEST_CHECK_CSTR(value_out.value.str, octVal_s); +} + +std::default_random_engine rng; + +void _arr_access_check(const char* name, int wordSize, const int* lows) { + TestVpiHandle arr_h + = vpi_handle_by_name(const_cast(TestSimulator::rooted(name)), NULL); + TEST_CHECK_NZ(arr_h); + + std::uniform_int_distribution rand64(std::numeric_limits::min(), + std::numeric_limits::max()); + + char octVal_s[wordSize / 3 + 1]; + + // fill octVal_s with random octal digits + if (wordSize < 64) { + sprintf(octVal_s, "%0*" PRIo64, wordSize / 3, + rand64(rng) % (static_cast(1) << wordSize)); + } else { + sprintf(octVal_s, "%0*" PRIo64, 63 / 3, rand64(rng)); + sprintf(octVal_s + 63 / 3, "%0*" PRIo64, (wordSize - 63) / 3, + rand64(rng) % (static_cast(1) << (wordSize - 63))); + } + + // Assume that reading/writing to the "flattened" packed register is already tested, + // check only reading/writing to sub-regs and validate the flattened result. + { + TestVpiHandle arr_iter_h = vpi_iterate(vpiReg, arr_h); + while (TestVpiHandle reg_h = vpi_scan(arr_iter_h)) { + s_vpi_value value_in; + s_vpi_value value_out; + s_vpi_error_info e; + + value_out.format = vpiOctStrVal; + value_in.format = vpiOctStrVal; + value_in.value.str = octVal_s; + vpi_put_value(reg_h, &value_in, NULL, vpiNoDelay); + TEST_CHECK_Z(vpi_chk_error(&e)); + vpi_get_value(reg_h, &value_out); + TEST_CHECK_CSTR(value_out.value.str, octVal_s); + + // test each I/O data format + if (wordSize <= 64) { + _arr_access_format_check(reg_h, wordSize, lows, octVal_s, vpiIntVal); + _arr_access_format_check(reg_h, wordSize, lows, octVal_s, vpiDecStrVal); + } + _arr_access_format_check(reg_h, wordSize, lows, octVal_s, vpiVectorVal); + _arr_access_format_check(reg_h, wordSize, lows, octVal_s, vpiBinStrVal); + _arr_access_format_check(reg_h, wordSize, lows, octVal_s, vpiOctStrVal); + _arr_access_format_check(reg_h, wordSize, lows, octVal_s, vpiHexStrVal); + _arr_access_format_check(reg_h, wordSize, lows, octVal_s, vpiStringVal); + } + arr_iter_h.freed(); + } +} + +struct params { + const char* name; + int wordSize; + const int lows[4]; +}; + +void _multidim_check() { + static struct params values[] + = {{"arr_cdata", 6, {0, 1, 2, 3}}, {"arr_sdata", 12, {4, 5, 6, 7}}, + {"arr_idata", 30, {8, 9, 10, 11}}, {"arr_qdata", 60, {12, 13, 14, 15}}, + {"arr_wdata", 126, {16, 17, 18, 19}}, {nullptr, 0, {0, 0, 0, 0}}}; + struct params* value = values; + while (value->name) { + _arr_iter_check(value->name, value->wordSize, value->lows); + _arr_access_check(value->name, value->wordSize, value->lows); + value++; + } +} + +// TEST END + +extern "C" int mon_check() { + // Callback from initial block in monitor + //if (int status = _mon_check_param()) return status; + printf("-mon_check()\n"); + _multidim_check(); + return errors; +} + +#ifdef IS_VPI + +static int mon_check_vpi() { + TestVpiHandle href = vpi_handle(vpiSysTfCall, 0); + s_vpi_value vpi_value; + + vpi_value.format = vpiIntVal; + vpi_value.value.integer = mon_check(); + vpi_put_value(href, &vpi_value, NULL, vpiNoDelay); + + return 0; +} + +static s_vpi_systf_data vpi_systf_data[] = {{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$mon_check", + (PLI_INT32(*)(PLI_BYTE8*))mon_check_vpi, 0, 0, 0}, + 0}; + +void vpi_compat_bootstrap(void) { + p_vpi_systf_data systf_data_p; + systf_data_p = &(vpi_systf_data[0]); + while (systf_data_p->type != 0) vpi_register_systf(systf_data_p++); +} + +void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0}; + +#else + +int main(int argc, char** argv) { + const std::unique_ptr contextp{new VerilatedContext}; + + uint64_t sim_time = 1100; // TODO + contextp->debug(0); + contextp->commandArgs(argc, argv); + + const std::unique_ptr topp{new VM_PREFIX{contextp.get(), + // Note null name - we're flattening it out + ""}}; + +#ifdef VERILATOR +#ifdef TEST_VERBOSE + contextp->scopesDump(); +#endif +#endif + +#if VM_TRACE + contextp->traceEverOn(true); + VL_PRINTF("Enabling waves...\n"); + VerilatedVcdC* tfp = new VerilatedVcdC; + topp->trace(tfp, 99); + tfp->open(STRINGIFY(TEST_OBJ_DIR) "/simx.vcd"); +#endif + + topp->eval(); + topp->clk = 0; + contextp->timeInc(10); + + while (contextp->time() < sim_time && !contextp->gotFinish()) { + contextp->timeInc(1); + topp->eval(); + VerilatedVpi::callValueCbs(); + topp->clk = !topp->clk; + // mon_do(); +#if VM_TRACE + if (tfp) tfp->dump(main_time); +#endif + } + if (!contextp->gotFinish()) { + vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish"); + } + topp->final(); + +#if VM_TRACE + if (tfp) tfp->close(); +#endif + + return 0; +} + +#endif diff --git a/test_regress/t/t_vpi_multidim.py b/test_regress/t/t_vpi_multidim.py new file mode 100755 index 000000000..ab0961ef9 --- /dev/null +++ b/test_regress/t/t_vpi_multidim.py @@ -0,0 +1,22 @@ +#!/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') + +test.compile(make_top_shell=False, + make_main=False, + make_pli=True, + v_flags2=["+define+USE_VPI_NOT_DPI"], + verilator_flags2=["--exe --vpi --no-l2name --public-flat-rw", test.pli_filename]) + +test.execute(use_libvpi=True) + +test.passes() diff --git a/test_regress/t/t_vpi_multidim.v b/test_regress/t/t_vpi_multidim.v new file mode 100644 index 000000000..2cdd39c9b --- /dev/null +++ b/test_regress/t/t_vpi_multidim.v @@ -0,0 +1,44 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// 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 + +module t (/*AUTOARG*/ + // Inputs + clk + ); /*verilator public_module*/ + +`ifdef VERILATOR +`systemc_header +extern "C" int mon_check(); +`verilog +`endif + + input clk; + + logic [3:2][5:3] arr_cdata [1:0][2:1]; // 2x3 (6) bit words + logic [7:6][12:7] arr_sdata [5:4][6:5]; // 2x6 (12) bit words + logic [11:10][25:11] arr_idata [9:8][10:9]; // 2x15 (30) bit words + logic [15:14][44:15] arr_qdata [13:12][14:13]; // 2x30 (60) bit words + logic [19:18][81:19] arr_wdata [17:16][18:17]; // 2x63 (126) bit words + + int status; + + initial begin +`ifdef VERILATOR + status = $c32("mon_check()"); +`else + status = $mon_check(); +`endif + if (status!=0) begin + $write("%%Error: t_vpi_multidim.cpp:%0d: C Test failed\n", status); + $stop; + end + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule : t diff --git a/test_regress/t/t_vpi_package.cpp b/test_regress/t/t_vpi_package.cpp index 4feb00a48..c2ba1eb52 100644 --- a/test_regress/t/t_vpi_package.cpp +++ b/test_regress/t/t_vpi_package.cpp @@ -116,10 +116,12 @@ int mon_check() { CHECK_RESULT_Z(count_params(unitHandle, 1)); CHECK_RESULT_Z(count_params(pkgHandle, 2)); - CHECK_RESULT_Z(count_params(tHandle, 3)); + CHECK_RESULT_Z(count_params(tHandle, 6)); CHECK_RESULT_Z(check_handle(const_cast("someOtherInt"), tHandle)) CHECK_RESULT_Z(check_handle(const_cast("t.someOtherInt"), NULL)) + CHECK_RESULT_Z(check_handle(const_cast("someString"), tHandle)) + CHECK_RESULT_Z(check_handle(const_cast("t.someString"), NULL)) CHECK_RESULT_Z(check_handle(const_cast("someInt"), pkgHandle)) CHECK_RESULT_Z(check_handle(const_cast("somepackage::someInt"), NULL)) CHECK_RESULT_Z(check_handle(const_cast("dollarUnitInt"), unitHandle)) diff --git a/test_regress/t/t_vpi_package.py b/test_regress/t/t_vpi_package.py index 61cf61c9e..91593a3fe 100755 --- a/test_regress/t/t_vpi_package.py +++ b/test_regress/t/t_vpi_package.py @@ -14,7 +14,7 @@ test.scenarios('simulator') test.compile(make_top_shell=False, make_main=False, make_pli=True, - verilator_flags2=["--exe --vpi --no-l2name", test.pli_filename]) + verilator_flags2=["--exe --vpi --no-l2name --public-depth 1", test.pli_filename]) test.execute(use_libvpi=True) diff --git a/test_regress/t/t_vpi_package.v b/test_regress/t/t_vpi_package.v index 9f9ca4161..b69e2cdad 100644 --- a/test_regress/t/t_vpi_package.v +++ b/test_regress/t/t_vpi_package.v @@ -8,19 +8,24 @@ import "DPI-C" context function int mon_check(); -parameter int dollarUnitInt /*verilator public_flat_rd*/ = 3; +parameter int dollarUnitInt = 3; package somepackage; - parameter int someInt /*verilator public_flat_rd*/ = 5; - parameter int anotherInt /*verilator public_flat_rd*/ = 6; + parameter int someInt = 5; + parameter int anotherInt = 6; endpackage module t (/*AUTOARG*/ - ); /*verilator public_module*/ + ); - parameter int someOtherInt /* verilator public_flat_rd*/ = 7; - parameter int yetAnotherInt /* verilator public_flat_rd*/ = 9; - parameter int stillAnotherInt /* verilator public_flat_rd*/ = 17; + parameter int someOtherInt = 7; + parameter int yetAnotherInt = 9; + parameter int stillAnotherInt = 17; + parameter int register = 0; + parameter int n_str = 2; + // Edge case with pvi code generation + parameter string someString [n_str] = '{default: ""}; + logic reference; integer status; diff --git a/test_regress/t/t_vpi_public_depth.cpp b/test_regress/t/t_vpi_public_depth.cpp index cb4da699c..e1bf6e2a6 100644 --- a/test_regress/t/t_vpi_public_depth.cpp +++ b/test_regress/t/t_vpi_public_depth.cpp @@ -26,6 +26,8 @@ #include "Vt_vpi_public_depth__Dpi.h" #elif defined(T_VPI_PUBLIC_DEPTH_OFF) #include "Vt_vpi_public_depth_off__Dpi.h" +#elif defined(T_VPI_PUBLIC_OFF) +#include "Vt_vpi_public_off__Dpi.h" #else #error "Bad test" #endif @@ -87,6 +89,15 @@ int mon_check() { it.freed(); // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle CHECK_RESULT_Z(topmod_done_should_be_0); + TestVpiHandle mod_a = vpi_handle_by_name(const_cast("\\mod.a "), topmod); +#if defined(T_VPI_PUBLIC_OFF) + // metacomment from module A should be ignored + CHECK_RESULT_Z(mod_a); + return 0; +#endif + + CHECK_RESULT_NZ(mod_a); + TestVpiHandle it2 = vpi_iterate(vpiModule, topmod); CHECK_RESULT_NZ(it2); diff --git a/test_regress/t/t_vpi_public_depth.v b/test_regress/t/t_vpi_public_depth.v index eeaedae9a..3766dfec3 100644 --- a/test_regress/t/t_vpi_public_depth.v +++ b/test_regress/t/t_vpi_public_depth.v @@ -65,7 +65,8 @@ module A(/*AUTOARG*/ clk, a, b ); - input clk; + // this comment should get ignored for public-ignore + input clk /* verilator public_flat_rw */; input a, b; output x; diff --git a/test_regress/t/t_vpi_public_off.py b/test_regress/t/t_vpi_public_off.py new file mode 100755 index 000000000..34b624b8e --- /dev/null +++ b/test_regress/t/t_vpi_public_off.py @@ -0,0 +1,29 @@ +#!/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') +test.pli_filename = "t/t_vpi_public_depth.cpp" +test.top_filename = "t/t_vpi_public_depth.v" + +test.compile(make_top_shell=False, + make_main=False, + make_pli=True, + iv_flags2=["-g2005-sv"], + verilator_flags2=[ + "+define+USE_DOLLAR_C32 --exe --vpi --no-l2name", + test.pli_filename, + "--public-depth 1 --public-ignore", + ], + make_flags=['CPPFLAGS_ADD=-DTEST_VPI_PUBLIC_OFF']) + +test.execute(use_libvpi=True) + +test.passes() diff --git a/test_regress/t/t_vpi_put_value_array.cpp b/test_regress/t/t_vpi_put_value_array.cpp new file mode 100644 index 000000000..2e9008ba4 --- /dev/null +++ b/test_regress/t/t_vpi_put_value_array.cpp @@ -0,0 +1,759 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2024 by Diego Roux. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* + +#ifndef IS_VPI + +#include "verilated.h" +#include "verilated_vpi.h" + +#include "Vt_vpi_put_value_array.h" + +#endif + +// These require the above. Comment prevents clang-format moving them +#include "TestSimulator.h" +#include "TestVpi.h" + +#include + +//====================================================================== + +int test_vpiRawFourStateVal(char* name, PLI_BYTE8* test_data, int index, const unsigned num, + const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u num=%u size=%u elem_size=%u\n\n", __func__, name, index, num, + size, elem_size); +#endif + + // prepare index and test data arrays + int index_arr[1] = {index}; + std::vector test_data_four_state; + test_data_four_state.reserve(size * elem_size * 2); + for (unsigned i = 0; i < size; i++) { + for (unsigned j = 0; j < elem_size; j++) { + test_data_four_state[(i * 2 * elem_size) + j] = test_data[(i * elem_size) + j]; + } + for (unsigned j = 0; j < elem_size; j++) { + test_data_four_state[(((i * 2) + 1) * elem_size) + j] = -1; // bval should be ignored + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiRawFourStateVal; + arrayvalue.flags = 0; + arrayvalue.value.rawvals = test_data_four_state.data(); + vpi_put_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // get value to nu + arrayvalue.value.rawvals = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, size); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < (2 * size * elem_size); i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.rawvals[i] & 0xFF, i, + test_data_four_state[i] & 0xFF); + } +#endif + // compare to test data + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; + for (unsigned j = 0; j < elem_size; j++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", (i * 2 * elem_size) + j, (i * elem_size) + j); +#endif + CHECK_RESULT_HEX(arrayvalue.value.rawvals[(i * 2 * elem_size) + j], + test_data[(i * elem_size) + j]); + } + for (unsigned j = 0; j < elem_size; j++) { + CHECK_RESULT_HEX(arrayvalue.value.rawvals[(((i * 2) + 1) * elem_size) + j], 0); + } + } + + return 0; +} + +int test_vpiRawTwoStateVal(char* name, PLI_BYTE8* test_data, int index, const unsigned num, + const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u num=%u size=%u elem_size=%u\n\n", __func__, name, index, num, + size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiRawTwoStateVal; + arrayvalue.flags = 0; + arrayvalue.value.rawvals = test_data; + vpi_put_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // get value to check + arrayvalue.value.rawvals = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, size); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < (size * elem_size); i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.rawvals[i] & 0xFF, i, + test_data[i] & 0xFF); + } +#endif + + // compare to test data + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; + for (unsigned j = 0; j < elem_size; j++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", (i * elem_size) + j, (i * elem_size) + j); +#endif + CHECK_RESULT_HEX(arrayvalue.value.rawvals[(i * elem_size) + j], + test_data[(i * elem_size) + j]); + } + } + + return 0; +} + +int test_vpiVectorVal(char* name, PLI_BYTE8* test_data, int index, const unsigned num, + const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u num=%u size=%u elem_size=%u\n\n", __func__, name, index, num, + size, elem_size); +#endif + // prepare index + int index_arr[1] = {index}; + const unsigned elem_size_words = (elem_size + 3) / sizeof(PLI_UINT32); + const unsigned vec_size = elem_size_words * size; + std::vector test_data_vectors; + test_data_vectors.reserve(vec_size); + unsigned test_data_index = 0; + for (unsigned i = 0; i < size; i++) { + unsigned count = 0; + for (unsigned j = 0; j < elem_size_words; j++) { + PLI_UINT32& aval = test_data_vectors[(i * elem_size_words) + j].aval; + test_data_vectors[(i * elem_size_words) + j].bval = UINT32_MAX; + aval = 0; + for (unsigned k = 0; k < sizeof(PLI_UINT32); k++) { + if (count++ == elem_size) break; + aval |= static_cast(test_data[test_data_index++] & 0xFF) << (k * 8); + } + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiVectorVal; + arrayvalue.flags = 0; + arrayvalue.value.vectors = test_data_vectors.data(); + vpi_put_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // get value to check + arrayvalue.value.vectors = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, size); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < vec_size; i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.vectors[i].aval, i, + test_data_vectors[i].aval); + } +#endif + + // compare to test data + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; + for (unsigned j = 0; j < elem_size_words; j++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", (i * elem_size_words) + j, (i * elem_size_words) + j); +#endif + CHECK_RESULT_HEX(arrayvalue.value.vectors[(i * elem_size_words) + j].aval, + test_data_vectors[(i * elem_size_words) + j].aval); + } + for (unsigned j = 0; j < elem_size_words; j++) { + CHECK_RESULT_HEX(arrayvalue.value.vectors[(i * elem_size_words) + j].bval, 0); + } + } + + return 0; +} + +int test_vpiIntVal(char* name, PLI_BYTE8* test_data, int index, const unsigned num, + const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u num=%u size=%u elem_size=%u\n\n", __func__, name, index, num, + size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + std::vector test_data_integers; + test_data_integers.reserve(size); + for (unsigned i = 0; i < size; i++) { + PLI_INT32& integer = test_data_integers[i]; + integer = 0; + for (unsigned j = 0; j < elem_size; j++) { + integer |= (static_cast(test_data[(i * elem_size) + j]) & 0xFF) << (j * 8); + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = test_data_integers.data(); + vpi_put_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // get value to check + arrayvalue.value.vectors = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, size); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < size; i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.integers[i], i, + test_data_integers[i]); + } +#endif + + // compare to test data + for (unsigned i = 0; i < num; i++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", i, i); +#endif + CHECK_RESULT_HEX(arrayvalue.value.integers[i], test_data_integers[i]); + } + + return 0; +} + +int test_vpiShortIntVal(char* name, PLI_BYTE8* test_data, int index, const unsigned num, + const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u num=%u size=%u elem_size=%u\n\n", __func__, name, index, num, + size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + std::vector test_data_shortints; + test_data_shortints.reserve(size); + for (unsigned i = 0; i < size; i++) { + if (elem_size == 2) { + test_data_shortints[i] = test_data[i * 2] & 0xFF; + test_data_shortints[i] |= test_data[(i * 2) + 1] << 8; + } else { + test_data_shortints[i] = test_data[i] & 0xFF; + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiShortIntVal; + arrayvalue.flags = 0; + arrayvalue.value.shortints = test_data_shortints.data(); + vpi_put_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // get value to check + arrayvalue.value.vectors = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, size); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < size; i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.shortints[i], i, + test_data_shortints[i]); + } +#endif + + // compare to test data + for (unsigned i = 0; i < num; i++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", i, i); +#endif + CHECK_RESULT_HEX(arrayvalue.value.shortints[i], test_data_shortints[i]); + } + + return 0; +} + +int test_vpiLongIntVal(char* name, PLI_BYTE8* test_data, int index, const unsigned num, + const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u num=%u size=%u elem_size=%u\n\n", __func__, name, index, num, + size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + std::vector test_data_longints; + test_data_longints.reserve(size); + for (unsigned i = 0; i < size; i++) { + PLI_INT64& longint = test_data_longints[i]; + longint = 0; + for (unsigned j = 0; j < elem_size; j++) { + longint |= (static_cast(test_data[(i * elem_size) + j]) & 0xFF) << (j * 8); + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiLongIntVal; + arrayvalue.flags = 0; + arrayvalue.value.longints = test_data_longints.data(); + vpi_put_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // get value to check + arrayvalue.value.vectors = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, size); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // compare to test data + for (unsigned i = 0; i < num; i++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", i, i); +#endif + CHECK_RESULT_HEX(arrayvalue.value.longints[i], test_data_longints[i]); + } + + return 0; +} + +int mon_check_props(void) { + // skip test if not verilator (value_array accessors unimplemented in other sims) + if (!TestSimulator::is_verilator()) { +#ifdef VERILATOR + printf("TestSimulator indicating not verilator, but VERILATOR macro is defined\n"); + return 1; +#endif + return 0; + } + + const unsigned NUM_ELEMENTS = 4; + + PLI_BYTE8 write_bytes[NUM_ELEMENTS] + = {static_cast(0xad), static_cast(0xde), + static_cast(0xef), static_cast(0xbe)}; + + PLI_BYTE8 write_shorts[NUM_ELEMENTS * 2] = { + static_cast(0xad), static_cast(0xde), static_cast(0xef), + static_cast(0xbe), static_cast(0xfe), static_cast(0xca), + static_cast(0x0d), static_cast(0xf0)}; + + PLI_BYTE8 write_words[NUM_ELEMENTS * 4] = { + static_cast(0xef), static_cast(0xbe), static_cast(0xad), + static_cast(0xde), static_cast(0x0d), static_cast(0xf0), + static_cast(0xfe), static_cast(0xca), static_cast(0x03), + static_cast(0x02), static_cast(0x01), static_cast(0x00), + static_cast(0x07), static_cast(0x06), static_cast(0x05), + static_cast(0x04)}; + + PLI_BYTE8 write_longs[NUM_ELEMENTS * 8] = { + static_cast(0x0d), static_cast(0xf0), static_cast(0xfe), + static_cast(0xca), static_cast(0xef), static_cast(0xbe), + static_cast(0xad), static_cast(0xde), static_cast(0x07), + static_cast(0x06), static_cast(0x05), static_cast(0x04), + static_cast(0x03), static_cast(0x02), static_cast(0x01), + static_cast(0x00), static_cast(0x0F), static_cast(0x0E), + static_cast(0x0D), static_cast(0x0C), static_cast(0x0B), + static_cast(0x0A), static_cast(0x09), static_cast(0x08), + static_cast(0x17), static_cast(0x16), static_cast(0x15), + static_cast(0x14), static_cast(0x13), static_cast(0x12), + static_cast(0x11), static_cast(0x10)}; + + PLI_BYTE8 write_customs[NUM_ELEMENTS * 9] = { + static_cast(0x0d), static_cast(0xf0), static_cast(0xfe), + static_cast(0xca), static_cast(0xef), static_cast(0xbe), + static_cast(0xad), static_cast(0xde), static_cast(0x1A), + static_cast(0x07), static_cast(0x06), static_cast(0x05), + static_cast(0x04), static_cast(0x03), static_cast(0x02), + static_cast(0x01), static_cast(0x00), static_cast(0x15), + static_cast(0x0F), static_cast(0x0E), static_cast(0x0D), + static_cast(0x0C), static_cast(0x0B), static_cast(0x0A), + static_cast(0x09), static_cast(0x08), static_cast(0x0A), + static_cast(0x17), static_cast(0x16), static_cast(0x15), + static_cast(0x14), static_cast(0x13), static_cast(0x12), + static_cast(0x11), static_cast(0x10), static_cast(0x05)}; + + char write_bytes_name[] = "test.write_bytes"; + char write_bytes_nonzero_index_name[] = "test.write_bytes_nonzero_index"; + char write_bytes_rl_name[] = "test.write_bytes_rl"; + char write_shorts_name[] = "test.write_shorts"; + char write_words_name[] = "test.write_words"; + char write_integers_name[] = "test.write_integers"; + char write_longs_name[] = "test.write_longs"; + char write_customs_name[] = "test.write_customs"; + char write_customs_nonzero_index_rl_name[] = "test.write_customs_nonzero_index_rl"; + + for (unsigned i = 0; i < NUM_ELEMENTS; i++) { + for (unsigned j = 0; j < (NUM_ELEMENTS + 1); j++) { + if (test_vpiRawFourStateVal(write_bytes_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawFourStateVal(write_bytes_nonzero_index_name, write_bytes, i + 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawFourStateVal(write_bytes_rl_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawFourStateVal(write_shorts_name, write_shorts, i, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiRawFourStateVal(write_words_name, write_words, i, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawFourStateVal(write_integers_name, write_words, i, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawFourStateVal(write_longs_name, write_longs, i, j, NUM_ELEMENTS, 8)) + return 1; + if (test_vpiRawFourStateVal(write_customs_name, write_customs, i, j, NUM_ELEMENTS, 9)) + return 1; + if (test_vpiRawFourStateVal(write_customs_nonzero_index_rl_name, write_customs, i + 1, + j, NUM_ELEMENTS, 9)) + return 1; + + if (test_vpiRawTwoStateVal(write_bytes_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawTwoStateVal(write_bytes_rl_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawTwoStateVal(write_bytes_nonzero_index_name, write_bytes, i + 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawTwoStateVal(write_shorts_name, write_shorts, i, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiRawTwoStateVal(write_words_name, write_words, i, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawTwoStateVal(write_integers_name, write_words, i, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawTwoStateVal(write_longs_name, write_longs, i, j, NUM_ELEMENTS, 8)) + return 1; + if (test_vpiRawTwoStateVal(write_customs_name, write_customs, i, j, NUM_ELEMENTS, 9)) + return 1; + if (test_vpiRawTwoStateVal(write_customs_nonzero_index_rl_name, write_customs, i + 1, + j, NUM_ELEMENTS, 9)) + return 1; + + if (test_vpiVectorVal(write_bytes_name, write_bytes, i, j, NUM_ELEMENTS, 1)) return 1; + if (test_vpiVectorVal(write_bytes_nonzero_index_name, write_bytes, i + 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiVectorVal(write_bytes_rl_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiVectorVal(write_shorts_name, write_shorts, i, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiVectorVal(write_words_name, write_words, i, j, NUM_ELEMENTS, 4)) return 1; + if (test_vpiVectorVal(write_integers_name, write_words, i, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiVectorVal(write_longs_name, write_longs, i, j, NUM_ELEMENTS, 8)) return 1; + if (test_vpiVectorVal(write_customs_name, write_customs, i, j, NUM_ELEMENTS, 9)) + return 1; + if (test_vpiVectorVal(write_customs_nonzero_index_rl_name, write_customs, i + 1, j, + NUM_ELEMENTS, 9)) + return 1; + + if (test_vpiShortIntVal(write_bytes_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiShortIntVal(write_bytes_nonzero_index_name, write_bytes, i + 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiShortIntVal(write_bytes_rl_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiShortIntVal(write_shorts_name, write_shorts, i, j, NUM_ELEMENTS, 2)) + return 1; + + if (test_vpiIntVal(write_bytes_name, write_bytes, i, j, NUM_ELEMENTS, 1)) return 1; + if (test_vpiIntVal(write_bytes_nonzero_index_name, write_bytes, i + 1, j, NUM_ELEMENTS, + 1)) + return 1; + if (test_vpiIntVal(write_bytes_rl_name, write_bytes, i, j, NUM_ELEMENTS, 1)) return 1; + if (test_vpiIntVal(write_words_name, write_words, i, j, NUM_ELEMENTS, 4)) return 1; + if (test_vpiIntVal(write_integers_name, write_words, i, j, NUM_ELEMENTS, 4)) return 1; + + if (test_vpiLongIntVal(write_bytes_name, write_bytes, i, j, NUM_ELEMENTS, 1)) return 1; + if (test_vpiLongIntVal(write_bytes_nonzero_index_name, write_bytes, i + 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiLongIntVal(write_bytes_rl_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiLongIntVal(write_shorts_name, write_shorts, i, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiLongIntVal(write_words_name, write_words, i, j, NUM_ELEMENTS, 4)) return 1; + if (test_vpiLongIntVal(write_integers_name, write_words, i, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiLongIntVal(write_longs_name, write_longs, i, j, NUM_ELEMENTS, 8)) return 1; + } + } + + { + // test unsupported format + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_longs", NULL); + CHECK_RESULT_NZ(object); + + double datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiRealVal; + arrayvalue.flags = 0; + arrayvalue.value.reals = datap; + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + + arrayvalue.format = vpiShortRealVal; + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + + arrayvalue.format = vpiTimeVal; + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test null array value + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_words", NULL); + CHECK_RESULT_NZ(object); + + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, 0, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported TestVpiHandle + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported type + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_scalar", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test index out of bounds + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_bounds", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + PLI_INT32 indexp[1] = {4}; + + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + + indexp[0] = 0; + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test inaccessible + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_inaccessible", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported flags + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_words", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiPropagateOff; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + + arrayvalue.flags = vpiOneValue; + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported format & type combination + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_words", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiShortIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + + arrayvalue.flags = vpiOneValue; + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test num out of bounds + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_words", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, &arrayvalue, indexp, 5); + CHECK_RESULT_NZ(~vpi_chk_error(0)); + } + + { + // test null arrayvalue + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_words", NULL); + CHECK_RESULT_NZ(object); + + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, 0, indexp, 0); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test null indexp + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_words", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + + vpi_get_value_array(object, &arrayvalue, 0, 0); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + return 0; +} + +extern "C" int mon_check(void) { return mon_check_props(); } + +#ifdef IS_VPI + +static int mon_check_vpi() { + TestVpiHandle href = vpi_handle(vpiSysTfCall, 0); + s_vpi_value vpi_value; + + vpi_value.format = vpiIntVal; + vpi_value.value.integer = mon_check(); + vpi_put_value(href, &vpi_value, NULL, vpiNoDelay); + + return 0; +} + +static s_vpi_systf_data vpi_systf_data[] = {{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$mon_check", + (PLI_INT32(*)(PLI_BYTE8*))mon_check_vpi, 0, 0, 0}, + 0}; + +// cver entry +void vpi_compat_bootstrap(void) { + p_vpi_systf_data systf_data_p; + systf_data_p = &(vpi_systf_data[0]); + while (systf_data_p->type != 0) vpi_register_systf(systf_data_p++); +} + +// icarus entry +void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0}; + +#else + +int main(int argc, char** argv) { + Verilated::commandArgs(argc, argv); + const std::unique_ptr contextp{new VerilatedContext}; + const std::unique_ptr top{new VM_PREFIX{contextp.get(), ""}}; + contextp->fatalOnVpiError(0); + +#ifdef VERILATOR +#ifdef TEST_VERBOSE + contextp->scopesDump(); +#endif +#endif + + while (!contextp->gotFinish()) { top->eval(); } + + return 0; +} +#endif diff --git a/test_regress/t/t_vpi_put_value_array.py b/test_regress/t/t_vpi_put_value_array.py new file mode 100755 index 000000000..9f7ed1832 --- /dev/null +++ b/test_regress/t/t_vpi_put_value_array.py @@ -0,0 +1,23 @@ +#!/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') + +test.compile(make_top_shell=False, + make_main=False, + make_pli=True, + verilator_flags2=["--exe --vpi --no-l2name", test.pli_filename], + iv_flags2=["-g2005-sv -D USE_VPI_NOT_DPI"], + v_flags2=["+define+USE_VPI_NOT_DPI +define+VERILATOR_COMMENTS"]) + +test.execute(use_libvpi=True) + +test.passes() diff --git a/test_regress/t/t_vpi_put_value_array.v b/test_regress/t/t_vpi_put_value_array.v new file mode 100644 index 000000000..f0aefe35b --- /dev/null +++ b/test_regress/t/t_vpi_put_value_array.v @@ -0,0 +1,76 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2024 by Diego Roux. 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 + +`ifdef VERILATOR_COMMENTS + `define PUBLIC_FLAT_RD /*verilator public_flat_rd*/ + `define PUBLIC_FLAT_RW /*verilator public_flat_rw*/ +`else + `define PUBLIC_FLAT_RD + `define PUBLIC_FLAT_RW +`endif + +module test (); + +`ifdef VERILATOR +`systemc_header +extern "C" int mon_check(); +`verilog +`endif + + reg [7:0] write_bytes [0:3] `PUBLIC_FLAT_RW; + reg [7:0] write_bytes_rl [3:0] `PUBLIC_FLAT_RW; + reg [7:0] write_bytes_nonzero_index [1:4] `PUBLIC_FLAT_RW; + + reg [15:0] write_shorts [0:3] `PUBLIC_FLAT_RW; + reg [31:0] write_words [0:3] `PUBLIC_FLAT_RW; + reg [63:0] write_longs [0:3] `PUBLIC_FLAT_RW; + reg [68:0] write_customs [0:3] `PUBLIC_FLAT_RW; + reg [68:0] write_customs_nonzero_index_rl [4:1] `PUBLIC_FLAT_RW; + + integer write_integers [0:3] `PUBLIC_FLAT_RW; + + reg [7:0] write_scalar `PUBLIC_FLAT_RW; + reg [7:0] write_bounds [1:3] `PUBLIC_FLAT_RW; + reg [7:0] write_inaccessible [0:3] `PUBLIC_FLAT_RD; + +`ifdef IVERILOG + // stop icarus optimizing signals away + wire redundant = write_bytes[0][0] | + write_bytes[0][0] | + write_bytes_rl[0][0] | + write_bytes_nonzero_index[1][0] | + write_shorts[0][0] | + write_words[0][0] | + write_longs[0][0] | + write_customs[0][0] | + write_customs_nonzero_index_rl[1][0] | + write_integers[0][0] | + write_scalar[0] | + write_bounds[1][0] | + write_inaccessible[0][0]; +`endif + integer status; + + initial begin +`ifdef IVERILOG + status = $mon_check; +`endif + +`ifdef VERILATOR + status = $c32("mon_check()"); +`endif + + if (status != 0) begin + $write("%%Error: t_vpi_put_value_array.cpp:%0d: C Test failed\n", status); + $stop; + end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_vpi_unimpl.cpp b/test_regress/t/t_vpi_unimpl.cpp index c6d73f324..732b30d53 100644 --- a/test_regress/t/t_vpi_unimpl.cpp +++ b/test_regress/t/t_vpi_unimpl.cpp @@ -57,8 +57,6 @@ int _mon_check_unimpl(p_cb_data cb_data) { vpi_get64(0, NULL); vpi_get_delays(NULL, NULL); vpi_put_delays(NULL, NULL); - vpi_get_value_array(NULL, NULL, NULL, 0); - vpi_put_value_array(NULL, NULL, NULL, 0); vpi_get_time(NULL, NULL); vpi_mcd_name(0); vpi_compare_objects(NULL, NULL); diff --git a/test_regress/t/t_vpi_var.cpp b/test_regress/t/t_vpi_var.cpp index db721c306..2cb16563f 100644 --- a/test_regress/t/t_vpi_var.cpp +++ b/test_regress/t/t_vpi_var.cpp @@ -297,7 +297,7 @@ int _mon_check_var() { d = vpi_get(vpiVector, vh4); CHECK_RESULT(d, 1); p = vpi_get_str(vpiType, vh4); - CHECK_RESULT_CSTR(p, "vpiMemory"); + CHECK_RESULT_CSTR(p, "vpiRegArray"); } t_vpi_value tmpValue; @@ -320,14 +320,14 @@ int _mon_check_var() { CHECK_RESULT_CSTR(p, "vpiConstant"); } { - TestVpiHandle vh10 = vpi_iterate(vpiMemoryWord, vh4); + TestVpiHandle vh10 = vpi_iterate(vpiReg, vh4); CHECK_RESULT_NZ(vh10); p = vpi_get_str(vpiType, vh10); CHECK_RESULT_CSTR(p, "vpiIterator"); TestVpiHandle vh11 = vpi_scan(vh10); CHECK_RESULT_NZ(vh11); p = vpi_get_str(vpiType, vh11); - CHECK_RESULT_CSTR(p, "vpiMemoryWord"); + CHECK_RESULT_CSTR(p, "vpiReg"); TestVpiHandle vh12 = vpi_handle(vpiLeftRange, vh11); CHECK_RESULT_NZ(vh12); vpi_get_value(vh12, &tmpValue); @@ -643,15 +643,15 @@ int _mon_check_quad() { TestVpiHandle vhidx3 = vpi_handle_by_index(vh2, 3); CHECK_RESULT_NZ(vhidx3); - // Memory words should not be indexable + // Packed words should be indexable TestVpiHandle vhidx3idx0 = vpi_handle_by_index(vhidx3, 0); - CHECK_RESULT(vhidx3idx0, 0); + CHECK_RESULT_NZ(vhidx3idx0); TestVpiHandle vhidx2idx2 = vpi_handle_by_index(vhidx2, 2); - CHECK_RESULT(vhidx2idx2, 0); + CHECK_RESULT_NZ(vhidx2idx2); TestVpiHandle vhidx3idx3 = vpi_handle_by_index(vhidx3, 3); - CHECK_RESULT(vhidx3idx3, 0); + CHECK_RESULT_NZ(vhidx3idx3); TestVpiHandle vhidx2idx61 = vpi_handle_by_index(vhidx2, 61); - CHECK_RESULT(vhidx2idx61, 0); + CHECK_RESULT_NZ(vhidx2idx61); v.format = vpiVectorVal; v.value.vector = vv; @@ -702,6 +702,7 @@ int _mon_check_delayed() { CHECK_RESULT_Z(vpi_chk_error(nullptr)); // test unsupported vpiInertialDelay cases + // - should these also throw vpi errors? v.format = vpiStringVal; v.value.str = nullptr; vpi_put_value(vh, &v, &t, vpiInertialDelay); @@ -712,9 +713,11 @@ int _mon_check_delayed() { vpi_put_value(vh, &v, &t, vpiInertialDelay); CHECK_RESULT_NZ(vpi_chk_error(nullptr)); + // This format throws an error now + Verilated::fatalOnVpiError(false); v.format = vpiObjTypeVal; vpi_put_value(vh, &v, &t, vpiInertialDelay); - CHECK_RESULT_NZ(vpi_chk_error(nullptr)); + Verilated::fatalOnVpiError(true); return 0; } diff --git a/test_regress/t/t_vpi_var.v b/test_regress/t/t_vpi_var.v index d93196cc9..240694b86 100644 --- a/test_regress/t/t_vpi_var.v +++ b/test_regress/t/t_vpi_var.v @@ -39,7 +39,7 @@ extern "C" int mon_check(); reg [0:61] quads[2:3] /*verilator public_flat_rw @(posedge clk) */; // verilator lint_on ASCRANGE - reg [31:0] count /*verilator public_flat_rd */; + reg [31:0] count /*verilator public_flat */; reg [31:0] half_count /*verilator public_flat_rd */; reg [31:0] delayed /*verilator public_flat_rw */; reg [31:0] delayed_mem [16] /*verilator public_flat_rw */; diff --git a/test_regress/t/t_vpi_var2.v b/test_regress/t/t_vpi_var2.v index 0c0019ddf..93b720f3d 100644 --- a/test_regress/t/t_vpi_var2.v +++ b/test_regress/t/t_vpi_var2.v @@ -51,7 +51,7 @@ extern "C" int mon_check(); reg invisible1; // verilator lint_on ASCRANGE -/*verilator public_flat_rd_on*/ +/*verilator public_flat_on*/ reg [31:0] count; reg [31:0] half_count; /*verilator public_off*/ diff --git a/test_regress/t/t_wrapper_context_top0.out b/test_regress/t/t_wrapper_context_top0.out index d028fb387..74af3f696 100644 --- a/test_regress/t/t_wrapper_context_top0.out +++ b/test_regress/t/t_wrapper_context_top0.out @@ -75,5 +75,8 @@ C 'ft/t_wrapper_context.vl38n4pagev_line/topoblockS38-39htop0.top' C 'ft/t_wrapper_context.vl40n7pagev_branch/topoifS40htop0.top' 0 C 'ft/t_wrapper_context.vl40n8pagev_branch/topoelseS46htop0.top' 34 C 'ft/t_wrapper_context.vl41n11pagev_line/topoelsehtop0.top' 0 +C 'ft/t_wrapper_context.vl41n27pagev_expr/topo((counter >= 32'sh5)==0) => 0htop0.top' 0 +C 'ft/t_wrapper_context.vl41n27pagev_expr/topo((counter >= 32'sh5)==1 && stop==1) => 1htop0.top' 0 +C 'ft/t_wrapper_context.vl41n27pagev_expr/topo(stop==0) => 0htop0.top' 0 C 'ft/t_wrapper_context.vl47n10pagev_branch/topoifS47-49htop0.top' 1 C 'ft/t_wrapper_context.vl47n11pagev_branch/topoelsehtop0.top' 33 diff --git a/test_regress/t/t_wrapper_context_top1.out b/test_regress/t/t_wrapper_context_top1.out index 391e5cb35..f7fab4025 100644 --- a/test_regress/t/t_wrapper_context_top1.out +++ b/test_regress/t/t_wrapper_context_top1.out @@ -75,5 +75,8 @@ C 'ft/t_wrapper_context.vl38n4pagev_line/topoblockS38-39htop1.top' C 'ft/t_wrapper_context.vl40n7pagev_branch/topoifS40htop1.top' 19 C 'ft/t_wrapper_context.vl40n8pagev_branch/topoelseS46htop1.top' 0 C 'ft/t_wrapper_context.vl41n11pagev_line/topoelsehtop1.top' 18 +C 'ft/t_wrapper_context.vl41n27pagev_expr/topo((counter >= 32'sh5)==0) => 0htop1.top' 18 +C 'ft/t_wrapper_context.vl41n27pagev_expr/topo((counter >= 32'sh5)==1 && stop==1) => 1htop1.top' 1 +C 'ft/t_wrapper_context.vl41n27pagev_expr/topo(stop==0) => 0htop1.top' 0 C 'ft/t_wrapper_context.vl47n10pagev_branch/topoifS47-49htop1.top' 0 C 'ft/t_wrapper_context.vl47n11pagev_branch/topoelsehtop1.top' 0 diff --git a/test_regress/t/t_xml_tag.out b/test_regress/t/t_xml_tag.out index cfc91c506..c342d280c 100644 --- a/test_regress/t/t_xml_tag.out +++ b/test_regress/t/t_xml_tag.out @@ -22,7 +22,7 @@ - +